diff options
39 files changed, 512 insertions, 2159 deletions
@@ -3,8 +3,6 @@ members = [ "stockton-input", "stockton-input-codegen", "stockton-skeleton", - "stockton-levels", - "stockton-render", "stockton-contrib", "rendy-memory", "rendy-descriptor", diff --git a/examples/render-quad/Cargo.toml b/examples/render-quad/Cargo.toml index 99cf38e..7e7f57c 100644 --- a/examples/render-quad/Cargo.toml +++ b/examples/render-quad/Cargo.toml @@ -6,15 +6,10 @@ edition = "2018" [dependencies] stockton-skeleton = { path = "../../stockton-skeleton", features = ["vulkan"] } -stockton-input = { path = "../../stockton-input" } -stockton-input-codegen = { path = "../../stockton-input-codegen" } -stockton-levels = { path = "../../stockton-levels" } -stockton-contrib = { path = "../../stockton-contrib", features = ["delta_time", "flycam"] } -stockton-render = { path = "../../stockton-render" } winit = "^0.21" log = "0.4.0" simplelog = "^0.10" image = "0.23.2" -egui = "^0.12" legion = { version = "^0.3" } anyhow = "1.0.40" +gfx-hal = "^0.8.0" diff --git a/examples/render-quad/src/data/shader.frag b/examples/render-quad/src/data/shader.frag new file mode 100644 index 0000000..929073f --- /dev/null +++ b/examples/render-quad/src/data/shader.frag @@ -0,0 +1,11 @@ +#version 450 + +layout (location = 0) in vec2 frag_pos; +layout (location = 1) in vec3 frag_color; + +layout (location = 0) out vec4 color; + +void main() +{ + color = vec4(frag_color, 1.0); +}
\ No newline at end of file diff --git a/examples/render-quad/src/data/shader.vert b/examples/render-quad/src/data/shader.vert new file mode 100644 index 0000000..5ca3090 --- /dev/null +++ b/examples/render-quad/src/data/shader.vert @@ -0,0 +1,16 @@ +#version 450 + +layout (location = 0) in vec2 position; +layout (location = 1) in vec3 color; + +out gl_PerVertex { + vec4 gl_Position; +}; +layout (location = 1) out vec3 frag_color; + + +void main() +{ + gl_Position = vec4(position, 0.5, 1.0); + frag_color = color; +}
\ No newline at end of file diff --git a/examples/render-quad/src/draw_pass.rs b/examples/render-quad/src/draw_pass.rs new file mode 100644 index 0000000..9b32fcb --- /dev/null +++ b/examples/render-quad/src/draw_pass.rs @@ -0,0 +1,335 @@ +//! Minimal code for drawing any level, based on traits from stockton-levels + +use anyhow::{Context, Result}; +use hal::{ + buffer::SubRange, + command::{ClearColor, ClearValue, RenderAttachmentInfo, SubpassContents}, + format::Format, + image::Layout, + pass::Attachment, + pso::{ + BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, DepthStencilDesc, Face, Factor, + FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, Rasterizer, State, + VertexInputRate, + }, +}; +use legion::{Entity, IntoQuery}; +use std::{ + array::IntoIter, + iter::{empty, once}, +}; +use stockton_skeleton::{ + buffers::draw::DrawBuffers, + builders::{ + AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, + ShaderKind, VertexBufferSpec, VertexPrimitiveAssemblerSpec, + }, + draw_passes::util::TargetSpecificResources, + mem::{DataPool, StagingPool}, + queue_negotiator::QueueFamilyNegotiator, + types::*, + DrawPass, IntoDrawPass, PassPosition, RenderingContext, Session, +}; + +use crate::ExampleState; + +/// The vertices that go to the shader (XY + RGB) +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct Vertex(pub Vector2, pub Vector3); + +/// An example draw pass +pub struct ExampleDrawPass<'a> { + /// Index and vertex buffer pair + draw_buffers: DrawBuffers<'a, Vertex, DataPool, StagingPool>, + + /// Resources that depend on the surface. This is seperate so that we can deal with surface changes more easily. + surface_resources: SurfaceDependentResources, + + /// Entity we get our state from + state_ent: Entity, +} + +/// Config for our draw pass. This is turned into our drawpass using [`IntoDrawPass`] +pub struct ExampleDrawPassConfig { + pub state_ent: Entity, +} + +impl<'a, P: PassPosition> DrawPass<P> for ExampleDrawPass<'a> { + /// Called every frame to queue actual drawing. + fn queue_draw( + &mut self, + session: &Session, + img_view: &ImageViewT, + cmd_buffer: &mut CommandBufferT, + ) -> anyhow::Result<()> { + // Commit any changes to our vertex buffers + // We queue this first so that it's executed before any draw commands + self.draw_buffers + .vertex_buffer + .record_commit_cmds(cmd_buffer)?; + self.draw_buffers + .index_buffer + .record_commit_cmds(cmd_buffer)?; + + // Get framebuffer + let fb = self.surface_resources.framebuffers.get_next(); + + // Get state + let (state,) = <(&ExampleState,)>::query().get(&session.world, self.state_ent)?; + + // Begin render pass & bind everything needed + unsafe { + cmd_buffer.begin_render_pass( + &self.surface_resources.pipeline.renderpass, + fb, + self.surface_resources.pipeline.render_area, + vec![RenderAttachmentInfo { + image_view: img_view, + clear_value: ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }, + }] + .into_iter(), + SubpassContents::Inline, + ); + cmd_buffer.bind_graphics_pipeline(&self.surface_resources.pipeline.pipeline); + + // Bind buffers + cmd_buffer.bind_vertex_buffers( + 0, + once(( + self.draw_buffers.vertex_buffer.get_buffer(), + SubRange { + offset: 0, + size: None, + }, + )), + ); + cmd_buffer.bind_index_buffer( + self.draw_buffers.index_buffer.get_buffer(), + SubRange { + offset: 0, + size: None, + }, + hal::IndexType::U16, + ); + } + + // Draw an example + self.draw_buffers.index_buffer[0] = (0, 1, 2); + self.draw_buffers.vertex_buffer[0] = Vertex(Vector2::new(0.5, 0.5), state.color()); + self.draw_buffers.vertex_buffer[1] = Vertex(Vector2::new(0.0, -0.5), state.color()); + self.draw_buffers.vertex_buffer[2] = Vertex(Vector2::new(-0.5, 0.5), state.color()); + + unsafe { + cmd_buffer.draw_indexed(0..3, 0, 0..1); + } + + // Remember to clean up afterwards! + unsafe { + cmd_buffer.end_render_pass(); + } + + Ok(()) + } + + /// Destroy all our vulkan objects + fn deactivate(self, context: &mut RenderingContext) -> Result<()> { + self.draw_buffers.deactivate(context); + self.surface_resources.deactivate(context)?; + + Ok(()) + } + + /// Deal with a surface change + fn handle_surface_change( + mut self, + _session: &Session, + context: &mut RenderingContext, + ) -> Result<Self> { + // We need to make sure there's never an invalid value for self.surface_resources, + // and that we deactivate everything in case of an error (since we'll be dropped in that case). + let new_resources = match SurfaceDependentResources::new::<P>(context) { + Ok(x) => x, + Err(e) => { + <Self as DrawPass<P>>::deactivate(self, context)?; + + return Err(e); + } + }; + + let old_resources = self.surface_resources; + self.surface_resources = new_resources; + + match old_resources.deactivate(context) { + Ok(_) => Ok(self), + Err(e) => { + <Self as DrawPass<P>>::deactivate(self, context)?; + Err(e) + } + } + } +} + +impl<'a, P: PassPosition> IntoDrawPass<ExampleDrawPass<'a>, P> for ExampleDrawPassConfig { + /// Create our example draw pass + fn init( + self, + _session: &mut Session, + context: &mut RenderingContext, + ) -> Result<ExampleDrawPass<'a>> { + let surface_resources = SurfaceDependentResources::new::<P>(context)?; + let draw_buffers = + match DrawBuffers::from_context(context).context("Error creating draw buffers") { + Ok(x) => x, + Err(e) => { + surface_resources.deactivate(context)?; + return Err(e); + } + }; + + Ok(ExampleDrawPass { + draw_buffers, + surface_resources, + state_ent: self.state_ent, + }) + } + + fn find_aux_queues( + _adapter: &Adapter, + _queue_negotiator: &mut QueueFamilyNegotiator, + ) -> Result<()> { + // We don't need any queues, but we'd need code to find their families here if we did. + Ok(()) + } +} + +/// Used to store resources which depend on the surface, for convenience in handle_surface_change +struct SurfaceDependentResources { + pub pipeline: CompletePipeline, + pub framebuffers: TargetSpecificResources<FramebufferT>, +} + +impl SurfaceDependentResources { + pub fn new<P: PassPosition>(context: &mut RenderingContext) -> Result<Self> { + let (pipeline, framebuffers) = { + // Our graphics pipeline + // Vulkan has a lot of config, so this is basically always going to be a big builder block + let pipeline_spec = PipelineSpecBuilder::default() + .rasterizer(Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::NONE, + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: true, + line_width: State::Static(1.0), + }) + .depth_stencil(DepthStencilDesc { + depth: None, + depth_bounds: false, + stencil: None, + }) + .blender(BlendDesc { + logic_op: Some(LogicOp::Copy), + targets: vec![ColorBlendDesc { + mask: ColorMask::ALL, + blend: Some(BlendState { + color: BlendOp::Add { + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, + }, + alpha: BlendOp::Add { + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, + }, + }), + }], + }) + .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers( + InputAssemblerDesc::new(Primitive::TriangleList), + vec![VertexBufferSpec { + attributes: vec![Format::Rg32Sfloat, Format::Rgb32Sfloat], + rate: VertexInputRate::Vertex, + }], + )) + .shader_vertex(ShaderDesc { + source: include_str!("./data/shader.vert").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Vertex, + }) + .shader_fragment(ShaderDesc { + source: include_str!("./data/shader.frag").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Fragment, + }) + .renderpass(RenderpassSpec { + colors: vec![AttachmentSpec { + attachment: Attachment { + format: Some(context.properties().color_format), + samples: 1, + // Here we use PassPosition to get the proper operations + // Since, for example, the last pass needs to finish in present mode. + ops: P::attachment_ops(), + stencil_ops: P::attachment_ops(), + layouts: P::layout_as_range(), + }, + // This is the layout we want to deal with in our `queue_draw` function. + // It's almost certainly `Layout::ColorAttachmentOptimal` + used_layout: Layout::ColorAttachmentOptimal, + }], + depth: None, + inputs: vec![], + resolves: vec![], + preserves: vec![], + }) + .build() + .context("Error building pipeline")?; + + // Lock our device to actually build it + // Try to lock the device for as little time as possible + let mut device = context.lock_device()?; + + let pipeline = pipeline_spec + .build(&mut device, context.properties().extent, empty()) + .context("Error building pipeline")?; + + // Our framebuffers just have the swapchain framebuffer attachment + // TargetSpecificResources makes sure we use a different one each frame. + let fat = context.properties().swapchain_framebuffer_attachment(); + let framebuffers = TargetSpecificResources::new( + || unsafe { + Ok(device.create_framebuffer( + &pipeline.renderpass, + IntoIter::new([fat.clone()]), + context.properties().extent, + )?) + }, + context.properties().image_count as usize, + )?; + + (pipeline, framebuffers) + }; + + Ok(Self { + pipeline, + framebuffers, + }) + } + + pub fn deactivate(self, context: &mut RenderingContext) -> Result<()> { + unsafe { + let mut device = context.lock_device()?; + for fb in self.framebuffers.dissolve() { + device.destroy_framebuffer(fb); + } + + self.pipeline.deactivate(&mut device); + } + + Ok(()) + } +} diff --git a/examples/render-quad/src/level.rs b/examples/render-quad/src/level.rs deleted file mode 100644 index 74d1cc3..0000000 --- a/examples/render-quad/src/level.rs +++ /dev/null @@ -1,79 +0,0 @@ -use stockton_levels::parts::{ - data::{FaceRef, Geometry, TextureRef}, - HasFaces, HasTextures, HasVisData, IsFace, IsTexture, -}; -use stockton_skeleton::components::{CameraSettings, Transform}; - -pub struct DemoLevel { - pub faces: Box<[Face]>, - pub textures: Box<[Texture]>, -} - -impl DemoLevel { - fn face_idx(&self, search: &Face) -> FaceRef { - for (idx, face) in self.faces.iter().enumerate() { - if face == search { - return idx as u32; - } - } - panic!("face not in level") - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Face { - pub geometry: Geometry, - pub texture_idx: TextureRef, -} - -impl HasFaces for DemoLevel { - type Face = Face; - - fn get_face(&self, index: FaceRef) -> Option<&Self::Face> { - self.faces.get(index as usize) - } -} - -impl IsFace<DemoLevel> for Face { - fn index(&self, container: &DemoLevel) -> stockton_levels::parts::data::FaceRef { - container.face_idx(self) - } - - fn geometry(&self, _container: &DemoLevel) -> Geometry { - self.geometry.clone() - } - - fn texture_idx(&self, _container: &DemoLevel) -> TextureRef { - self.texture_idx - } -} - -pub struct Texture { - pub name: String, -} - -impl HasTextures for DemoLevel { - type Texture = Texture; - - fn get_texture(&self, idx: TextureRef) -> Option<&Self::Texture> { - self.textures.get(idx as usize) - } -} - -impl IsTexture for Texture { - fn name(&self) -> &str { - &self.name - } -} - -impl<'a> HasVisData<'a> for DemoLevel { - type Faces = std::ops::Range<FaceRef>; - - fn get_visible( - &'a self, - _transform: &Transform, - _settings: &CameraSettings, - ) -> Self::Faces { - 0..self.faces.len() as u32 - } -} diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs index c5d52a9..1955b51 100644 --- a/examples/render-quad/src/main.rs +++ b/examples/render-quad/src/main.rs @@ -1,82 +1,29 @@ //! Renders ./example.bsp geometry: (), texture_idx: () geometry: (), texture_idx: () -#[macro_use] -extern crate stockton_input_codegen; +extern crate gfx_hal as hal; #[macro_use] extern crate legion; -use std::{ - collections::BTreeMap, - path::Path, - sync::{Arc, RwLock}, -}; - -use stockton_contrib::{delta_time::*, flycam::*}; -use stockton_input::{Axis, InputManager, Mouse}; -use stockton_levels::{ - parts::{data::{Geometry, Vertex}, FsResolver}, - types::Rgba, -}; -use stockton_render::{ - level::{LevelDrawPass, LevelDrawPassConfig}, - ui::UiDrawPass, - window::{process_window_events_system, UiState, WindowEvent, WindowFlow}, -}; -use stockton_skeleton::{ - draw_passes::ConsDrawPass, error::full_error_display, Renderer, - Session, types::*, - components::{CameraSettings, Transform}, -}; - use anyhow::{Context, Result}; -use egui::{containers::CentralPanel, Frame}; use log::warn; +use stockton_skeleton::{error::full_error_display, Renderer, Session}; use winit::{ - event::Event, + event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; -mod level; -use level::*; - -type Dp<'a> = ConsDrawPass<LevelDrawPass<'a, DemoLevel>, UiDrawPass<'a>>; +mod draw_pass; +mod system; +use draw_pass::*; +use system::*; -#[derive(InputManager, Default, Clone, Debug)] -struct MovementInputs { - #[axis] - x: Axis, - - #[axis] - y: Axis, - - #[axis] - z: Axis, -} - -impl FlycamInput for MovementInputs { - fn get_x_axis(&self) -> &Axis { - &self.x - } - fn get_y_axis(&self) -> &Axis { - &self.y - } - fn get_z_axis(&self) -> &Axis { - &self.z - } -} - -#[system] -fn hello_world(#[resource] ui: &mut UiState) { - CentralPanel::default() - .frame(Frame::none()) - .show(ui.ctx(), |ui| { - ui.heading("Hello, World!"); - }); -} +/// Alias for our drawpass +type Dp<'a> = ExampleDrawPass<'a>; fn main() { + // Wrap for full error display if let Err(err) = try_main() { eprintln!("{}", full_error_display(err)); } @@ -101,165 +48,66 @@ fn try_main() -> Result<()> { .build(&event_loop) .context("Error creating window")?; + // Grab cursor + window.set_cursor_visible(false); if window.set_cursor_grab(true).is_err() { warn!("warning: cursor not grabbed"); } - window.set_cursor_visible(false); - - // TODO: Parse the map file - let map = Arc::new(RwLock::new(DemoLevel { - faces: vec![Face { - geometry: Geometry::Vertices( - Vertex { - position: Vector3::new(-128.0, 128.0, 128.0), - tex: Vector2::new(0.0, 0.0), - color: Rgba::from_slice(&[0, 0, 0, 1]), - }, - Vertex { - position: Vector3::new(-128.0, -128.0, 128.0), - tex: Vector2::new(0.0, 1.0), - color: Rgba::from_slice(&[0, 0, 0, 1]), - }, - Vertex { - position: Vector3::new(128.0, 128.0, 128.0), - tex: Vector2::new(1.0, 0.0), - color: Rgba::from_slice(&[0, 0, 0, 1]), - }, - ), - texture_idx: 0, - }] - .into_boxed_slice(), - textures: vec![Texture { - name: "example_texture".to_string(), - }] - .into_boxed_slice(), - })); - - // Create the UI State - let ui = UiState::default(); - - // Create the input manager - let manager = { - use stockton_input::InputMutation::*; - use MovementInputsFields::*; - - let mut actions = BTreeMap::new(); - actions.insert(17, (Z, PositiveAxis)); // W - actions.insert(30, (X, NegativeAxis)); // A - actions.insert(31, (Z, NegativeAxis)); // S - actions.insert(32, (X, PositiveAxis)); // D - actions.insert(29, (Y, NegativeAxis)); // Ctrl - actions.insert(57, (Y, PositiveAxis)); // Space - - MovementInputsManager::new(actions) - }; - - // Load everything into the session + // Our game world let mut session = Session::new(move |schedule| { - schedule - .add_system(update_deltatime_system()) - .add_system(process_window_events_system::<MovementInputsManager>()) - .flush() - .add_system(hello_world_system()) - .add_system(flycam_move_system::<MovementInputsManager>()); + schedule.add_system(mutate_state_system()); }); - session.resources.insert(map.clone()); - session.resources.insert(manager); - session.resources.insert(Timing::default()); - session.resources.insert(Mouse::default()); - session.resources.insert(ui); - - // Add our player entity - let player = session.world.push(( - Transform { - position: Vector3::new(0.0, 0.0, 0.0), - rotation: Vector3::new(0.0, 0.0, 0.0), - }, - CameraSettings { - far: 1024.0, - fov: 90.0, - near: 0.1, - }, - FlycamControlled::new(512.0, 400.0), - )); + // An entity to keep track of our state + let state_ent = session.world.push((ExampleState::default(),)); // Create the renderer - let renderer = Renderer::<Dp<'static>>::new( - &window, - &mut session, - ( - LevelDrawPassConfig { - active_camera: player, - tex_resolver: FsResolver::new(Path::new("./examples/render-quad/textures"), map), - }, - (), - ), - )?; - - let new_control_flow = Arc::new(RwLock::new(ControlFlow::Poll)); - let (window_flow, tx) = WindowFlow::new(new_control_flow.clone()); - session.resources.insert(window_flow); - - // Populate the initial UI state - { - let ui = &mut session.resources.get_mut::<UiState>().unwrap(); - ui.populate_initial_state(&renderer); - } + let renderer = + Renderer::<Dp<'static>>::new(&window, &mut session, ExampleDrawPassConfig { state_ent })?; + // We'll be moving it in/out of here, so we need an Option for safety. let mut renderer = Some(renderer); // Done loading - This is our main loop. // It just communicates events to the session and continuously ticks - event_loop.run(move |event, _, flow| { - match event { - Event::MainEventsCleared => { - window.request_redraw(); - } - Event::RedrawRequested(_) => { - session.do_update(); - let r = renderer.take().unwrap(); - match r.render(&session) { - Ok(r) => { - renderer = Some(r); - } - Err(e) => { - println!("Error drawing: {}", full_error_display(e)); - - // TODO: Not really sound - *(new_control_flow.write().unwrap()) = ControlFlow::Exit; - } + event_loop.run(move |event, _, flow| match event { + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(_) => { + session.do_update(); + + // Render + let r = renderer.take().unwrap(); + match r.render(&session) { + Ok(r) => { + renderer = Some(r); } - } - _ => { - if let Some(we) = WindowEvent::from(&event) { - tx.send(we).unwrap(); + Err(e) => { + println!("Error drawing: {}", full_error_display(e)); - if let WindowEvent::SizeChanged(_, _) = we { - let r = renderer.take().unwrap(); - match r.recreate_surface(&session) { - Ok(r) => { - renderer = Some(r); - } - Err(e) => { - println!("Error resizing: {}", full_error_display(e)); - - // TODO: Not really sound - *(new_control_flow.write().unwrap()) = ControlFlow::Exit; - } - } - } + *flow = ControlFlow::Exit; } } } + Event::WindowEvent { + window_id: _, + event: WindowEvent::Resized(_), + } => { + // (Attempt) resize + let r = renderer.take().unwrap(); + match r.recreate_surface(&session) { + Ok(r) => { + renderer = Some(r); + } + Err(e) => { + println!("Error resizing: {}", full_error_display(e)); - // Update the control flow if the session has requested it. - { - let new_control_flow = new_control_flow.read().unwrap(); - if *new_control_flow != *flow { - *flow = *new_control_flow; + *flow = ControlFlow::Exit; + } } - }; + } + _ => (), }); } diff --git a/examples/render-quad/src/system.rs b/examples/render-quad/src/system.rs new file mode 100644 index 0000000..3342774 --- /dev/null +++ b/examples/render-quad/src/system.rs @@ -0,0 +1,73 @@ +//! An example system that just alternates the colours of our triangle + +use stockton_skeleton::types::Vector3; + +/// RGB Channels +#[derive(Debug, Clone, Copy)] +enum ColorChannel { + Red, + Green, + Blue, +} + +/// A component for our entity. +#[derive(Debug, Clone, Copy)] +pub struct ExampleState { + channel: ColorChannel, + falling: bool, + col_val: Vector3, +} + +impl ExampleState { + pub fn color(&self) -> Vector3 { + self.col_val + } +} + +impl Default for ExampleState { + fn default() -> Self { + Self { + channel: ColorChannel::Red, + falling: true, + col_val: Vector3::new(1.0, 1.0, 1.0), + } + } +} + +/// The speed at which we change colour +const TRANSITION_SPEED: f32 = 0.1; + +/// Keep changing the colour of any ExampleStates in our world. +#[system(for_each)] +pub fn mutate_state(state: &mut ExampleState) { + // Which value we're changing + let val = match state.channel { + ColorChannel::Red => &mut state.col_val.x, + ColorChannel::Green => &mut state.col_val.y, + ColorChannel::Blue => &mut state.col_val.z, + }; + + if state.falling { + *val -= TRANSITION_SPEED; + + // Fall, then rise + if *val <= 0.0 { + *val = 0.0; + state.falling = false; + } + } else { + *val += TRANSITION_SPEED; + + if *val >= 1.0 { + *val = 1.0; + + // Rather than going back to falling, go to the next channel + state.falling = true; + state.channel = match state.channel { + ColorChannel::Red => ColorChannel::Green, + ColorChannel::Green => ColorChannel::Blue, + ColorChannel::Blue => ColorChannel::Red, + } + } + } +} diff --git a/stockton-contrib/Cargo.toml b/stockton-contrib/Cargo.toml index c6630e2..ba7a70f 100644 --- a/stockton-contrib/Cargo.toml +++ b/stockton-contrib/Cargo.toml @@ -12,7 +12,7 @@ stockton-input = { path = "../stockton-input" } legion = { version = "^0.3" } [features] -default = [] +default = ['delta_time'] delta_time = [] flycam = []
\ No newline at end of file diff --git a/stockton-contrib/src/flycam.rs b/stockton-contrib/src/flycam.rs index 42b80f6..b281a7a 100644 --- a/stockton-contrib/src/flycam.rs +++ b/stockton-contrib/src/flycam.rs @@ -1,7 +1,7 @@ use std::f32::consts::PI; use stockton_input::{Axis, InputManager, Mouse}; -use stockton_skeleton::{types::Vector3, components::Transform}; +use stockton_skeleton::{components::Transform, types::Vector3}; use crate::delta_time::Timing; diff --git a/stockton-levels/Cargo.toml b/stockton-levels/Cargo.toml deleted file mode 100644 index 448a8ba..0000000 --- a/stockton-levels/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "stockton-levels" -version = "0.1.0" -authors = ["Oscar <oscar.shrimpton.personal@gmail.com>"] -description = "Traits relating to levels renderable by stockton." -repository = "https://github.com/tcmal/stockton" -homepage = "https://github.com/tcmal/stockton" -edition = "2018" - -[dependencies] -nalgebra = "^0.20" -serde = { version = "1.0", features = ["derive"] } -stockton-skeleton = { path = "../stockton-skeleton" } -image = "0.23.11" diff --git a/stockton-levels/src/features.rs b/stockton-levels/src/features.rs deleted file mode 100644 index b4ddb99..0000000 --- a/stockton-levels/src/features.rs +++ /dev/null @@ -1,21 +0,0 @@ -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) -// any later version. - -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. - -// You should have received a copy of the GNU General Public License along -// with this program. If not, see <http://www.gnu.org/licenses/>. -//! Marker traits for different feature sets - -use crate::parts::*; - -pub trait MinRenderFeatures<'a>: HasFaces + HasTextures + HasVisData<'a> + Send + Sync {} -impl<'a, T> MinRenderFeatures<'a> for T where - T: HasFaces + HasTextures + HasVisData<'a> + Send + Sync -{ -} diff --git a/stockton-levels/src/lib.rs b/stockton-levels/src/lib.rs deleted file mode 100644 index 49609c2..0000000 --- a/stockton-levels/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) -// any later version. - -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. - -// You should have received a copy of the GNU General Public License along -// with this program. If not, see <http://www.gnu.org/licenses/>. -//! Interfaces & Data structures stockton expects when rendering a level. - -extern crate nalgebra as na; - -pub mod features; -pub mod parts; -pub mod prelude; -pub mod types; diff --git a/stockton-levels/src/parts/entities.rs b/stockton-levels/src/parts/entities.rs deleted file mode 100644 index 7a5ac74..0000000 --- a/stockton-levels/src/parts/entities.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::iter::Iterator; - -pub type EntityRef = u32; - -/// A game entity -pub trait IsEntity<C: HasEntities + ?Sized> { - fn get_attr(&self, container: &C) -> Option<&str>; -} - -pub trait HasEntities { - type Entity: IsEntity<Self>; - - fn get_entity(&self, idx: EntityRef) -> Option<&Self::Entity>; - fn iter_entities(&self) -> Entities<Self> { - Entities { - next: 0, - container: self, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Entities<'a, T: HasEntities + ?Sized> { - next: EntityRef, - container: &'a T, -} - -impl<'a, T: HasEntities> Iterator for Entities<'a, T> { - type Item = &'a T::Entity; - - fn next(&mut self) -> Option<Self::Item> { - let res = self.container.get_entity(self.next); - self.next += 1; - res - } -} diff --git a/stockton-levels/src/parts/faces.rs b/stockton-levels/src/parts/faces.rs deleted file mode 100644 index 1023af7..0000000 --- a/stockton-levels/src/parts/faces.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::{textures::TextureRef, vertices::Vertex}; -use serde::{Deserialize, Serialize}; - -pub type FaceRef = u32; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum Geometry { - Vertices(Vertex, Vertex, Vertex), -} - -pub trait IsFace<C: HasFaces + ?Sized> { - fn index(&self, container: &C) -> FaceRef; - fn geometry(&self, container: &C) -> Geometry; - fn texture_idx(&self, container: &C) -> TextureRef; -} - -pub trait HasFaces { - type Face: IsFace<Self>; - - fn get_face(&self, index: FaceRef) -> Option<&Self::Face>; - fn iter_faces(&self) -> Faces<Self> { - Faces { - next: 0, - container: self, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Faces<'a, T: HasFaces + ?Sized> { - next: FaceRef, - container: &'a T, -} - -impl<'a, T: HasFaces> Iterator for Faces<'a, T> { - type Item = &'a T::Face; - - fn next(&mut self) -> Option<Self::Item> { - let res = self.container.get_face(self.next); - self.next += 1; - res - } -} diff --git a/stockton-levels/src/parts/mod.rs b/stockton-levels/src/parts/mod.rs deleted file mode 100644 index d164bc3..0000000 --- a/stockton-levels/src/parts/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod entities; -mod faces; -mod textures; -mod vertices; -mod visdata; - -pub mod data { - pub use super::entities::{Entities, EntityRef}; - pub use super::faces::{FaceRef, Faces, Geometry}; - pub use super::textures::{TextureRef, Textures}; - pub use super::vertices::{Vertex, VertexRef}; -} - -pub use entities::{HasEntities, IsEntity}; -pub use faces::{HasFaces, IsFace}; -pub use textures::{HasTextures, IsTexture, FsResolver}; -pub use visdata::HasVisData; diff --git a/stockton-levels/src/parts/textures.rs b/stockton-levels/src/parts/textures.rs deleted file mode 100644 index ae946fd..0000000 --- a/stockton-levels/src/parts/textures.rs +++ /dev/null @@ -1,83 +0,0 @@ -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) -// any later version. - -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. - -// You should have received a copy of the GNU General Public License along -// with this program. If not, see <http://www.gnu.org/licenses/>. - -use std::{iter::Iterator, path::Path, sync::{Arc, RwLock}}; -use image::{RgbaImage, io::Reader}; -use stockton_skeleton::texture::TextureResolver; - -pub type TextureRef = u32; - -pub trait IsTexture { - fn name(&self) -> &str; -} - -pub trait HasTextures { - type Texture: IsTexture; - - fn get_texture(&self, idx: TextureRef) -> Option<&Self::Texture>; - fn iter_textures(&self) -> Textures<Self> { - Textures { - next: 0, - container: self, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Textures<'a, T: HasTextures + ?Sized> { - next: TextureRef, - container: &'a T, -} - -impl<'a, T: HasTextures> Iterator for Textures<'a, T> { - type Item = &'a T::Texture; - - fn next(&mut self) -> Option<Self::Item> { - let res = self.container.get_texture(self.next); - self.next += 1; - res - } -} - - -/// A basic filesystem resolver which gets the texture name from any HasTextures Object. -pub struct FsResolver<'a, T: HasTextures> { - path: &'a Path, - map_lock: Arc<RwLock<T>>, -} - -impl<'a, T: HasTextures> FsResolver<'a, T> { - pub fn new(path: &'a Path, map_lock: Arc<RwLock<T>>) -> Self { - FsResolver { path, map_lock } - } -} - -impl<'a, T: HasTextures> TextureResolver for FsResolver<'a, T> { - type Image = RgbaImage; - - fn resolve(&mut self, tex: u32) -> Option<Self::Image> { - let map = self.map_lock.read().unwrap(); - let tex = map.get_texture(tex)?; - let path = self.path.join(&tex.name()); - - if let Ok(file) = Reader::open(path) { - if let Ok(guessed) = file.with_guessed_format() { - if let Ok(decoded) = guessed.decode() { - return Some(decoded.into_rgba8()); - } - } - } - - None - } -} diff --git a/stockton-levels/src/parts/vertices.rs b/stockton-levels/src/parts/vertices.rs deleted file mode 100644 index d797afd..0000000 --- a/stockton-levels/src/parts/vertices.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::types::Rgba; -use na::{Vector2, Vector3}; -use serde::de; -use serde::de::{Deserializer, MapAccess, SeqAccess, Visitor}; -use serde::ser::{Serialize, SerializeStruct, Serializer}; -use serde::Deserialize; -use std::fmt; - -pub type VertexRef = u32; - -/// A vertex, used to describe a face. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Vertex { - pub position: Vector3<f32>, - pub tex: Vector2<f32>, - pub color: Rgba, -} - -impl Serialize for Vertex { - fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { - let mut state = serializer.serialize_struct("Vertex", 5)?; - state.serialize_field("pos_x", &self.position.x)?; - state.serialize_field("pos_y", &self.position.y)?; - state.serialize_field("pos_z", &self.position.z)?; - state.serialize_field("tex_u", &self.tex.x)?; - state.serialize_field("tex_v", &self.tex.y)?; - state.serialize_field("color", &self.color)?; - - state.end() - } -} - -impl<'de> Deserialize<'de> for Vertex { - fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "snake_case")] - enum Field { - PosX, - PosY, - PosZ, - TexU, - TexV, - Color, - } - const FIELDS: &[&str] = &["pos_x", "pos_y", "pos_z", "tex_x", "tex_y", "color"]; - - struct VertexVisitor; - - impl<'de> Visitor<'de> for VertexVisitor { - type Value = Vertex; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct Vertex") - } - - fn visit_seq<V>(self, mut seq: V) -> Result<Vertex, V::Error> - where - V: SeqAccess<'de>, - { - let pos_x = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - let pos_y = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - let pos_z = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(2, &self))?; - let tex_u = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(3, &self))?; - let tex_v = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(4, &self))?; - let color = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(5, &self))?; - Ok(Vertex { - position: Vector3::new(pos_x, pos_y, pos_z), - tex: Vector2::new(tex_u, tex_v), - color, - }) - } - - fn visit_map<V>(self, mut map: V) -> Result<Vertex, V::Error> - where - V: MapAccess<'de>, - { - let mut pos_x = None; - let mut pos_y = None; - let mut pos_z = None; - let mut tex_u = None; - let mut tex_v = None; - let mut color = None; - while let Some(key) = map.next_key()? { - match key { - Field::PosX => { - if pos_x.is_some() { - return Err(de::Error::duplicate_field("pos_x")); - } - pos_x = Some(map.next_value()?); - } - Field::PosY => { - if pos_y.is_some() { - return Err(de::Error::duplicate_field("pos_y")); - } - pos_y = Some(map.next_value()?); - } - Field::PosZ => { - if pos_z.is_some() { - return Err(de::Error::duplicate_field("pos_z")); - } - pos_z = Some(map.next_value()?); - } - Field::TexU => { - if tex_u.is_some() { - return Err(de::Error::duplicate_field("tex_u")); - } - tex_u = Some(map.next_value()?); - } - Field::TexV => { - if tex_v.is_some() { - return Err(de::Error::duplicate_field("tex_v")); - } - tex_v = Some(map.next_value()?); - } - Field::Color => { - if color.is_some() { - return Err(de::Error::duplicate_field("color")); - } - color = Some(map.next_value()?); - } - } - } - let position = Vector3::new( - pos_x.ok_or_else(|| de::Error::missing_field("pos_x"))?, - pos_y.ok_or_else(|| de::Error::missing_field("pos_y"))?, - pos_z.ok_or_else(|| de::Error::missing_field("pos_z"))?, - ); - let tex = Vector2::new( - tex_u.ok_or_else(|| de::Error::missing_field("tex_u"))?, - tex_v.ok_or_else(|| de::Error::missing_field("tex_v"))?, - ); - let color = color.ok_or_else(|| de::Error::missing_field("nanos"))?; - Ok(Vertex { - position, - tex, - color, - }) - } - } - - deserializer.deserialize_struct("Vertex", FIELDS, VertexVisitor) - } -} diff --git a/stockton-levels/src/parts/visdata.rs b/stockton-levels/src/parts/visdata.rs deleted file mode 100644 index 3e58d4c..0000000 --- a/stockton-levels/src/parts/visdata.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::faces::FaceRef; -use std::iter::Iterator; -use stockton_skeleton::components::{CameraSettings, Transform}; - -pub trait HasVisData<'a> { - type Faces: Iterator<Item = FaceRef>; - fn get_visible(&'a self, transform: &Transform, settings: &CameraSettings) -> Self::Faces; -} diff --git a/stockton-levels/src/prelude.rs b/stockton-levels/src/prelude.rs deleted file mode 100644 index 0da890b..0000000 --- a/stockton-levels/src/prelude.rs +++ /dev/null @@ -1,16 +0,0 @@ -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) -// any later version. - -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. - -// You should have received a copy of the GNU General Public License along -// with this program. If not, see <http://www.gnu.org/licenses/>. -//! Common traits, etc. - -pub use crate::features::*; -pub use crate::parts::*; diff --git a/stockton-levels/src/types.rs b/stockton-levels/src/types.rs deleted file mode 100644 index dad824c..0000000 --- a/stockton-levels/src/types.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Various types used in parsed BSP files. - -use serde::{Deserialize, Serialize}; -use std::convert::TryInto; - -/// RGBA Colour (0-255) -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] -pub struct Rgba { - pub r: u8, - pub g: u8, - pub b: u8, - pub a: u8, -} - -impl Rgba { - /// Interpret the given bytes as an RGBA colour. - pub fn from_bytes(bytes: [u8; 4]) -> Rgba { - Rgba { - r: bytes[0], - g: bytes[1], - b: bytes[2], - a: bytes[3], - } - } - - /// Convert a slice to an RGBA colour - /// # Panics - /// If slice is not 4 bytes long. - pub fn from_slice(slice: &[u8]) -> Rgba { - Rgba::from_bytes(slice.try_into().unwrap()) - } -} - -/// RGB Colour (0-255) -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] -pub struct Rgb { - pub r: u8, - pub g: u8, - pub b: u8, -} - -impl Rgb { - /// 255, 255, 255 - pub fn white() -> Rgb { - Rgb { - r: 255, - g: 255, - b: 255, - } - } - - /// Interpret the given bytes as an RGB colour. - pub fn from_bytes(bytes: [u8; 3]) -> Rgb { - Rgb { - r: bytes[0], - g: bytes[1], - b: bytes[2], - } - } - - /// Convert a slice to an RGB colour - /// # Panics - /// If slice is not 3 bytes long. - pub fn from_slice(slice: &[u8]) -> Rgb { - Rgb::from_bytes(slice.try_into().unwrap()) - } -} diff --git a/stockton-render/Cargo.toml b/stockton-render/Cargo.toml deleted file mode 100644 index 3ba8c01..0000000 --- a/stockton-render/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "stockton-render" -version = "0.1.0" -authors = ["tcmal <oscar.shrimpton.personal@gmail.com>"] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -stockton-skeleton = { path = "../stockton-skeleton" } -stockton-levels = { path = "../stockton-levels" } -stockton-input = { path = "../stockton-input" } -anyhow = "1.0.40" -egui = "^0.12" -epaint = "^0.12" -gfx-hal = "^0.8.0" -shaderc = "^0.7" -legion = { version = "^0.3" } -log = "0.4.0" -winit = "^0.21" -nalgebra-glm = "^0.6" -thiserror = "1.0.25" diff --git a/stockton-render/src/data/3d.frag b/stockton-render/src/data/3d.frag deleted file mode 100644 index 336d9fe..0000000 --- a/stockton-render/src/data/3d.frag +++ /dev/null @@ -1,15 +0,0 @@ -#version 450 - -// DescriptorSet 0 = Textures -layout(set = 0, binding = 0) uniform texture2D tex[8]; -layout(set = 0, binding = 1) uniform sampler samp[8]; - -layout (location = 1) in vec2 frag_uv; -layout (location = 2) in flat int frag_tex; - -layout (location = 0) out vec4 color; - -void main() -{ - color = texture(sampler2D(tex[frag_tex % 8], samp[frag_tex % 8]), frag_uv); -}
\ No newline at end of file diff --git a/stockton-render/src/data/3d.vert b/stockton-render/src/data/3d.vert deleted file mode 100644 index aaee1a5..0000000 --- a/stockton-render/src/data/3d.vert +++ /dev/null @@ -1,23 +0,0 @@ -#version 450 - -// DescriptorSet 0 = Matrices -layout (push_constant) uniform PushConsts { - mat4 vp; -} push; - -layout (location = 0) in vec3 position; -layout (location = 1) in int tex; -layout (location = 2) in vec2 uv; - -out gl_PerVertex { - vec4 gl_Position; -}; -layout (location = 1) out vec2 frag_uv; -layout (location = 2) out flat int frag_tex; - -void main() -{ - gl_Position = push.vp * vec4(position, 1.0); - frag_uv = uv; - frag_tex = tex; -}
\ No newline at end of file diff --git a/stockton-render/src/data/ui.frag b/stockton-render/src/data/ui.frag deleted file mode 100644 index c30c99e..0000000 --- a/stockton-render/src/data/ui.frag +++ /dev/null @@ -1,15 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -// DescriptorSet 0 = Textures -layout(set = 0, binding = 0) uniform texture2D tex[8]; -layout(set = 0, binding = 1) uniform sampler samp[8]; - -layout (location = 1) in vec2 frag_uv; -layout (location = 2) in vec4 frag_col; - -layout (location = 0) out vec4 color; - -void main() { - color = texture(sampler2D(tex[0], samp[0]), frag_uv) * frag_col; -}
\ No newline at end of file diff --git a/stockton-render/src/data/ui.vert b/stockton-render/src/data/ui.vert deleted file mode 100644 index 8912e96..0000000 --- a/stockton-render/src/data/ui.vert +++ /dev/null @@ -1,37 +0,0 @@ -#version 450 - -layout (push_constant) uniform PushConsts { - vec2 screen_size; -} push; - -layout (location = 0) in vec2 pos; -layout (location = 1) in vec2 uv; -layout (location = 2) in vec4 col; - -out gl_PerVertex { - vec4 gl_Position; -}; -layout (location = 1) out vec2 frag_uv; -layout (location = 2) out vec4 frag_col; - -vec3 linear_from_srgb(vec3 srgb) { - bvec3 cutoff = lessThan(srgb, vec3(10.31475)); - vec3 lower = srgb / vec3(3294.6); - vec3 higher = pow((srgb + vec3(14.025)) / vec3(269.025), vec3(2.4)); - return mix(higher, lower, cutoff); -} - -vec4 linear_from_srgba(vec4 srgba) { - return vec4(linear_from_srgb(srgba.rgb * 255.0), srgba.a); -} - -void main() { - gl_Position = vec4( - 2.0 * pos.x / push.screen_size.x - 1.0, - 2.0 * pos.y / push.screen_size.y - 1.0, - 0.0, - 1.0 - ); - frag_uv = uv; - frag_col = linear_from_srgba(col); -} diff --git a/stockton-render/src/level.rs b/stockton-render/src/level.rs deleted file mode 100644 index dd4d61a..0000000 --- a/stockton-render/src/level.rs +++ /dev/null @@ -1,554 +0,0 @@ -//! Minimal code for drawing any level, based on traits from stockton-levels - -use stockton_levels::{ - features::MinRenderFeatures, - parts::{data::Geometry, IsFace}, -}; -use stockton_skeleton::{ - buffers::{ - draw::{DrawBuffers, INITIAL_INDEX_SIZE, INITIAL_VERT_SIZE}, - image::{BoundImageView, ImageSpec, DEPTH_RESOURCES}, - }, - builders::{ - AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, - VertexBufferSpec, VertexPrimitiveAssemblerSpec, - }, - RenderingContext, - Session, - DrawPass, IntoDrawPass, PassPosition, - draw_passes::{util::TargetSpecificResources}, - error::LockPoisoned, - mem::{DataPool, DepthBufferPool, StagingPool, TexturesPool}, - queue_negotiator::QueueFamilyNegotiator, - texture::{TextureResolver, TexLoadQueue, TextureLoadConfig, TextureRepo}, - types::*, - components::{CameraSettings, Transform}, -}; - -use anyhow::{Context, Result}; -use hal::{ - buffer::SubRange, - command::{ClearColor, ClearDepthStencil, ClearValue, RenderAttachmentInfo, SubpassContents}, - format::Format, - image::{Filter, FramebufferAttachment, Layout, Usage, ViewCapabilities, WrapMode}, - pass::{Attachment, AttachmentLoadOp, AttachmentOps, AttachmentStoreOp}, - pso::{ - BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, Comparison, DepthStencilDesc, - DepthTest, Face, Factor, FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, - Rasterizer, ShaderStageFlags, State, VertexInputRate, - }, -}; -use legion::{Entity, IntoQuery}; -use na::{look_at_lh, perspective_lh_zo}; -use shaderc::ShaderKind; -use std::{ - array::IntoIter, - convert::TryInto, - iter::{empty, once}, - marker::PhantomData, - sync::{Arc, RwLock}, -}; -use thiserror::Error; - -/// The Vertexes that go to the shader -#[derive(Debug, Clone, Copy)] -struct UvPoint(pub Vector3, pub i32, pub Vector2); - -/// Draw a level -pub struct LevelDrawPass<'a, M> { - repo: TextureRepo<TexturesPool, StagingPool>, - active_camera: Entity, - draw_buffers: DrawBuffers<'a, UvPoint, DataPool, StagingPool>, - surface_resources: SurfaceDependentResources, - _d: PhantomData<M>, -} - -impl<'a, M, P: PassPosition> DrawPass<P> for LevelDrawPass<'a, M> -where - M: for<'b> MinRenderFeatures<'b> + 'static, -{ - fn queue_draw( - &mut self, - session: &Session, - img_view: &ImageViewT, - cmd_buffer: &mut CommandBufferT, - ) -> anyhow::Result<()> { - // We might have loaded more textures - self.repo.process_responses(); - - // Make sure we update the vertex buffers after they're written to, but before they're read from. - self.draw_buffers - .vertex_buffer - .record_commit_cmds(cmd_buffer)?; - self.draw_buffers - .index_buffer - .record_commit_cmds(cmd_buffer)?; - - // Get level & camera - let mut query = <(&Transform, &CameraSettings)>::query(); - let (camera_transform, camera_settings) = query - .get(&session.world, self.active_camera) - .context("Couldn't find camera components")?; - - let camera_vp = { - let aspect_ratio = self.surface_resources.pipeline.render_area.w as f32 - / self.surface_resources.pipeline.render_area.h as f32; - - // Get look direction from euler angles - let direction = euler_to_direction(&camera_transform.rotation); - - // Converts world space to camera space - let view_matrix = look_at_lh( - &camera_transform.position, - &(camera_transform.position + direction), - &Vector3::new(0.0, 1.0, 0.0), - ); - - // Converts camera space to screen space - let projection_matrix = { - let mut temp = perspective_lh_zo( - aspect_ratio, - camera_settings.fov, - camera_settings.near, - camera_settings.far, - ); - - // Vulkan's co-ord system is different from OpenGLs - temp[(1, 1)] *= -1.0; - - temp - }; - - // Chain them together into a single matrix - projection_matrix * view_matrix - }; - let map_lock: Arc<RwLock<M>> = session.resources.get::<Arc<RwLock<M>>>().unwrap().clone(); - let map = map_lock.read().map_err(|_| LockPoisoned::Map)?; - - // Get framebuffer and depth buffer - let fb = self.surface_resources.framebuffers.get_next(); - let db = self.surface_resources.depth_buffers.get_next(); - - unsafe { - cmd_buffer.begin_render_pass( - &self.surface_resources.pipeline.renderpass, - fb, - self.surface_resources.pipeline.render_area, - vec![ - RenderAttachmentInfo { - image_view: img_view, - clear_value: ClearValue { - color: ClearColor { - float32: [0.0, 0.0, 0.0, 1.0], - }, - }, - }, - RenderAttachmentInfo { - image_view: &*db.img_view(), - clear_value: ClearValue { - depth_stencil: ClearDepthStencil { - depth: 1.0, - stencil: 0, - }, - }, - }, - ] - .into_iter(), - SubpassContents::Inline, - ); - cmd_buffer.bind_graphics_pipeline(&self.surface_resources.pipeline.pipeline); - - // VP Matrix - let vp = &*(camera_vp.data.as_slice() as *const [f32] as *const [u32]); - - cmd_buffer.push_graphics_constants( - &self.surface_resources.pipeline.pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - vp, - ); - - // Bind buffers - cmd_buffer.bind_vertex_buffers( - 0, - once(( - self.draw_buffers.vertex_buffer.get_buffer(), - SubRange { - offset: 0, - size: None, - }, - )), - ); - cmd_buffer.bind_index_buffer( - self.draw_buffers.index_buffer.get_buffer(), - SubRange { - offset: 0, - size: None, - }, - hal::IndexType::U16, - ); - } - - // Get visible faces - let mut faces = map.get_visible(camera_transform, camera_settings); - - // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. - let face = faces.next(); - if let Some(face) = face { - let mut face = map.get_face(face).ok_or(LevelError::BadReference)?; - let mut current_chunk = face.texture_idx(&map) as usize / 8; - let mut chunk_start = 0; - - let mut curr_vert_idx: usize = 0; - let mut curr_idx_idx: usize = 0; - loop { - if current_chunk != face.texture_idx(&map) as usize / 8 { - // Last index was last of group, so draw it all if textures are loaded. - self.draw_or_queue( - current_chunk, - cmd_buffer, - chunk_start as u32, - curr_idx_idx as u32, - )?; - - // Next group of same-chunked faces starts here. - chunk_start = curr_idx_idx; - current_chunk = face.texture_idx(&map) as usize / 8; - } - - match face.geometry(&map) { - Geometry::Vertices(v1, v2, v3) => { - for v in &[v1, v2, v3] { - let uvp = - UvPoint(v.position, face.texture_idx(&map).try_into()?, v.tex); - - self.draw_buffers.vertex_buffer[curr_vert_idx] = uvp; - curr_vert_idx += 1; - } - self.draw_buffers.index_buffer[curr_idx_idx] = ( - curr_vert_idx as u16 - 3, - curr_vert_idx as u16 - 2, - curr_vert_idx as u16 - 1, - ); - curr_idx_idx += 1; - } - } - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? - { - println!("out of vertex buffer space!"); - break; - } - - match faces.next() { - Some(x) => face = map.get_face(x).ok_or(LevelError::BadReference)?, - None => break, - }; - } - - // Draw the final group of chunks - self.draw_or_queue( - current_chunk, - cmd_buffer, - chunk_start as u32, - curr_idx_idx as u32, - )?; - } - - unsafe { - cmd_buffer.end_render_pass(); - } - - Ok(()) - } - - fn deactivate(self, context: &mut RenderingContext) -> Result<()> { - self.draw_buffers.deactivate(context); - self.surface_resources.deactivate(context)?; - self.repo.deactivate(context); - - Ok(()) - } - - fn handle_surface_change( - mut self, - _session: &Session, - context: &mut RenderingContext, - ) -> Result<Self> { - let new_resources = - SurfaceDependentResources::new::<P>(context, &*self.repo.get_ds_layout()?)?; - let old_resources = self.surface_resources; - self.surface_resources = new_resources; - - match old_resources.deactivate(context) { - Ok(_) => Ok(self), - Err(e) => { - <Self as DrawPass<P>>::deactivate(self, context)?; - Err(e) - } - } - } -} - -impl<'a, M> LevelDrawPass<'a, M> { - fn draw_or_queue( - &mut self, - current_chunk: usize, - cmd_buffer: &mut CommandBufferT, - chunk_start: u32, - curr_idx_idx: u32, - ) -> Result<()> { - if let Some(ds) = self.repo.attempt_get_descriptor_set(current_chunk) { - unsafe { - cmd_buffer.bind_graphics_descriptor_sets( - &*self.surface_resources.pipeline.pipeline_layout, - 0, - once(ds), - empty(), - ); - cmd_buffer.draw_indexed(chunk_start * 3..(curr_idx_idx * 3) + 1, 0, 0..1); - } - } else { - self.repo.queue_load(current_chunk)? - } - - Ok(()) - } -} - -pub struct LevelDrawPassConfig<R> { - pub active_camera: Entity, - pub tex_resolver: R, -} - -impl<'a, M, R, P> IntoDrawPass<LevelDrawPass<'a, M>, P> for LevelDrawPassConfig<R> -where - M: for<'b> MinRenderFeatures<'b> + 'static, - R: TextureResolver + Send + Sync + 'static, - P: PassPosition, -{ - fn init( - self, - _session: &mut Session, - context: &mut RenderingContext, - ) -> Result<LevelDrawPass<'a, M>> { - let repo = TextureRepo::new::<_, TexLoadQueue>( - context, - TextureLoadConfig { - resolver: self.tex_resolver, - filter: Filter::Linear, - wrap_mode: WrapMode::Tile, - }, - ) - .context("Error creating texture repo")?; - let draw_buffers = - DrawBuffers::from_context(context).context("Error creating draw buffers")?; - - let surface_resources = - SurfaceDependentResources::new::<P>(context, &*repo.get_ds_layout()?)?; - - Ok(LevelDrawPass { - repo, - draw_buffers, - active_camera: self.active_camera, - surface_resources, - _d: PhantomData, - }) - } - - fn find_aux_queues( - adapter: &Adapter, - queue_negotiator: &mut QueueFamilyNegotiator, - ) -> Result<()> { - queue_negotiator.find(adapter, &TexLoadQueue, 1)?; - - Ok(()) - } -} - -/// Indicates an issue with the level object being used -#[derive(Debug, Error)] -pub enum LevelError { - #[error("Referential Integrity broken")] - BadReference, -} - -/// Used to store resources which depend on the surface, for convenience in handle_surface_change -struct SurfaceDependentResources { - pub pipeline: CompletePipeline, - pub framebuffers: TargetSpecificResources<FramebufferT>, - pub depth_buffers: TargetSpecificResources<BoundImageView<DepthBufferPool>>, -} - -impl SurfaceDependentResources { - pub fn new<P: PassPosition>( - context: &mut RenderingContext, - ds_layout: &DescriptorSetLayoutT, - ) -> Result<Self> { - let db_spec = ImageSpec { - width: context.properties().extent.width, - height: context.properties().extent.height, - format: context.properties().depth_format, - usage: Usage::DEPTH_STENCIL_ATTACHMENT, - resources: DEPTH_RESOURCES, - }; - let img_count = context.properties().image_count; - - let depth_buffers = TargetSpecificResources::new( - || { - BoundImageView::from_context(context, &db_spec) - .context("Error creating depth buffer") - }, - img_count as usize, - )?; - - let (pipeline, framebuffers) = { - let pipeline_spec = PipelineSpecBuilder::default() - .rasterizer(Rasterizer { - polygon_mode: PolygonMode::Fill, - cull_face: Face::BACK, - front_face: FrontFace::CounterClockwise, - depth_clamping: false, - depth_bias: None, - conservative: true, - line_width: State::Static(1.0), - }) - .depth_stencil(DepthStencilDesc { - depth: Some(DepthTest { - fun: Comparison::Less, - write: true, - }), - depth_bounds: false, - stencil: None, - }) - .blender(BlendDesc { - logic_op: Some(LogicOp::Copy), - targets: vec![ColorBlendDesc { - mask: ColorMask::ALL, - blend: Some(BlendState { - color: BlendOp::Add { - src: Factor::SrcAlpha, - dst: Factor::OneMinusSrcAlpha, - }, - alpha: BlendOp::Add { - src: Factor::SrcAlpha, - dst: Factor::OneMinusSrcAlpha, - }, - }), - }], - }) - .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers( - InputAssemblerDesc::new(Primitive::TriangleList), - vec![VertexBufferSpec { - attributes: vec![Format::Rgb32Sfloat, Format::R32Sint, Format::Rg32Sfloat], - rate: VertexInputRate::Vertex, - }], - )) - .shader_vertex(ShaderDesc { - source: include_str!("./data/3d.vert").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Vertex, - }) - .shader_fragment(ShaderDesc { - source: include_str!("./data/3d.frag").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Fragment, - }) - .push_constants(vec![(ShaderStageFlags::VERTEX, 0..64)]) - .renderpass(RenderpassSpec { - colors: vec![AttachmentSpec { - attachment: Attachment { - format: Some(context.properties().color_format), - samples: 1, - ops: P::attachment_ops(), - stencil_ops: P::attachment_ops(), - layouts: P::layout_as_range(), - }, - - used_layout: Layout::ColorAttachmentOptimal, - }], - depth: Some(AttachmentSpec { - attachment: Attachment { - format: Some(context.properties().depth_format), - samples: 1, - ops: AttachmentOps::new( - AttachmentLoadOp::Clear, - AttachmentStoreOp::DontCare, - ), - stencil_ops: AttachmentOps::new( - AttachmentLoadOp::DontCare, - AttachmentStoreOp::DontCare, - ), - layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal, - }, - used_layout: Layout::DepthStencilAttachmentOptimal, - }), - inputs: vec![], - resolves: vec![], - preserves: vec![], - }) - .build() - .context("Error building pipeline")?; - let mut device = context.lock_device()?; - - let pipeline = pipeline_spec - .build(&mut device, context.properties().extent, once(ds_layout)) - .context("Error building pipeline")?; - - let fat = context.properties().swapchain_framebuffer_attachment(); - let dat = FramebufferAttachment { - usage: Usage::DEPTH_STENCIL_ATTACHMENT, - format: context.properties().depth_format, - view_caps: ViewCapabilities::empty(), - }; - - let framebuffers = TargetSpecificResources::new( - || unsafe { - Ok(device.create_framebuffer( - &pipeline.renderpass, - IntoIter::new([fat.clone(), dat.clone()]), - context.properties().extent, - )?) - }, - context.properties().image_count as usize, - )?; - - (pipeline, framebuffers) - }; - - Ok(Self { - pipeline, - framebuffers, - depth_buffers, - }) - } - - pub fn deactivate(self, context: &mut RenderingContext) -> Result<()> { - for db in self.depth_buffers.dissolve() { - db.deactivate_with_context(context); - } - unsafe { - let mut device = context.lock_device()?; - for fb in self.framebuffers.dissolve() { - device.destroy_framebuffer(fb); - } - - self.pipeline.deactivate(&mut device); - } - - Ok(()) - } -} - -fn euler_to_direction(euler: &Vector3) -> Vector3 { - let pitch = euler.x; - let yaw = euler.y; - let _roll = euler.z; // TODO: Support camera roll - - Vector3::new( - yaw.sin() * pitch.cos(), - pitch.sin(), - yaw.cos() * pitch.cos(), - ) -} diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs deleted file mode 100644 index 34f5117..0000000 --- a/stockton-render/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[macro_use] -extern crate legion; -extern crate gfx_hal as hal; -extern crate nalgebra_glm as na; - -pub mod level; -pub mod ui; -pub mod window; diff --git a/stockton-render/src/ui.rs b/stockton-render/src/ui.rs deleted file mode 100644 index 981be02..0000000 --- a/stockton-render/src/ui.rs +++ /dev/null @@ -1,421 +0,0 @@ -//! Minimal code for drawing any level, based on traits from stockton-levels -use crate::window::UiState; - -use stockton_skeleton::{ - buffers::draw::DrawBuffers, - builders::{ - AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, - VertexBufferSpec, VertexPrimitiveAssemblerSpec, - }, - RenderingContext, - Session, - draw_passes::{util::TargetSpecificResources, DrawPass, IntoDrawPass, PassPosition}, - mem::{DataPool, StagingPool, TexturesPool}, - queue_negotiator::QueueFamilyNegotiator, - texture::{ - TextureResolver, LoadableImage, TexLoadQueue, TextureLoadConfig, TextureRepo, - }, - types::*, -}; - -use std::{ - array::IntoIter, - convert::TryInto, - iter::{empty, once}, - sync::Arc, -}; - -use anyhow::{anyhow, Context, Result}; -use egui::{ClippedMesh, TextureId}; -use egui::{CtxRef, Texture}; -use hal::{ - buffer::SubRange, - command::{ClearColor, ClearValue, RenderAttachmentInfo, SubpassContents}, - format::Format, - image::Layout, - pass::Attachment, - pso::{ - BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, DepthStencilDesc, Face, Factor, - FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, Rasterizer, Rect, - ShaderStageFlags, State, VertexInputRate, - }, -}; -use shaderc::ShaderKind; - -#[derive(Debug)] -pub struct UiPoint(pub Vector2, pub Vector2, pub [f32; 4]); - -/// Draw a Ui object -pub struct UiDrawPass<'a> { - repo: TextureRepo<TexturesPool, StagingPool>, - draw_buffers: DrawBuffers<'a, UiPoint, DataPool, StagingPool>, - - surface_resources: SurfaceDependentResources, -} - -impl<'a, P: PassPosition> DrawPass<P> for UiDrawPass<'a> { - fn queue_draw( - &mut self, - session: &Session, - img_view: &ImageViewT, - cmd_buffer: &mut CommandBufferT, - ) -> anyhow::Result<()> { - // We might have loaded more textures - self.repo.process_responses(); - - // Make sure we update the vertex buffers after they're written to, but before they're read from. - self.draw_buffers - .vertex_buffer - .record_commit_cmds(cmd_buffer)?; - self.draw_buffers - .index_buffer - .record_commit_cmds(cmd_buffer)?; - - // Get level & camera - let ui: &mut UiState = &mut session.resources.get_mut::<UiState>().unwrap(); - - // Get framebuffer and depth buffer - let fb = self.surface_resources.framebuffers.get_next(); - unsafe { - cmd_buffer.begin_render_pass( - &self.surface_resources.pipeline.renderpass, - fb, - self.surface_resources.pipeline.render_area, - vec![RenderAttachmentInfo { - image_view: img_view, - clear_value: ClearValue { - color: ClearColor { - float32: [0.0, 0.0, 0.0, 1.0], - }, - }, - }] - .into_iter(), - SubpassContents::Inline, - ); - cmd_buffer.bind_graphics_pipeline(&self.surface_resources.pipeline.pipeline); - - // Bind buffers - cmd_buffer.bind_vertex_buffers( - 0, - once(( - self.draw_buffers.vertex_buffer.get_buffer(), - SubRange { - offset: 0, - size: None, - }, - )), - ); - cmd_buffer.bind_index_buffer( - self.draw_buffers.index_buffer.get_buffer(), - SubRange { - offset: 0, - size: None, - }, - hal::IndexType::U16, - ); - } - - let (_out, shapes) = ui.end_frame(); - let screen = ui - .dimensions() - .ok_or_else(|| anyhow!("UI not set up properly."))?; - let shapes = ui.ctx().tessellate(shapes); - - let mut next_idx_idx = 0; - let mut next_vert_idx = 0; - for ClippedMesh(rect, tris) in shapes.iter() { - assert!(tris.texture_id == TextureId::Egui); - - // Copy triangles/indicies - for i in (0..tris.indices.len()).step_by(3) { - self.draw_buffers.index_buffer[next_idx_idx + (i / 3)] = ( - tris.indices[i].try_into()?, - tris.indices[i + 1].try_into()?, - tris.indices[i + 2].try_into()?, - ); - } - - for (i, vertex) in tris.vertices.iter().enumerate() { - self.draw_buffers.vertex_buffer[next_vert_idx + i] = UiPoint( - Vector2::new(vertex.pos.x, vertex.pos.y), - Vector2::new(vertex.uv.x, vertex.uv.y), - [ - vertex.color.r() as f32 / 255.0, - vertex.color.g() as f32 / 255.0, - vertex.color.b() as f32 / 255.0, - vertex.color.a() as f32 / 255.0, - ], - ); - } - - // TODO: *Properly* deal with textures - if let Some(ds) = self.repo.attempt_get_descriptor_set(0) { - unsafe { - cmd_buffer.push_graphics_constants( - &self.surface_resources.pipeline.pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - &[screen.x.to_bits(), screen.y.to_bits()], - ); - - cmd_buffer.set_scissors( - 0, - IntoIter::new([Rect { - x: rect.min.x as i16, - y: rect.min.y as i16, - w: rect.width() as i16, - h: rect.height() as i16, - }]), - ); - cmd_buffer.bind_graphics_descriptor_sets( - &self.surface_resources.pipeline.pipeline_layout, - 0, - IntoIter::new([ds]), - empty(), - ); - // Call draw - cmd_buffer.draw_indexed( - (next_idx_idx as u32 * 3)..((next_idx_idx * 3) + tris.indices.len()) as u32, - next_vert_idx as i32, - 0..1, - ); - } - } else { - self.repo.queue_load(0)?; - } - - next_idx_idx += tris.indices.len() / 3; - next_vert_idx += tris.vertices.len(); - } - - unsafe { - cmd_buffer.end_render_pass(); - } - - Ok(()) - } - - fn deactivate(self, context: &mut RenderingContext) -> Result<()> { - self.draw_buffers.deactivate(context); - self.surface_resources.deactivate(context)?; - self.repo.deactivate(context); - - Ok(()) - } - - fn handle_surface_change( - mut self, - _session: &Session, - context: &mut RenderingContext, - ) -> Result<Self> { - let new_surface_resources = - SurfaceDependentResources::new::<P>(context, &*self.repo.get_ds_layout()?)?; - let old_surface_resources = self.surface_resources; - self.surface_resources = new_surface_resources; - - match old_surface_resources.deactivate(context) { - Ok(_) => Ok(self), - Err(e) => { - <Self as DrawPass<P>>::deactivate(self, context)?; - Err(e) - } - } - } -} - -impl<'a, P: PassPosition> IntoDrawPass<UiDrawPass<'a>, P> for () { - fn init(self, session: &mut Session, context: &mut RenderingContext) -> Result<UiDrawPass<'a>> { - let ui: &mut UiState = &mut session.resources.get_mut::<UiState>().unwrap(); - let repo = TextureRepo::new::<_, TexLoadQueue>( - context, - TextureLoadConfig { - resolver: UiTextures::new(ui.ctx().clone()), - filter: hal::image::Filter::Linear, - wrap_mode: hal::image::WrapMode::Clamp, - }, - ) - .context("Error creating texture repo")?; - - let draw_buffers = - DrawBuffers::from_context(context).context("Error creating draw buffers")?; - - let surface_resources = - SurfaceDependentResources::new::<P>(context, &*repo.get_ds_layout()?)?; - - Ok(UiDrawPass { - repo, - draw_buffers, - surface_resources, - }) - } - - fn find_aux_queues( - adapter: &Adapter, - queue_negotiator: &mut QueueFamilyNegotiator, - ) -> Result<()> { - queue_negotiator.find(adapter, &TexLoadQueue, 1)?; - - Ok(()) - } -} - -pub struct UiTexture(Arc<Texture>); - -pub struct UiTextures { - ctx: CtxRef, -} - -impl TextureResolver for UiTextures { - type Image = UiTexture; - fn resolve(&mut self, tex: u32) -> Option<Self::Image> { - if tex == 0 { - Some(UiTexture(self.ctx.texture())) - } else { - None - } - } -} - -impl UiTextures { - pub fn new(ctx: CtxRef) -> Self { - UiTextures { ctx } - } -} - -impl LoadableImage for UiTexture { - fn width(&self) -> u32 { - self.0.width as u32 - } - fn height(&self) -> u32 { - self.0.height as u32 - } - unsafe fn copy_row(&self, y: u32, ptr: *mut u8) { - let row_size = self.0.width as u32; - let pixels = &self.0.pixels[(y * row_size) as usize..((y + 1) * row_size) as usize]; - - for (i, x) in pixels.iter().enumerate() { - *ptr.offset(i as isize * 4) = 255; - *ptr.offset((i as isize * 4) + 1) = 255; - *ptr.offset((i as isize * 4) + 2) = 255; - *ptr.offset((i as isize * 4) + 3) = *x; - } - } -} - -struct SurfaceDependentResources { - pipeline: CompletePipeline, - framebuffers: TargetSpecificResources<FramebufferT>, -} - -impl SurfaceDependentResources { - fn new<P: PassPosition>( - context: &mut RenderingContext, - ds_layout: &DescriptorSetLayoutT, - ) -> Result<Self> { - let mut device = context.lock_device()?; - - let spec = PipelineSpecBuilder::default() - .rasterizer(Rasterizer { - polygon_mode: PolygonMode::Fill, - cull_face: Face::NONE, - front_face: FrontFace::CounterClockwise, - depth_clamping: false, - depth_bias: None, - conservative: true, - line_width: State::Static(1.0), - }) - .depth_stencil(DepthStencilDesc { - depth: None, - depth_bounds: false, - stencil: None, - }) - .blender(BlendDesc { - logic_op: Some(LogicOp::Copy), - targets: vec![ColorBlendDesc { - mask: ColorMask::ALL, - blend: Some(BlendState { - color: BlendOp::Add { - src: Factor::SrcAlpha, - dst: Factor::OneMinusSrcAlpha, - }, - alpha: BlendOp::Add { - src: Factor::SrcAlpha, - dst: Factor::OneMinusSrcAlpha, - }, - }), - }], - }) - .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers( - InputAssemblerDesc::new(Primitive::TriangleList), - vec![VertexBufferSpec { - attributes: vec![Format::Rg32Sfloat, Format::Rg32Sfloat, Format::Rgba32Sfloat], - rate: VertexInputRate::Vertex, - }], - )) - .shader_vertex(ShaderDesc { - source: include_str!("./data/ui.vert").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Vertex, - }) - .shader_fragment(ShaderDesc { - source: include_str!("./data/ui.frag").to_string(), - entry: "main".to_string(), - kind: ShaderKind::Fragment, - }) - .push_constants(vec![(ShaderStageFlags::VERTEX, 0..8)]) - .renderpass(RenderpassSpec { - colors: vec![AttachmentSpec { - attachment: Attachment { - format: Some(context.properties().color_format), - samples: 1, - ops: P::attachment_ops(), - stencil_ops: P::attachment_ops(), - layouts: P::layout_as_range(), - }, - used_layout: Layout::ColorAttachmentOptimal, - }], - depth: None, - inputs: vec![], - resolves: vec![], - preserves: vec![], - }) - .dynamic_scissor(true) - .build() - .context("Error building pipeline")?; - - let pipeline = spec - .build(&mut device, context.properties().extent, once(ds_layout)) - .context("Error building pipeline")?; - - let fat = context.properties().swapchain_framebuffer_attachment(); - - let framebuffers = TargetSpecificResources::new( - || unsafe { - Ok(device.create_framebuffer( - &pipeline.renderpass, - IntoIter::new([fat.clone()]), - context.properties().extent, - )?) - }, - context.properties().image_count as usize, - )?; - - Ok(Self { - framebuffers, - pipeline, - }) - } - - fn deactivate(self, context: &mut RenderingContext) -> Result<()> { - unsafe { - let mut device = context.lock_device()?; - self.pipeline.deactivate(&mut device); - - for fb in self.framebuffers.dissolve() { - device.destroy_framebuffer(fb); - } - } - - Ok(()) - } -} diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs deleted file mode 100644 index 429a3c2..0000000 --- a/stockton-render/src/window.rs +++ /dev/null @@ -1,278 +0,0 @@ -use stockton_input::{Action as KBAction, InputManager, Mouse}; -use stockton_skeleton::{ - draw_passes::{DrawPass, Singular}, - Renderer, -}; - -use std::sync::{ - mpsc::{channel, Receiver, Sender}, - Arc, RwLock, -}; - -use egui::{CtxRef, Event, Modifiers, Output, Pos2, RawInput, Rect, Vec2}; -use epaint::ClippedShape; -use legion::systems::Runnable; -use log::debug; -use winit::{ - event::{ElementState, Event as WinitEvent, MouseButton, WindowEvent as WinitWindowEvent}, - event_loop::ControlFlow, -}; - -#[derive(Debug, Clone, Copy)] -pub enum WindowEvent { - SizeChanged(u32, u32), - CloseRequested, - KeyboardAction(KBAction), - MouseAction(KBAction), - MouseMoved(f32, f32), - MouseLeft, -} - -impl WindowEvent { - pub fn from(winit_event: &WinitEvent<()>) -> Option<WindowEvent> { - // TODO - match winit_event { - WinitEvent::WindowEvent { event, .. } => match event { - WinitWindowEvent::CloseRequested => Some(WindowEvent::CloseRequested), - WinitWindowEvent::Resized(size) => { - Some(WindowEvent::SizeChanged(size.width, size.height)) - } - WinitWindowEvent::KeyboardInput { input, .. } => match input.state { - ElementState::Pressed => Some(WindowEvent::KeyboardAction(KBAction::KeyPress( - input.scancode, - ))), - ElementState::Released => Some(WindowEvent::KeyboardAction( - KBAction::KeyRelease(input.scancode), - )), - }, - WinitWindowEvent::CursorMoved { position, .. } => Some(WindowEvent::MouseMoved( - position.x as f32, - position.y as f32, - )), - WinitWindowEvent::CursorLeft { .. } => Some(WindowEvent::MouseLeft), - WinitWindowEvent::MouseInput { button, state, .. } => { - let mb: stockton_input::MouseButton = match button { - MouseButton::Left => stockton_input::MouseButton::Left, - MouseButton::Right => stockton_input::MouseButton::Right, - MouseButton::Middle => stockton_input::MouseButton::Middle, - MouseButton::Other(x) => stockton_input::MouseButton::Other(*x), - }; - - match state { - ElementState::Pressed => { - Some(WindowEvent::MouseAction(KBAction::MousePress(mb))) - } - ElementState::Released => { - Some(WindowEvent::MouseAction(KBAction::MouseRelease(mb))) - } - } - } - _ => None, - }, - _ => None, - } - } -} - -pub struct UiState { - ctx: CtxRef, - raw_input: RawInput, - frame_active: bool, - - modifiers: Modifiers, - pointer_pos: Pos2, -} - -impl UiState { - pub fn populate_initial_state<T: DrawPass<Singular>>(&mut self, renderer: &Renderer<T>) { - let props = renderer.context().properties(); - self.set_dimensions(props.extent.width, props.extent.height); - self.set_pixels_per_point(Some(renderer.context().pixels_per_point())); - debug!("{:?}", self.raw_input); - } - - #[inline] - pub fn ctx(&mut self) -> &CtxRef { - if !self.frame_active { - self.begin_frame() - } - &self.ctx - } - - #[inline] - fn begin_frame(&mut self) { - #[allow(deprecated)] - let new_raw_input = RawInput { - scroll_delta: Vec2::new(0.0, 0.0), - zoom_delta: 0.0, - screen_size: self.raw_input.screen_size, - screen_rect: self.raw_input.screen_rect, - pixels_per_point: self.raw_input.pixels_per_point, - time: self.raw_input.time, - predicted_dt: self.raw_input.predicted_dt, - modifiers: self.modifiers, - events: Vec::new(), - }; - self.ctx.begin_frame(self.raw_input.take()); - self.raw_input = new_raw_input; - self.frame_active = true; - } - - #[inline] - pub(crate) fn end_frame(&mut self) -> (Output, Vec<ClippedShape>) { - self.frame_active = false; - self.ctx.end_frame() - } - - #[inline] - pub fn dimensions(&self) -> Option<egui::math::Vec2> { - Some(self.raw_input.screen_rect?.size()) - } - - fn set_mouse_pos(&mut self, x: f32, y: f32) { - self.raw_input - .events - .push(Event::PointerMoved(Pos2::new(x, y))); - - self.pointer_pos = Pos2::new(x, y); - } - - fn set_mouse_left(&mut self) { - self.raw_input.events.push(Event::PointerGone); - } - - fn set_dimensions(&mut self, w: u32, h: u32) { - self.raw_input.screen_rect = - Some(Rect::from_x_y_ranges(0.0..=(w as f32), 0.0..=(h as f32))); - } - fn set_pixels_per_point(&mut self, ppp: Option<f32>) { - debug!("Using {:?} pixels per point", ppp); - self.raw_input.pixels_per_point = ppp; - } - - fn handle_action(&mut self, action: KBAction) { - // TODO - match action { - KBAction::MousePress(btn) => { - self.raw_input.events.push(Event::PointerButton { - pos: self.pointer_pos, - button: match btn { - stockton_input::MouseButton::Left => egui::PointerButton::Primary, - stockton_input::MouseButton::Right => egui::PointerButton::Secondary, - stockton_input::MouseButton::Middle => egui::PointerButton::Middle, - stockton_input::MouseButton::Other(_) => todo!(), - }, - pressed: true, - modifiers: self.modifiers, - }); - } - KBAction::MouseRelease(btn) => { - self.raw_input.events.push(Event::PointerButton { - pos: self.pointer_pos, - button: match btn { - stockton_input::MouseButton::Left => egui::PointerButton::Primary, - stockton_input::MouseButton::Right => egui::PointerButton::Secondary, - stockton_input::MouseButton::Middle => egui::PointerButton::Middle, - stockton_input::MouseButton::Other(_) => todo!(), - }, - pressed: false, - modifiers: self.modifiers, - }); - } - _ => (), - } - } -} - -impl Default for UiState { - fn default() -> Self { - UiState { - ctx: CtxRef::default(), - raw_input: RawInput::default(), - frame_active: false, - modifiers: Default::default(), - pointer_pos: Pos2::new(0.0, 0.0), - } - } -} - -pub struct WindowFlow { - window_events: Receiver<WindowEvent>, - update_control_flow: Arc<RwLock<ControlFlow>>, -} - -impl WindowFlow { - pub fn new(update_control_flow: Arc<RwLock<ControlFlow>>) -> (Self, Sender<WindowEvent>) { - let (tx, rx) = channel(); - ( - Self { - window_events: rx, - update_control_flow, - }, - tx, - ) - } -} - -#[system] -/// A system to process the window events sent to renderer by the winit event loop. -pub fn _process_window_events<T: 'static + InputManager>( - #[resource] window_channel: &mut WindowFlow, - #[resource] manager: &mut T, - #[resource] mouse: &mut Mouse, - #[resource] ui_state: &mut UiState, - #[state] actions_buf: &mut Vec<KBAction>, -) { - let mut actions_buf_cursor = 0; - let mut mouse_delta = mouse.abs; - - while let Ok(event) = window_channel.window_events.try_recv() { - match event { - WindowEvent::SizeChanged(w, h) => { - ui_state.set_dimensions(w, h); - } - WindowEvent::CloseRequested => { - let mut flow = window_channel.update_control_flow.write().unwrap(); - // TODO: Let everything know this is our last frame - *flow = ControlFlow::Exit; - } - WindowEvent::KeyboardAction(action) => { - if actions_buf_cursor >= actions_buf.len() { - actions_buf.push(action); - } else { - actions_buf[actions_buf_cursor] = action; - } - actions_buf_cursor += 1; - - ui_state.handle_action(action); - } - WindowEvent::MouseMoved(x, y) => { - mouse_delta.x = x; - mouse_delta.y = y; - - ui_state.set_mouse_pos(x, y); - } - WindowEvent::MouseLeft => { - ui_state.set_mouse_left(); - } - WindowEvent::MouseAction(action) => { - if actions_buf_cursor >= actions_buf.len() { - actions_buf.push(action); - } else { - actions_buf[actions_buf_cursor] = action; - } - actions_buf_cursor += 1; - - ui_state.handle_action(action); - } - }; - } - - mouse.handle_frame(mouse_delta); - - manager.handle_frame(&actions_buf[0..actions_buf_cursor]); -} - -pub fn process_window_events_system<T: 'static + InputManager>() -> impl Runnable { - _process_window_events_system::<T>(Vec::with_capacity(4)) -} diff --git a/stockton-skeleton/src/builders/pipeline.rs b/stockton-skeleton/src/builders/pipeline.rs index 82673a2..af8d430 100644 --- a/stockton-skeleton/src/builders/pipeline.rs +++ b/stockton-skeleton/src/builders/pipeline.rs @@ -97,6 +97,7 @@ pub struct PipelineSpec { #[builder(setter(strip_option), default)] shader_tesselation: Option<(ShaderDesc, ShaderDesc)>, + #[builder(default = "vec![]")] push_constants: Vec<(ShaderStageFlags, Range<u32>)>, #[builder(default = "false")] diff --git a/stockton-skeleton/src/builders/shader.rs b/stockton-skeleton/src/builders/shader.rs index fde185d..be48951 100644 --- a/stockton-skeleton/src/builders/shader.rs +++ b/stockton-skeleton/src/builders/shader.rs @@ -1,8 +1,10 @@ use crate::types::*; +pub use shaderc::ShaderKind; + use anyhow::{Context, Result}; use hal::pso::Specialization; -use shaderc::{Compiler, ShaderKind}; +use shaderc::Compiler; #[derive(Debug, Clone)] pub struct ShaderDesc { diff --git a/stockton-skeleton/src/context.rs b/stockton-skeleton/src/context.rs index 943e0dc..6838fb6 100644 --- a/stockton-skeleton/src/context.rs +++ b/stockton-skeleton/src/context.rs @@ -34,8 +34,8 @@ use crate::{ error::{EnvironmentError, LockPoisoned, UsageError}, mem::MemoryPool, queue_negotiator::{QueueFamilyNegotiator, QueueFamilySelector, SharedQueue}, + session::Session, types::*, - session::Session }; /// The actual data behind [`StatefulRenderingContext`] diff --git a/stockton-skeleton/src/draw_passes/cons.rs b/stockton-skeleton/src/draw_passes/cons.rs index f6c3d1b..9fb5d5d 100644 --- a/stockton-skeleton/src/draw_passes/cons.rs +++ b/stockton-skeleton/src/draw_passes/cons.rs @@ -2,7 +2,9 @@ //! Note that this can be extended to an arbitrary amount of draw passes. use super::{Beginning, DrawPass, End, IntoDrawPass, Middle, Singular}; -use crate::{session::Session, context::RenderingContext, queue_negotiator::QueueFamilyNegotiator, types::*}; +use crate::{ + context::RenderingContext, queue_negotiator::QueueFamilyNegotiator, session::Session, types::*, +}; use anyhow::Result; diff --git a/stockton-skeleton/src/draw_passes/mod.rs b/stockton-skeleton/src/draw_passes/mod.rs index 9a13c73..17564fa 100644 --- a/stockton-skeleton/src/draw_passes/mod.rs +++ b/stockton-skeleton/src/draw_passes/mod.rs @@ -1,7 +1,9 @@ //! Traits and common draw passes. use std::ops::Range; -use crate::{context::RenderingContext, queue_negotiator::QueueFamilyNegotiator, types::*, session::Session}; +use crate::{ + context::RenderingContext, queue_negotiator::QueueFamilyNegotiator, session::Session, types::*, +}; use hal::{ image::Layout, pass::{AttachmentLoadOp, AttachmentOps, AttachmentStoreOp}, diff --git a/stockton-skeleton/src/lib.rs b/stockton-skeleton/src/lib.rs index d57c5f6..1e0d108 100644 --- a/stockton-skeleton/src/lib.rs +++ b/stockton-skeleton/src/lib.rs @@ -8,26 +8,26 @@ extern crate derive_builder; pub mod buffers; pub mod builders; +pub mod components; pub mod context; pub mod draw_passes; pub mod error; pub mod mem; pub mod queue_negotiator; +pub mod session; mod target; pub mod texture; pub mod types; pub mod utils; -pub mod components; -pub mod session; +pub use anyhow::Result; pub use context::RenderingContext; -pub use session::Session; pub use draw_passes::{DrawPass, IntoDrawPass, PassPosition}; -pub use anyhow::Result; +pub use session::Session; +use anyhow::Context; use draw_passes::Singular; use std::mem::ManuallyDrop; -use anyhow::{Context}; use winit::window::Window; /// Renders a world to a window when you tell it to. @@ -75,6 +75,7 @@ impl<DP: DrawPass<Singular>> Renderer<DP> { } Err((_e, c)) => { // TODO: Try to detect if the error is actually surface related. + let c = c.attempt_recovery()?; match c.draw_next_frame(session, &mut *self.draw_pass) { Ok(c) => { @@ -94,9 +95,11 @@ impl<DP: DrawPass<Singular>> Renderer<DP> { // Safety: If this fails at any point, the ManuallyDrop won't be touched again, as Renderer will be dropped. // Hence, we can always take from the ManuallyDrop unsafe { - let ctx = ManuallyDrop::take(&mut self.context).recreate_surface()?; + let ctx = ManuallyDrop::take(&mut self.context); + log::debug!("ctx"); + let ctx = ctx.recreate_surface()?; self.context = ManuallyDrop::new(ctx); - + log::debug!("Finished resizing ctx"); let dp = ManuallyDrop::take(&mut self.draw_pass) .handle_surface_change(session, &mut self.context)?; self.draw_pass = ManuallyDrop::new(dp); diff --git a/stockton-skeleton/src/target.rs b/stockton-skeleton/src/target.rs index 23f56b9..68829b1 100644 --- a/stockton-skeleton/src/target.rs +++ b/stockton-skeleton/src/target.rs @@ -4,8 +4,8 @@ use crate::{ context::ContextProperties, draw_passes::{DrawPass, Singular}, + session::Session, types::*, - session::Session }; use std::{ diff --git a/stockton-skeleton/src/texture/load.rs b/stockton-skeleton/src/texture/load.rs index 80c332e..27498af 100644 --- a/stockton-skeleton/src/texture/load.rs +++ b/stockton-skeleton/src/texture/load.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, RwLock}; -use super::{block::TexturesBlock, repo::BLOCK_SIZE, TextureResolver, LoadableImage}; +use super::{block::TexturesBlock, repo::BLOCK_SIZE, LoadableImage, TextureResolver}; use crate::{ buffers::{ image::{ImageSpec, SampledImage, COLOR_RESOURCES}, diff --git a/stockton-skeleton/src/texture/loader.rs b/stockton-skeleton/src/texture/loader.rs index 6212492..0a98722 100644 --- a/stockton-skeleton/src/texture/loader.rs +++ b/stockton-skeleton/src/texture/loader.rs @@ -6,8 +6,7 @@ use super::{ load_image, QueuedLoad, TextureLoadConfig, TextureLoadError, FORMAT, LAYERS, RESOURCES, }, repo::BLOCK_SIZE, - TextureResolver, - PIXEL_SIZE, + TextureResolver, PIXEL_SIZE, }; use crate::{ buffers::image::SampledImage, |