mirror of https://github.com/sotrh/learn-wgpu
removed camera research
parent
99fd6523d7
commit
733683cf6f
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
||||
[package]
|
||||
name = "camera"
|
||||
version = "0.1.0"
|
||||
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bytemuck = { version = "1.4", features = [ "derive" ] }
|
||||
env_logger = "0.7"
|
||||
failure = "0.1"
|
||||
futures = "0.3.5"
|
||||
image = "0.23"
|
||||
log = "0.4"
|
||||
# tobj = "1"
|
||||
wgpu = "0.5.0"
|
||||
winit = "0.22"
|
||||
imgui-wgpu = "0.7"
|
||||
|
||||
[dependencies.cgmath]
|
||||
version = "0.17"
|
||||
features = ["swizzle"]
|
||||
|
||||
[build-dependencies]
|
||||
shaderc = "0.6"
|
||||
glob = "0.3"
|
||||
failure = "0.1"
|
||||
fs_extra = "1.1"
|
@ -1,94 +0,0 @@
|
||||
use failure::bail;
|
||||
use fs_extra::copy_items;
|
||||
use fs_extra::dir::CopyOptions;
|
||||
use glob::glob;
|
||||
use std::env;
|
||||
use std::fs::{read_to_string, write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
copy_res();
|
||||
compile_shaders();
|
||||
}
|
||||
|
||||
fn copy_res() {
|
||||
// This tells cargo to rerun this script if something in /res/ changes.
|
||||
println!("cargo:rerun-if-changed=res/*");
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let mut copy_options = CopyOptions::new();
|
||||
copy_options.overwrite = true;
|
||||
let mut paths_to_copy = Vec::new();
|
||||
paths_to_copy.push("./res/");
|
||||
copy_items(&paths_to_copy, out_dir, ©_options).unwrap();
|
||||
}
|
||||
|
||||
fn compile_shaders() {
|
||||
// This tells cargo to rerun this script if something in /src/ changes.
|
||||
println!("cargo:rerun-if-changed=src/*");
|
||||
|
||||
// Collect all shaders recursively within /src/
|
||||
let mut shader_paths = [
|
||||
glob("./src/**/*.vert").unwrap(),
|
||||
glob("./src/**/*.frag").unwrap(),
|
||||
glob("./src/**/*.comp").unwrap(),
|
||||
];
|
||||
|
||||
// This could be parallelized
|
||||
let shaders = shader_paths
|
||||
.iter_mut()
|
||||
.flatten()
|
||||
.map(|glob_result| ShaderData::load(glob_result.unwrap()).unwrap())
|
||||
.collect::<Vec<ShaderData>>();
|
||||
|
||||
let mut compiler = shaderc::Compiler::new().unwrap();
|
||||
|
||||
// This can't be parallelized. The [shaderc::Compiler] is not
|
||||
// thread safe. Also, it creates a lot of resources. You could
|
||||
// spawn multiple processes to handle this, but it would probably
|
||||
// be better just to only compile shaders that have been changed
|
||||
// recently.
|
||||
for shader in shaders {
|
||||
let compiled = compiler
|
||||
.compile_into_spirv(
|
||||
&shader.src,
|
||||
shader.kind,
|
||||
&shader.src_path.to_str().unwrap(),
|
||||
"main",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
write(shader.spv_path, compiled.as_binary_u8()).unwrap();
|
||||
}
|
||||
|
||||
// panic!("Debugging...");
|
||||
}
|
||||
|
||||
struct ShaderData {
|
||||
src: String,
|
||||
src_path: PathBuf,
|
||||
spv_path: PathBuf,
|
||||
kind: shaderc::ShaderKind,
|
||||
}
|
||||
|
||||
impl ShaderData {
|
||||
pub fn load(src_path: PathBuf) -> Result<Self, failure::Error> {
|
||||
let extension = src_path.extension().unwrap().to_str().unwrap();
|
||||
let kind = match extension {
|
||||
"vert" => shaderc::ShaderKind::Vertex,
|
||||
"frag" => shaderc::ShaderKind::Fragment,
|
||||
"comp" => shaderc::ShaderKind::Compute,
|
||||
_ => bail!("Unsupported shader: {}", src_path.display()),
|
||||
};
|
||||
|
||||
let src = read_to_string(src_path.clone())?;
|
||||
let spv_path = src_path.with_extension(format!("{}.spv", extension));
|
||||
|
||||
Ok(Self {
|
||||
src,
|
||||
src_path,
|
||||
spv_path,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
use cgmath::prelude::*;
|
||||
use cgmath::*;
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
use std::time::Duration;
|
||||
use winit::event::*;
|
||||
|
||||
#[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.0,
|
||||
0.0, 0.0, 0.5, 1.0,
|
||||
);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Camera {
|
||||
position: Point3<f32>,
|
||||
yaw: Rad<f32>,
|
||||
pitch: Rad<f32>,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
|
||||
position: V,
|
||||
yaw: Y,
|
||||
pitch: P,
|
||||
) -> Self {
|
||||
Self {
|
||||
position: position.into(),
|
||||
yaw: yaw.into(),
|
||||
pitch: pitch.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
||||
Matrix4::look_at_dir(
|
||||
self.position,
|
||||
Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
|
||||
Vector3::unit_y(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Projection {
|
||||
aspect: f32,
|
||||
fovy: Rad<f32>,
|
||||
znear: f32,
|
||||
zfar: f32,
|
||||
}
|
||||
|
||||
impl Projection {
|
||||
pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
|
||||
Self {
|
||||
aspect: width as f32 / height as f32,
|
||||
fovy: fovy.into(),
|
||||
znear,
|
||||
zfar,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.aspect = width as f32 / height as f32;
|
||||
}
|
||||
|
||||
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
||||
OPENGL_TO_WGPU_MATRIX * perspective(self.fovy, self.aspect, self.znear, self.zfar)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CameraController {
|
||||
amount_left: f32,
|
||||
amount_right: f32,
|
||||
amount_forward: f32,
|
||||
amount_backward: f32,
|
||||
rotate_horizontal: f32,
|
||||
rotate_vertical: f32,
|
||||
speed: f32,
|
||||
is_dirty: bool,
|
||||
}
|
||||
|
||||
impl CameraController {
|
||||
pub fn new(speed: f32) -> Self {
|
||||
Self {
|
||||
amount_left: 0.0,
|
||||
amount_right: 0.0,
|
||||
amount_forward: 0.0,
|
||||
amount_backward: 0.0,
|
||||
rotate_horizontal: 0.0,
|
||||
rotate_vertical: 0.0,
|
||||
speed,
|
||||
is_dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
|
||||
let amount = if state == ElementState::Pressed {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
match key {
|
||||
VirtualKeyCode::W | VirtualKeyCode::Up => {
|
||||
self.amount_forward = amount;
|
||||
self.is_dirty = true;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::S | VirtualKeyCode::Down => {
|
||||
self.amount_backward = amount;
|
||||
self.is_dirty = true;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::A | VirtualKeyCode::Left => {
|
||||
self.amount_left = amount;
|
||||
self.is_dirty = true;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::D | VirtualKeyCode::Right => {
|
||||
self.amount_right = amount;
|
||||
self.is_dirty = true;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
|
||||
self.rotate_horizontal = mouse_dx as f32;
|
||||
self.rotate_vertical = mouse_dy as f32;
|
||||
self.is_dirty = true;
|
||||
}
|
||||
|
||||
pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) {
|
||||
self.is_dirty = false;
|
||||
let dt = dt.as_secs_f32();
|
||||
|
||||
let (sin, cos) = camera.yaw.0.sin_cos();
|
||||
|
||||
let forward = Vector3::new(cos, 0.0, sin).normalize();
|
||||
let right = Vector3::new(sin, 0.0, cos).normalize();
|
||||
|
||||
camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
|
||||
camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
|
||||
camera.yaw += Rad(-self.rotate_horizontal) * dt;
|
||||
camera.pitch += Rad(self.rotate_vertical) * dt;
|
||||
|
||||
self.rotate_horizontal = 0.0;
|
||||
self.rotate_vertical = 0.0;
|
||||
|
||||
if camera.pitch < -Rad(FRAC_PI_2) {
|
||||
camera.pitch = -Rad(FRAC_PI_2);
|
||||
} else if camera.pitch > Rad(FRAC_PI_2) {
|
||||
camera.pitch = Rad(FRAC_PI_2);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.is_dirty
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
use cgmath::*;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Vertex {
|
||||
position: Vector3<f32>,
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for Vertex {}
|
||||
unsafe impl bytemuck::Zeroable for Vertex {}
|
||||
|
||||
impl Vertex {
|
||||
pub fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
|
||||
wgpu::VertexBufferDescriptor {
|
||||
stride: size_of::<Vertex>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &wgpu::vertex_attr_array!(
|
||||
0 => Float4
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn vertex(x: f32, y: f32, z: f32) -> Vertex {
|
||||
Vertex {
|
||||
position: Vector3 { x, y, z },
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mesh {
|
||||
pub index_count: u32,
|
||||
pub index_buffer: wgpu::Buffer,
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn axes(device: &wgpu::Device) -> Self {
|
||||
let axes_vertices: &[Vertex] = &[
|
||||
vertex(0.0, 0.0, 0.0),
|
||||
vertex(1000.0, 0.0, 0.0),
|
||||
vertex(0.0, 1000.0, 0.0),
|
||||
vertex(0.0, 0.0, 1000.0),
|
||||
];
|
||||
let axes_indices: &[u16] = &[0, 1, 0, 2, 0, 3];
|
||||
let index_count = axes_indices.len() as u32;
|
||||
let index_buffer = device
|
||||
.create_buffer_with_data(bytemuck::cast_slice(axes_indices), wgpu::BufferUsage::INDEX);
|
||||
let vertex_buffer = device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(axes_vertices),
|
||||
wgpu::BufferUsage::VERTEX,
|
||||
);
|
||||
Self {
|
||||
index_count,
|
||||
index_buffer,
|
||||
vertex_buffer,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location=0) in vec3 vPosition;
|
||||
layout(location=0) out vec4 fColor;
|
||||
|
||||
void main() {
|
||||
fColor = vec4(vPosition, 1);
|
||||
}
|
Binary file not shown.
@ -1,15 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location=0) in vec3 aPosition;
|
||||
layout(location=0) out vec3 vPosition;
|
||||
|
||||
layout(set=0, binding=0) uniform Uniforms {
|
||||
mat4 projectionMatrix;
|
||||
mat4 viewMatrix;
|
||||
mat4 viewProjectionMatrix;
|
||||
} ubo;
|
||||
|
||||
void main() {
|
||||
vPosition = aPosition;
|
||||
gl_Position = ubo.viewProjectionMatrix * vec4(aPosition, 1);
|
||||
}
|
Binary file not shown.
@ -1,7 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 f_Color;
|
||||
|
||||
void main() {
|
||||
f_Color = vec4(1, 1, 0, 1);
|
||||
}
|
Binary file not shown.
@ -1,196 +0,0 @@
|
||||
use cgmath::*;
|
||||
use std::time::Duration;
|
||||
use winit::dpi::*;
|
||||
use winit::event::*;
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::camera::*;
|
||||
use crate::data::*;
|
||||
use crate::pipeline::*;
|
||||
use crate::resource::*;
|
||||
|
||||
pub struct Demo {
|
||||
surface: wgpu::Surface,
|
||||
#[allow(dead_code)]
|
||||
adapter: wgpu::Adapter,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
sc_desc: wgpu::SwapChainDescriptor,
|
||||
swap_chain: wgpu::SwapChain,
|
||||
debug_pipeline: wgpu::RenderPipeline,
|
||||
// other resources
|
||||
axes: Mesh,
|
||||
clear_color: wgpu::Color,
|
||||
pub is_running: bool,
|
||||
camera: Camera,
|
||||
controller: CameraController,
|
||||
projection: Projection,
|
||||
uniforms: Uniforms,
|
||||
uniforms_bind_group: wgpu::BindGroup,
|
||||
last_mouse_pos: PhysicalPosition<f64>,
|
||||
mouse_pressed: bool,
|
||||
}
|
||||
|
||||
impl Demo {
|
||||
pub async fn new(window: &Window) -> Self {
|
||||
let surface = wgpu::Surface::create(window);
|
||||
let adapter: wgpu::Adapter = wgpu::Adapter::request(
|
||||
&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::Default,
|
||||
compatible_surface: Some(&surface),
|
||||
},
|
||||
wgpu::BackendBit::PRIMARY,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let (device, queue): (wgpu::Device, wgpu::Queue) =
|
||||
adapter.request_device(&Default::default()).await;
|
||||
let inner_size = window.inner_size();
|
||||
let sc_desc = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: inner_size.width,
|
||||
height: inner_size.height,
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
};
|
||||
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
|
||||
let camera = Camera::new((0.0, 0.5, 3.0), Deg(-90.0), Deg(0.0));
|
||||
let controller = CameraController::new(0.5);
|
||||
let projection =
|
||||
Projection::new(inner_size.width, inner_size.height, Deg(45.0), 0.1, 1000.0);
|
||||
let uniforms = Uniforms::new(&device, &camera, &projection);
|
||||
let (uniform_layout, uniforms_bind_group) = create_uniform_binding(&device, &uniforms);
|
||||
let debug_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&uniform_layout],
|
||||
});
|
||||
let debug_pipeline = RenderPipelineBuilder::new()
|
||||
.layout(&debug_pipeline_layout)
|
||||
.color_solid(sc_desc.format)
|
||||
.primitive_topology(wgpu::PrimitiveTopology::LineList)
|
||||
.vertex_shader(include_bytes!("debug.vert.spv"))
|
||||
.fragment_shader(include_bytes!("debug.frag.spv"))
|
||||
.index_format(wgpu::IndexFormat::Uint16)
|
||||
.vertex_buffer(Vertex::desc())
|
||||
.cull_mode(wgpu::CullMode::None)
|
||||
.build(&device)
|
||||
.unwrap();
|
||||
let axes = Mesh::axes(&device);
|
||||
|
||||
Self {
|
||||
surface,
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
sc_desc,
|
||||
swap_chain,
|
||||
debug_pipeline,
|
||||
axes,
|
||||
clear_color: wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.2,
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
},
|
||||
is_running: true,
|
||||
camera,
|
||||
controller,
|
||||
projection,
|
||||
uniforms,
|
||||
uniforms_bind_group,
|
||||
last_mouse_pos: (0.0, 0.0).into(),
|
||||
mouse_pressed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: PhysicalSize<u32>) {
|
||||
self.sc_desc.width = new_size.width;
|
||||
self.sc_desc.height = new_size.height;
|
||||
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
|
||||
self.projection.resize(new_size.width, new_size.height);
|
||||
self.uniforms.apply_projection(&self.projection);
|
||||
}
|
||||
|
||||
pub fn input(&mut self, event: &WindowEvent) -> bool {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
self.controller.process_keyboard(*key, *state)
|
||||
|| match (key, *state == ElementState::Pressed) {
|
||||
(VirtualKeyCode::Escape, true) => {
|
||||
self.is_running = false;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput {
|
||||
button: MouseButton::Left,
|
||||
state,
|
||||
..
|
||||
} => {
|
||||
self.mouse_pressed = *state == ElementState::Pressed;
|
||||
true
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let mouse_dx = position.x - self.last_mouse_pos.x;
|
||||
let mouse_dy = position.y - self.last_mouse_pos.y;
|
||||
self.last_mouse_pos = *position;
|
||||
if self.mouse_pressed {
|
||||
self.controller.process_mouse(mouse_dx, mouse_dy);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, dt: Duration) {
|
||||
if self.controller.is_dirty() {
|
||||
self.controller.update_camera(&mut self.camera, dt);
|
||||
self.uniforms.apply_camera(&self.camera);
|
||||
}
|
||||
if let Some(cmds) = self.uniforms.update(&self.device) {
|
||||
self.queue.submit(&[cmds]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self) {
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
let frame = self
|
||||
.swap_chain
|
||||
.get_next_texture()
|
||||
.expect("Unable to retrieve swap chain texture");
|
||||
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: &frame.view,
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: self.clear_color,
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
pass.set_pipeline(&self.debug_pipeline);
|
||||
pass.set_bind_group(0, &self.uniforms_bind_group, &[]);
|
||||
pass.set_index_buffer(&self.axes.index_buffer, 0, 0);
|
||||
pass.set_vertex_buffer(0, &self.axes.vertex_buffer, 0, 0);
|
||||
pass.draw_indexed(0..self.axes.index_count, 0, 0..1);
|
||||
}
|
||||
|
||||
self.queue.submit(&[encoder.finish()]);
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
mod camera;
|
||||
mod data;
|
||||
mod demo;
|
||||
mod pipeline;
|
||||
mod resource;
|
||||
|
||||
use demo::*;
|
||||
|
||||
use winit::dpi::*;
|
||||
use winit::event::*;
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::*;
|
||||
|
||||
use futures::executor::block_on;
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(PhysicalSize::new(800, 600))
|
||||
.with_title(env!("CARGO_PKG_NAME"))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
let mut demo = block_on(Demo::new(&window));
|
||||
let mut last_update = Instant::now();
|
||||
let mut is_focused = false;
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = if is_focused {
|
||||
ControlFlow::Poll
|
||||
} else if demo.is_running {
|
||||
ControlFlow::Wait
|
||||
} else {
|
||||
ControlFlow::Exit
|
||||
};
|
||||
match event {
|
||||
Event::MainEventsCleared => {
|
||||
if is_focused {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
ref event,
|
||||
window_id,
|
||||
} => {
|
||||
if window_id == window.id() && !demo.input(event) {
|
||||
match event {
|
||||
WindowEvent::Focused(f) => is_focused = *f,
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
demo.resize(**new_inner_size)
|
||||
}
|
||||
WindowEvent::Resized(new_size) => demo.resize(*new_size),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(window_id) => {
|
||||
if window_id == window.id() {
|
||||
let now = Instant::now();
|
||||
let dt = now - last_update;
|
||||
last_update = now;
|
||||
demo.update(dt);
|
||||
demo.render();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
use failure::bail;
|
||||
|
||||
pub struct RenderPipelineBuilder<'a> {
|
||||
layout: Option<&'a wgpu::PipelineLayout>,
|
||||
vertex_shader: Option<&'a [u8]>,
|
||||
fragment_shader: Option<&'a [u8]>,
|
||||
front_face: wgpu::FrontFace,
|
||||
cull_mode: wgpu::CullMode,
|
||||
depth_bias: i32,
|
||||
depth_bias_slope_scale: f32,
|
||||
depth_bias_clamp: f32,
|
||||
primitive_topology: wgpu::PrimitiveTopology,
|
||||
color_states: Vec<wgpu::ColorStateDescriptor>,
|
||||
depth_stencil_state: Option<wgpu::DepthStencilStateDescriptor>,
|
||||
index_format: wgpu::IndexFormat,
|
||||
vertex_buffers: Vec<wgpu::VertexBufferDescriptor<'a>>,
|
||||
sample_count: u32,
|
||||
sample_mask: u32,
|
||||
alpha_to_coverage_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RenderPipelineBuilder<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
layout: None,
|
||||
vertex_shader: None,
|
||||
fragment_shader: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: wgpu::CullMode::None,
|
||||
depth_bias: 0,
|
||||
depth_bias_slope_scale: 0.0,
|
||||
depth_bias_clamp: 0.0,
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: Vec::new(),
|
||||
depth_stencil_state: None,
|
||||
index_format: wgpu::IndexFormat::Uint32,
|
||||
vertex_buffers: Vec::new(),
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout(&mut self, layout: &'a wgpu::PipelineLayout) -> &mut Self {
|
||||
self.layout = Some(layout);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vertex_shader(&mut self, spv: &'a [u8]) -> &mut Self {
|
||||
self.vertex_shader = Some(spv);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fragment_shader(&mut self, spv: &'a [u8]) -> &mut Self {
|
||||
self.fragment_shader = Some(spv);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn front_face(&mut self, ff: wgpu::FrontFace) -> &mut Self {
|
||||
self.front_face = ff;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cull_mode(&mut self, cm: wgpu::CullMode) -> &mut Self {
|
||||
self.cull_mode = cm;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_bias(&mut self, db: i32) -> &mut Self {
|
||||
self.depth_bias = db;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_bias_slope_scale(&mut self, dbss: f32) -> &mut Self {
|
||||
self.depth_bias_slope_scale = dbss;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_bias_clamp(&mut self, dbc: f32) -> &mut Self {
|
||||
self.depth_bias_clamp = dbc;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn primitive_topology(&mut self, pt: wgpu::PrimitiveTopology) -> &mut Self {
|
||||
self.primitive_topology = pt;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color_state(&mut self, cs: wgpu::ColorStateDescriptor) -> &mut Self {
|
||||
self.color_states.push(cs);
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper method for [RenderPipelineBuilder::color_state]
|
||||
pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self {
|
||||
self.color_state(wgpu::ColorStateDescriptor {
|
||||
format,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self {
|
||||
self.depth_stencil_state = Some(dss);
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper method for [RenderPipelineBuilder::depth_stencil_state]
|
||||
pub fn depth_no_stencil(
|
||||
&mut self,
|
||||
format: wgpu::TextureFormat,
|
||||
depth_write_enabled: bool,
|
||||
depth_compare: wgpu::CompareFunction,
|
||||
) -> &mut Self {
|
||||
self.depth_stencil_state(wgpu::DepthStencilStateDescriptor {
|
||||
format,
|
||||
depth_write_enabled,
|
||||
depth_compare,
|
||||
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||
stencil_read_mask: 0,
|
||||
stencil_write_mask: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Helper method for [RenderPipelineBuilder::depth_no_stencil]
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_format(&mut self, format: wgpu::TextureFormat) -> &mut Self {
|
||||
self.depth_no_stencil(format, true, wgpu::CompareFunction::Less)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn index_format(&mut self, ifmt: wgpu::IndexFormat) -> &mut Self {
|
||||
self.index_format = ifmt;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vertex_buffer(&mut self, vb: wgpu::VertexBufferDescriptor<'a>) -> &mut Self {
|
||||
self.vertex_buffers.push(vb);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn sample_count(&mut self, sc: u32) -> &mut Self {
|
||||
self.sample_count = sc;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn sample_mask(&mut self, sm: u32) -> &mut Self {
|
||||
self.sample_mask = sm;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn alpha_to_coverage_enabled(&mut self, atce: bool) -> &mut Self {
|
||||
self.alpha_to_coverage_enabled = atce;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self, device: &wgpu::Device) -> Result<wgpu::RenderPipeline, failure::Error> {
|
||||
// We need a layout
|
||||
if self.layout.is_none() {
|
||||
bail!("No pipeline layout supplied!");
|
||||
}
|
||||
let layout = self.layout.unwrap();
|
||||
|
||||
// Render pipelines always have a vertex shader, but due
|
||||
// to the way the builder pattern works, we can't
|
||||
// guarantee that the user will specify one, so we'll
|
||||
// just return an error if they forgot.
|
||||
//
|
||||
// We could supply a default one, but a "default" vertex
|
||||
// could take on many forms. An error is much more
|
||||
// explicit.
|
||||
if self.vertex_shader.is_none() {
|
||||
bail!("No vertex shader supplied!")
|
||||
}
|
||||
let vs = create_shader_module(device, self.vertex_shader.unwrap());
|
||||
|
||||
// The fragment shader is optional (IDK why, but it is).
|
||||
// Having the shader be optional is giving me issues with
|
||||
// the borrow checker so I'm going to use a default shader
|
||||
// if the user doesn't supply one.
|
||||
let fs_spv = self
|
||||
.fragment_shader
|
||||
.unwrap_or(include_bytes!("default.frag.spv"));
|
||||
let fs = create_shader_module(device, fs_spv);
|
||||
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
layout: &layout,
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
module: &vs,
|
||||
entry_point: "main",
|
||||
},
|
||||
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
|
||||
module: &fs,
|
||||
entry_point: "main",
|
||||
}),
|
||||
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
|
||||
front_face: self.front_face,
|
||||
cull_mode: self.cull_mode,
|
||||
depth_bias: self.depth_bias,
|
||||
depth_bias_slope_scale: self.depth_bias_slope_scale,
|
||||
depth_bias_clamp: self.depth_bias_clamp,
|
||||
}),
|
||||
primitive_topology: self.primitive_topology,
|
||||
color_states: &self.color_states,
|
||||
depth_stencil_state: self.depth_stencil_state.clone(),
|
||||
vertex_state: wgpu::VertexStateDescriptor {
|
||||
index_format: self.index_format,
|
||||
vertex_buffers: &self.vertex_buffers,
|
||||
},
|
||||
sample_count: self.sample_count,
|
||||
sample_mask: self.sample_mask,
|
||||
alpha_to_coverage_enabled: self.alpha_to_coverage_enabled,
|
||||
});
|
||||
Ok(pipeline)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_shader_module(device: &wgpu::Device, spirv: &[u8]) -> wgpu::ShaderModule {
|
||||
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(spirv)).unwrap())
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
use cgmath::*;
|
||||
|
||||
use crate::camera::*;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Uniforms {
|
||||
raw: UniformsRaw,
|
||||
buffer: wgpu::Buffer,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct UniformsRaw {
|
||||
projection_matrix: Matrix4<f32>,
|
||||
view_matrix: Matrix4<f32>,
|
||||
view_proj_matrix: Matrix4<f32>,
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for UniformsRaw {}
|
||||
unsafe impl bytemuck::Zeroable for UniformsRaw {}
|
||||
|
||||
impl Uniforms {
|
||||
pub fn new(device: &wgpu::Device, camera: &Camera, projection: &Projection) -> Self {
|
||||
let projection_matrix = projection.calc_matrix();
|
||||
let view_matrix = camera.calc_matrix();
|
||||
let raw = UniformsRaw {
|
||||
projection_matrix,
|
||||
view_matrix,
|
||||
view_proj_matrix: projection_matrix * view_matrix,
|
||||
};
|
||||
let buffer = device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&[raw]),
|
||||
wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
|
||||
);
|
||||
|
||||
Self {
|
||||
raw,
|
||||
buffer,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_camera(&mut self, camera: &Camera) {
|
||||
self.raw.view_matrix = camera.calc_matrix();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn apply_projection(&mut self, projection: &Projection) {
|
||||
self.raw.projection_matrix = projection.calc_matrix();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn update(&mut self, device: &wgpu::Device) -> Option<wgpu::CommandBuffer> {
|
||||
if self.dirty {
|
||||
self.dirty = false;
|
||||
self.raw.view_proj_matrix = self.raw.projection_matrix * self.raw.view_matrix;
|
||||
|
||||
let copy_buffer = device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&[self.raw]),
|
||||
wgpu::BufferUsage::COPY_SRC,
|
||||
);
|
||||
|
||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Uniforms::update()"),
|
||||
});
|
||||
encoder.copy_buffer_to_buffer(
|
||||
©_buffer,
|
||||
0,
|
||||
&self.buffer,
|
||||
0,
|
||||
size_of::<UniformsRaw>() as wgpu::BufferAddress,
|
||||
);
|
||||
Some(encoder.finish())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_uniform_binding(
|
||||
device: &wgpu::Device,
|
||||
uniforms: &Uniforms,
|
||||
) -> (wgpu::BindGroupLayout, wgpu::BindGroup) {
|
||||
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("Uniforms::BindGroupLayout"),
|
||||
bindings: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
|
||||
}],
|
||||
});
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("Uniforms::BindGroup"),
|
||||
layout: &layout,
|
||||
bindings: &[wgpu::Binding {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer {
|
||||
buffer: &uniforms.buffer,
|
||||
range: 0..size_of::<UniformsRaw>() as wgpu::BufferAddress,
|
||||
},
|
||||
}],
|
||||
});
|
||||
(layout, bind_group)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#version 450
|
||||
|
||||
const vec2 positions[3] = vec2[3](
|
||||
vec2(0.0, 0.5),
|
||||
vec2(-0.5, -0.5),
|
||||
vec2(0.5, -0.5)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue