diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000000000000000000000000000000000000..8cb66044361ae46bb56c6c92d066e9e462bb2af6 --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,50 @@ +use egui::{Color32, Frame, Id, ScrollArea, TextEdit, TextStyle}; +use egui_winit_vulkano::Gui; + +fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) { + ui.label(egui::RichText::new(text).size(size)); +} + +const CODE: &str = r#" +# Some markup +``` +let mut gui = Gui::new(&event_loop, renderer.surface(), renderer.queue()); +``` +Vulkan(o) is hard, that I know... +"#; + +#[derive(Debug)] +pub struct GState { + pub cursor_sensitivity: f32, + pub move_speed: f32, +} + +impl Default for GState { + fn default() -> Self { + Self { + cursor_sensitivity: 1.0, + move_speed: 1.0, + } + } +} + +pub fn gui_up(gui: &mut Gui, state: &mut GState) { + let mut code = CODE.to_owned(); + gui.immediate_ui(|gui| { + let ctx = gui.context(); + egui::SidePanel::left(Id::new("main_left")) + .frame(Frame::default().fill(Color32::from_rgba_unmultiplied(100, 100, 100, 200))) + .show(&ctx, |ui| { + ui.vertical_centered(|ui| { + ui.add(egui::widgets::Label::new("Efficient Realtime Rendering of Complex Closed Form Implicit Surfaces Using Modern RTX Enabled GPUs")); + sized_text(ui, "Settings", 32.0); + }); + ui.separator(); + ui.vertical_centered(|ui| { + //ui.heading("Camera Control"); + ui.add(egui::Slider::new(&mut state.cursor_sensitivity, 0.0..=2.0).text("Mouse Sensitivity")); + ui.add(egui::Slider::new(&mut state.move_speed, 0.0..=2.0).text("Movement Speed")); + }); + }); + }); +} diff --git a/src/main.rs b/src/main.rs index ea27f9cdb803634aab5aa25d9c46e4b49834372d..fe1b48adec799cb91d3e70b120de7d5b8b3b374e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,23 +16,24 @@ // and that you want to learn Vulkan. This means that for example it won't go into details about // what a vertex or a shader is. use bytemuck::{Pod, Zeroable}; -use cgmath::{Matrix3, Matrix4, Point3, Rad, Vector3}; +use cgmath::{ + AbsDiffEq, Basis3, Deg, EuclideanSpace, Euler, Matrix3, Matrix4, Point3, Quaternion, Rad, + SquareMatrix, Transform, Vector3, +}; use obj::{LoadConfig, ObjData}; use rodio::{source::Source, Decoder, OutputStream}; use std::io::Cursor; use std::{sync::Arc, time::Instant}; -use vulkano::buffer::CpuBufferPool; use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::device::QueueFlags; use vulkano::format::Format; use vulkano::image::AttachmentImage; -use vulkano::memory::allocator::{MemoryAllocator, MemoryUsage, StandardMemoryAllocator}; +use vulkano::memory::allocator::StandardMemoryAllocator; use vulkano::pipeline::graphics::depth_stencil::DepthStencilState; use vulkano::pipeline::graphics::rasterization::CullMode; use vulkano::pipeline::graphics::rasterization::FrontFace::Clockwise; -use vulkano::swapchain::SwapchainPresentInfo; -use vulkano::{memory, VulkanLibrary}; +use vulkano::swapchain::{PresentMode, SwapchainPresentInfo}; +use vulkano::VulkanLibrary; +use winit::event::{DeviceEvent, DeviceId, ElementState, MouseButton, VirtualKeyCode}; use egui_winit_vulkano::Gui; use vulkano::pipeline::StateMode::Fixed; @@ -41,7 +42,6 @@ use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassContents, }, - descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet}, device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo, }, @@ -55,7 +55,7 @@ use vulkano::{ vertex_input::BuffersDefinition, viewport::{Viewport, ViewportState}, }, - GraphicsPipeline, Pipeline, PipelineBindPoint, + GraphicsPipeline, Pipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, swapchain::{ @@ -70,6 +70,9 @@ use winit::{ window::{Window, WindowBuilder}, }; +use crate::gui::*; +mod gui; + fn main() { // The first step of any Vulkan program is to create an instance. // @@ -213,9 +216,6 @@ fn main() { // iterator. let queue = queues.next().expect("Unable to retrieve queues"); - // Create an egui GUI - let mut gui = Gui::new(&event_loop, surface.clone(), None, queue.clone(), false); - // Before we can draw on the surface, we have to create what is called a swapchain. Creating // a swapchain allocates the color buffers that will contain the image that will ultimately // be visible on the screen. These images are returned alongside the swapchain. @@ -276,6 +276,8 @@ fn main() { .next() .unwrap(), + present_mode: PresentMode::FifoRelaxed, + ..Default::default() }, ) @@ -393,7 +395,7 @@ fn main() { // The next step is to create a *render pass*, which is an object that describes where the // output of the graphics pipeline will go. It describes the layout of the images // where the colors, depth and/or stencil information will be written. - let render_pass = vulkano::single_pass_renderpass!( + let render_pass = vulkano::ordered_passes_renderpass!( device.clone(), attachments: { // `color` is a custom name we give to the first and only attachment. @@ -421,12 +423,19 @@ fn main() { samples: 1, } }, - pass: { + passes: [{ // We use the attachment named `color` as the one and only color attachment. color: [color], // No depth-stencil attachment is indicated with empty brackets. - depth_stencil: {depth} - } + depth_stencil: {depth}, + input: [] + },{ + // We use the attachment named `color` as the one and only color attachment. + color: [color], + // No depth-stencil attachment is indicated with empty brackets. + depth_stencil: {depth}, + input: [] + }] ) .unwrap(); @@ -504,6 +513,7 @@ fn main() { // that, we store the submission of the previous frame here. let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); + /* // Get a output stream handle to the default physical sound device let (_stream, stream_handle) = OutputStream::try_default().unwrap(); // Load a sound from a file, using a path relative to Cargo.toml @@ -512,24 +522,101 @@ fn main() { let source = Decoder::new(freebird).unwrap().repeat_infinite(); // Play the sound directly on the device stream_handle.play_raw(source.convert_samples()).unwrap(); + */ let rotation_start = Instant::now(); //let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone()); + // Create an egui GUI + let mut gui = Gui::new_with_subpass( + &event_loop, + surface.clone(), + None, + queue.clone(), + Subpass::from(render_pass.clone(), 1).unwrap(), + ); + + let mut gstate = GState::default(); + + let mut campos = Point3 { + x: 0f32, + y: 0f32, + z: 3f32, + }; + + let mut camforward = Euler::new(Deg(0f32), Deg(0f32), Deg(0f32)); + + let mut looking = false; + struct Keys { + w: bool, + s: bool, + a: bool, + d: bool, + } + let mut keys = Keys { + w: false, + s: false, + a: false, + d: false, + }; + event_loop.run(move |event, _, control_flow| { - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - *control_flow = ControlFlow::Exit; + if let Event::WindowEvent { event: we, .. } = &event { + if !gui.update(we) { + match &we { + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + WindowEvent::Resized(_) => { + recreate_swapchain = true; + } + WindowEvent::ScaleFactorChanged { .. } => { + recreate_swapchain = true; + } + WindowEvent::DroppedFile(file) => { + todo!() + } + WindowEvent::MouseInput { + device_id: d, + state: s, + button: b, + .. + } => { + println!("MOUSE {:?}, {:?}, {:?}", d, s, b); + if b == &MouseButton::Right { + looking = s == &ElementState::Pressed; + } + } + WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode { + Some(VirtualKeyCode::W) => { + keys.w = input.state == ElementState::Pressed; + } + Some(VirtualKeyCode::S) => { + keys.s = input.state == ElementState::Pressed; + } + Some(VirtualKeyCode::A) => { + keys.a = input.state == ElementState::Pressed; + } + Some(VirtualKeyCode::D) => { + keys.d = input.state == ElementState::Pressed; + } + _ => {} + }, + _ => {} + } } - Event::WindowEvent { - event: WindowEvent::Resized(_), + } + match event { + Event::DeviceEvent { + event: DeviceEvent::MouseMotion { delta }, .. } => { - recreate_swapchain = true; + if looking { + camforward.x -= Deg(delta.1 as f32) * gstate.cursor_sensitivity; + camforward.y += Deg(delta.0 as f32) * gstate.cursor_sensitivity; + } + //println!("AXISM {:?}", delta); } Event::RedrawEventsCleared => { // Do not draw frame when screen dimensions are zero. @@ -575,11 +662,44 @@ fn main() { recreate_swapchain = false; } + //println!("{:?}", right); + let uniform_data = { - let elapsed = rotation_start.elapsed(); - let rotation = - elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; - let rotation = Matrix3::from_angle_y(Rad(rotation as f32)); + if looking { + if keys.w { + campos -= Matrix3::from_angle_y(camforward.y) + * Matrix3::from_angle_x(camforward.x) + * Vector3::unit_z() + * 0.02 + * gstate.move_speed; + } + if keys.s { + campos += Matrix3::from_angle_y(camforward.y) + * Matrix3::from_angle_x(camforward.x) + * Vector3::unit_z() + * 0.02 + * gstate.move_speed; + } + if keys.a { + campos += Matrix3::from_angle_y(camforward.y) + * Matrix3::from_angle_x(camforward.x) + * Vector3::unit_x() + * 0.02 + * gstate.move_speed; + } + if keys.d { + campos -= Matrix3::from_angle_y(camforward.y) + * Matrix3::from_angle_x(camforward.x) + * Vector3::unit_x() + * 0.02 + * gstate.move_speed; + } + } else { + keys.w = false; + keys.s = false; + keys.a = false; + keys.d = false; + } // note: this teapot was meant for OpenGL where the origin is at the lower left // instead the origin is at the upper left in Vulkan, so we reverse the Y axis @@ -591,25 +711,28 @@ fn main() { 0.01, 100.0, ); - let view = Matrix4::look_at_rh( - Point3::new(0.3, 0.3, 1.0), - Point3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, -1.0, 0.0), - ); - let scale = Matrix4::from_scale(0.01); - - vs::ty::PushConstantData { - world: Matrix4::from(rotation).into(), - view: (view * scale).into(), + let scale = 0.01; + let view = Matrix4::from(camforward) + * Matrix4::from_angle_z(Deg(180f32)) + * Matrix4::from_translation(Point3::origin() - campos) + * Matrix4::from_scale(scale); + //*Matrix4::from_angle_z(Deg(180f32)); + + let pc = vs::ty::PushConstantData { + world: Matrix4::identity().into(), + view: view.into(), proj: proj.into(), - } + }; - /*println!( - "world: {:?} view: {:?} proj: {:?}", - uniform_data.world, uniform_data.view, uniform_data.proj - );*/ + if looking { + /*println!( + "world: {:?} view: {:?} proj: {:?}", + pc.world, pc.view, pc.proj + );*/ + println!("campos: {:?} camforward: {:?}", campos, camforward); + } - //uniform_buffer.from_data(uniform_data).unwrap() + pc }; //let layout = pipeline.layout().set_layouts().get(0).unwrap(); @@ -644,6 +767,8 @@ fn main() { recreate_swapchain = true; } + gui_up(&mut gui, &mut gstate); + // In order to draw, we have to build a *command buffer*. The command buffer object holds // the list of commands that are going to be executed. // @@ -660,6 +785,8 @@ fn main() { ) .unwrap(); + let cb = gui.draw_on_subpass_image(dimensions.into()); + builder // Before we can draw, we have to *enter a render pass*. .begin_render_pass( @@ -702,6 +829,10 @@ fn main() { .unwrap() // We leave the render pass. Note that if we had multiple // subpasses we could have called `next_subpass` to jump to the next subpass. + .next_subpass(SubpassContents::SecondaryCommandBuffers) + .unwrap() + .execute_commands(cb) + .unwrap() .end_render_pass() .unwrap();