diff options
53 files changed, 2812 insertions, 2527 deletions
@@ -60,4 +60,6 @@ textures/ # Intermediate BSP files. *.srf -*.prt
\ No newline at end of file +*.prt + +.idea diff --git a/examples/render-bsp/src/main.rs b/examples/render-bsp/src/main.rs index aeaf4e6..d4e5ba3 100644 --- a/examples/render-bsp/src/main.rs +++ b/examples/render-bsp/src/main.rs @@ -15,29 +15,25 @@ //! Renders ./example.bsp -extern crate stockton_types; +extern crate log; +extern crate simple_logger; extern crate stockton_levels; extern crate stockton_render; +extern crate stockton_types; extern crate winit; -extern crate log; -extern crate simple_logger; -use std::time::SystemTime; use std::f32::consts::PI; +use std::time::SystemTime; use winit::{ - event::{Event, WindowEvent, ElementState}, + event::{ElementState, Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder + window::WindowBuilder, }; -use stockton_levels::{ - prelude::*, - q3::Q3BSPFile -}; -use stockton_types::{World, Vector3, Vector2}; +use stockton_levels::{prelude::*, q3::Q3BSPFile}; use stockton_render::Renderer; - +use stockton_types::{Vector2, Vector3, World}; /// Movement speed, world units per second const SPEED: f32 = 100.0; @@ -46,168 +42,161 @@ const SPEED: f32 = 100.0; const PIXELS_PER_90D: f32 = 100.0; /// Sensitivity, derived from above -const SENSITIVITY: f32 = PI / (2.0 * PIXELS_PER_90D); +const SENSITIVITY: f32 = PI / (2.0 * PIXELS_PER_90D); #[derive(Debug)] struct KeyState { - pub up: bool, - pub left: bool, - pub right: bool, - pub down: bool, - pub inwards: bool, - pub out: bool + pub up: bool, + pub left: bool, + pub right: bool, + pub down: bool, + pub inwards: bool, + pub out: bool, } impl KeyState { - pub fn new() -> KeyState { - KeyState { - up: false, - left: false, - right: false, - down: false, - inwards: false, - out: false, - } - } - - /// Helper function to get our movement request as a normalized vector - pub fn as_vector(&self) -> Vector3 { - let mut vec = Vector3::new(0.0, 0.0, 0.0); - - if self.up { - vec.y = 1.0; - } else if self.down { - vec.y = -1.0; - } - - if self.right { - vec.x = 1.0; - } else if self.left { - vec.x = -1.0; - } - - if self.inwards { - vec.z = 1.0; - } else if self.out { - vec.z = -1.0; - } - - vec - } + pub fn new() -> KeyState { + KeyState { + up: false, + left: false, + right: false, + down: false, + inwards: false, + out: false, + } + } + + /// Helper function to get our movement request as a normalized vector + pub fn as_vector(&self) -> Vector3 { + let mut vec = Vector3::new(0.0, 0.0, 0.0); + + if self.up { + vec.y = 1.0; + } else if self.down { + vec.y = -1.0; + } + + if self.right { + vec.x = 1.0; + } else if self.left { + vec.x = -1.0; + } + + if self.inwards { + vec.z = 1.0; + } else if self.out { + vec.z = -1.0; + } + + vec + } } fn main() { - // Initialise logger - simple_logger::init_with_level(log::Level::Debug).unwrap(); - - // Make a window - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - - if let Err(_) = window.set_cursor_grab(true) { - println!("warning: cursor not grabbed"); - } - window.set_cursor_visible(false); - - // Parse the map file and swizzle the co-ords - let data = include_bytes!("../data/newtest.bsp").to_vec().into_boxed_slice(); - let bsp: Result<Q3BSPFile<Q3System>, stockton_levels::types::ParseError> = Q3BSPFile::parse_file(&data); - let bsp: Q3BSPFile<Q3System> = bsp.unwrap(); - let bsp: Q3BSPFile<VulkanSystem> = bsp.swizzle_to(); - - // Load into a world and create the new renderer - let world = World::new(bsp); - let mut renderer = Renderer::new(world, &window).unwrap(); - - // Done loading - This is our main loop. - - let mut last_update = SystemTime::now(); - let mut key_state = KeyState::new(); - let mut last_cursor_pos = Vector2::new(0.0, 0.0); - - // Keep rendering the world - event_loop.run(move |event, _, flow| { - *flow = ControlFlow::Poll; - match event { - Event::WindowEvent { - event, - .. - } => match event { - // Close when requested - WindowEvent::CloseRequested => { - *flow = ControlFlow::Exit - }, - - WindowEvent::Resized(_) => { - unsafe { renderer.context.handle_surface_change().unwrap() }; - } - - // Update our keystates - WindowEvent::KeyboardInput {input, ..} => match input.scancode { - // A - 30 => key_state.left = input.state == ElementState::Pressed, - // D - 32 => key_state.right = input.state == ElementState::Pressed, - // W (in) - 17 => key_state.inwards = input.state == ElementState::Pressed, - // S (out) - 31 => key_state.out = input.state == ElementState::Pressed, - // Space (up) - 57 => key_state.up = input.state == ElementState::Pressed, - // Ctrl (down) - 42 => key_state.down = input.state == ElementState::Pressed, - _ => () - }, - - // Look around with mouse - WindowEvent::CursorMoved { - position, - .. - } => { - // Don't do anything on the first frame - if last_cursor_pos.x != 0.0 || last_cursor_pos.y == 0.0 { - // Figure out how much to rotate by - let x_offset = (position.x as f32 - last_cursor_pos.x) * SENSITIVITY; - let y_offset = (position.y as f32 - last_cursor_pos.y) * SENSITIVITY; - - // Rotate - renderer.context.rotate(Vector3::new( - -y_offset, - x_offset, - 0.0 - )); - } - - // For tracking how much the mouse has moved - last_cursor_pos.x = position.x as f32; - last_cursor_pos.y = position.y as f32; - } - _ => () - }, - - // Nothing new happened - Event::MainEventsCleared => { - // Draw as many frames as we can - // This isn't ideal, but it'll do for now. - window.request_redraw() - }, - - // Redraw - either from above or resizing, etc - Event::RedrawRequested(_) => { - // Time since last frame drawn. Again, not ideal. - let delta = last_update.elapsed().unwrap().as_secs_f32(); - last_update = SystemTime::now(); - - // Move our camera if needed - let delta_pos = key_state.as_vector() * delta * SPEED; - if delta_pos.x != 0.0 || delta_pos.y != 0.0 || delta_pos.z != 0.0 { - renderer.context.move_camera_relative(delta_pos); - } - - // Render the frame - renderer.render_frame().unwrap() - } - _ => () - } - }); + // Initialise logger + simple_logger::init_with_level(log::Level::Debug).unwrap(); + + // Make a window + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + if window.set_cursor_grab(true).is_err() { + println!("warning: cursor not grabbed"); + } + window.set_cursor_visible(false); + + // Parse the map file and swizzle the co-ords + let data = include_bytes!("../data/newtest.bsp") + .to_vec() + .into_boxed_slice(); + let bsp: Result<Q3BSPFile<Q3System>, stockton_levels::types::ParseError> = + Q3BSPFile::parse_file(&data); + let bsp: Q3BSPFile<Q3System> = bsp.unwrap(); + let bsp: Q3BSPFile<VulkanSystem> = bsp.swizzle_to(); + + // Load into a world and create the new renderer + let world = World::new(bsp); + let mut renderer = Renderer::new(world, &window).unwrap(); + + // Done loading - This is our main loop. + + let mut last_update = SystemTime::now(); + let mut key_state = KeyState::new(); + let mut last_cursor_pos = Vector2::new(0.0, 0.0); + + // Keep rendering the world + event_loop.run(move |event, _, flow| { + *flow = ControlFlow::Poll; + match event { + Event::WindowEvent { event, .. } => match event { + // Close when requested + WindowEvent::CloseRequested => *flow = ControlFlow::Exit, + + WindowEvent::Resized(_) => { + unsafe { renderer.context.handle_surface_change().unwrap() }; + } + + // Update our keystates + WindowEvent::KeyboardInput { input, .. } => match input.scancode { + // A + 30 => key_state.left = input.state == ElementState::Pressed, + // D + 32 => key_state.right = input.state == ElementState::Pressed, + // W (in) + 17 => key_state.inwards = input.state == ElementState::Pressed, + // S (out) + 31 => key_state.out = input.state == ElementState::Pressed, + // Space (up) + 57 => key_state.up = input.state == ElementState::Pressed, + // Ctrl (down) + 42 => key_state.down = input.state == ElementState::Pressed, + _ => (), + }, + + // Look around with mouse + WindowEvent::CursorMoved { position, .. } => { + // Don't do anything on the first frame + if last_cursor_pos.x != 0.0 || last_cursor_pos.y == 0.0 { + // Figure out how much to rotate by + let x_offset = (position.x as f32 - last_cursor_pos.x) * SENSITIVITY; + let y_offset = (position.y as f32 - last_cursor_pos.y) * SENSITIVITY; + + // Rotate + renderer + .context + .rotate(Vector3::new(-y_offset, x_offset, 0.0)); + } + + // For tracking how much the mouse has moved + last_cursor_pos.x = position.x as f32; + last_cursor_pos.y = position.y as f32; + } + _ => (), + }, + + // Nothing new happened + Event::MainEventsCleared => { + // Draw as many frames as we can + // This isn't ideal, but it'll do for now. + window.request_redraw() + } + + // Redraw - either from above or resizing, etc + Event::RedrawRequested(_) => { + // Time since last frame drawn. Again, not ideal. + let delta = last_update.elapsed().unwrap().as_secs_f32(); + last_update = SystemTime::now(); + + // Move our camera if needed + let delta_pos = key_state.as_vector() * delta * SPEED; + if delta_pos.x != 0.0 || delta_pos.y != 0.0 || delta_pos.z != 0.0 { + renderer.context.move_camera_relative(delta_pos); + } + + // Render the frame + renderer.render_frame().unwrap() + } + _ => (), + } + }); } diff --git a/stockton-levels/src/coords.rs b/stockton-levels/src/coords.rs index 613c932..bddf1f2 100644 --- a/stockton-levels/src/coords.rs +++ b/stockton-levels/src/coords.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -14,8 +14,8 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Marker traits for different co-ordinate systems, and functions to swizzle between them -use na::Vector3; use na::base::Scalar; +use na::Vector3; use std::ops::Neg; pub trait CoordSystem {} @@ -28,17 +28,16 @@ impl CoordSystem for Q3System {} pub struct VulkanSystem; impl CoordSystem for VulkanSystem {} - pub struct Swizzler; pub trait SwizzleFromTo<F: CoordSystem, T: CoordSystem> { - fn swizzle<U: Scalar + Copy + Neg<Output = U>>(vec: &mut Vector3<U>) -> (); + fn swizzle<U: Scalar + Copy + Neg<Output = U>>(vec: &mut Vector3<U>); } impl SwizzleFromTo<Q3System, VulkanSystem> for Swizzler { - fn swizzle<U: Scalar + Copy + Neg<Output = U>>(vec: &mut Vector3<U>) -> () { - let temp = vec.y; - vec.y = vec.z; - vec.z = -temp; - } -}
\ No newline at end of file + fn swizzle<U: Scalar + Copy + Neg<Output = U>>(vec: &mut Vector3<U>) { + let temp = vec.y; + vec.y = vec.z; + vec.z = -temp; + } +} diff --git a/stockton-levels/src/features.rs b/stockton-levels/src/features.rs index 6cecbff..7b81cc8 100644 --- a/stockton-levels/src/features.rs +++ b/stockton-levels/src/features.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -14,9 +14,8 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Marker traits for different feature sets -use crate::traits::*; use crate::coords::CoordSystem; +use crate::traits::*; pub trait MinBSPFeatures<S: CoordSystem>: HasBSPTree<S> {} -impl<T, S: CoordSystem> MinBSPFeatures<S> for T - where T: HasBSPTree<S> {}
\ No newline at end of file +impl<T, S: CoordSystem> MinBSPFeatures<S> for T where T: HasBSPTree<S> {} diff --git a/stockton-levels/src/helpers.rs b/stockton-levels/src/helpers.rs index c3e6c80..91a672a 100644 --- a/stockton-levels/src/helpers.rs +++ b/stockton-levels/src/helpers.rs @@ -68,4 +68,4 @@ pub fn slice_to_vec3i(slice: &[u8]) -> Vector3<i32> { /// If slice isn't 8 bytes long. pub fn slice_to_vec2ui(slice: &[u8]) -> Vector2<u32> { Vector2::new(slice_to_u32(&slice[0..4]), slice_to_u32(&slice[4..8])) -}
\ No newline at end of file +} diff --git a/stockton-levels/src/lib.rs b/stockton-levels/src/lib.rs index 3026b9d..46484bd 100644 --- a/stockton-levels/src/lib.rs +++ b/stockton-levels/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License along // with this program. If not, see <http://www.gnu.org/licenses/>. //! Parses common features from many BSP file formats. +#![allow(incomplete_features)] #![feature(generic_associated_types)] #[macro_use] @@ -20,10 +21,10 @@ extern crate bitflags; extern crate bitvec; extern crate nalgebra as na; +pub mod coords; +pub mod features; mod helpers; +pub mod prelude; pub mod q3; -pub mod types; pub mod traits; -pub mod prelude; -pub mod features; -pub mod coords;
\ No newline at end of file +pub mod types; diff --git a/stockton-levels/src/prelude.rs b/stockton-levels/src/prelude.rs index 32485b3..ec6aa1f 100644 --- a/stockton-levels/src/prelude.rs +++ b/stockton-levels/src/prelude.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -14,6 +14,6 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Common traits, etc. -pub use crate::traits::*; +pub use crate::coords::*; pub use crate::features::*; -pub use crate::coords::*;
\ No newline at end of file +pub use crate::traits::*; diff --git a/stockton-levels/src/q3/brushes.rs b/stockton-levels/src/q3/brushes.rs index f82e57a..cd848a2 100644 --- a/stockton-levels/src/q3/brushes.rs +++ b/stockton-levels/src/q3/brushes.rs @@ -23,18 +23,18 @@ const BRUSH_SIZE: usize = 4 * 3; /// The size of one brushsize record const SIDE_SIZE: usize = 4 * 2; +use super::Q3BSPFile; +use crate::coords::CoordSystem; use crate::helpers::slice_to_i32; -use crate::types::{ParseError, Result}; use crate::traits::brushes::*; -use crate::coords::CoordSystem; -use super::Q3BSPFile; +use crate::types::{ParseError, Result}; /// Parse the brushes & brushsides lump from a bsp file. pub fn from_data( brushes_data: &[u8], sides_data: &[u8], n_textures: u32, - n_planes: u32 + n_planes: u32, ) -> Result<Box<[Brush]>> { if brushes_data.len() % BRUSH_SIZE != 0 || sides_data.len() % SIDE_SIZE != 0 { return Err(ParseError::Invalid); @@ -57,9 +57,9 @@ pub fn from_data( slice_to_i32(&brush[0..4]), slice_to_i32(&brush[4..8]), n_textures as usize, - n_planes as usize + n_planes as usize, )?, - texture_idx + texture_idx, }); } @@ -96,7 +96,7 @@ fn get_sides( sides.push(BrushSide { plane_idx, texture_idx, - is_opposing + is_opposing, }); } } @@ -104,15 +104,14 @@ fn get_sides( Ok(sides.into_boxed_slice()) } - impl<T: CoordSystem> HasBrushes<T> for Q3BSPFile<T> { type BrushesIter<'a> = std::slice::Iter<'a, Brush>; - fn brushes_iter<'a>(&'a self) -> Self::BrushesIter<'a> { + fn brushes_iter(&self) -> Self::BrushesIter<'_> { self.brushes.iter() } - fn get_brush<'a>(&'a self, index: u32) -> &'a Brush { + fn get_brush(&self, index: u32) -> &Brush { &self.brushes[index as usize] } } diff --git a/stockton-levels/src/q3/effects.rs b/stockton-levels/src/q3/effects.rs index 77ad1ed..a796570 100644 --- a/stockton-levels/src/q3/effects.rs +++ b/stockton-levels/src/q3/effects.rs @@ -17,11 +17,11 @@ use std::str; -use crate::helpers::slice_to_u32; -use crate::types::{Result, ParseError}; -use crate::traits::effects::*; use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::helpers::slice_to_u32; +use crate::traits::effects::*; +use crate::types::{ParseError, Result}; /// The size of one effect definition const EFFECT_SIZE: usize = 64 + 4 + 4; @@ -42,23 +42,24 @@ pub fn from_data(data: &[u8], n_brushes: u32) -> Result<Box<[Effect]>> { } effects.push(Effect { - name: str::from_utf8(&raw[..64]).map_err(|_| ParseError::Invalid)?.to_owned(), - brush_idx + name: str::from_utf8(&raw[..64]) + .map_err(|_| ParseError::Invalid)? + .to_owned(), + brush_idx, }); } Ok(effects.into_boxed_slice()) } - impl<T: CoordSystem> HasEffects<T> for Q3BSPFile<T> { type EffectsIter<'a> = std::slice::Iter<'a, Effect>; - fn effects_iter<'a>(&'a self) -> Self::EffectsIter<'a> { + fn effects_iter(&self) -> Self::EffectsIter<'_> { self.effects.iter() } - fn get_effect<'a>(&'a self, index: u32) -> &'a Effect { + fn get_effect(&self, index: u32) -> &Effect { &self.effects[index as usize] } } diff --git a/stockton-levels/src/q3/entities.rs b/stockton-levels/src/q3/entities.rs index 4382ef2..950d89a 100644 --- a/stockton-levels/src/q3/entities.rs +++ b/stockton-levels/src/q3/entities.rs @@ -15,13 +15,13 @@ // You should have received a copy of the GNU General Public License // along with stockton-bsp. If not, see <http://www.gnu.org/licenses/>. -use std::str; use std::collections::HashMap; +use std::str; -use crate::types::{Result, ParseError}; -use crate::traits::entities::*; use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::traits::entities::*; +use crate::types::{ParseError, Result}; const QUOTE: u8 = b'"'; const END_BRACKET: u8 = b'}'; @@ -72,7 +72,10 @@ pub fn from_data(data: &[u8]) -> Result<Box<[Entity]>> { state = ParseState::InsideEntity; val_end = i; - attrs.insert(string[key_start..key_end].to_owned(), string[val_start..val_end].to_owned()); + attrs.insert( + string[key_start..key_end].to_owned(), + string[val_start..val_end].to_owned(), + ); } _ => { return Err(ParseError::Invalid); @@ -103,7 +106,7 @@ pub fn from_data(data: &[u8]) -> Result<Box<[Entity]>> { impl<T: CoordSystem> HasEntities for Q3BSPFile<T> { type EntitiesIter<'a> = std::slice::Iter<'a, Entity>; - fn entities_iter<'a>(&'a self) -> Self::EntitiesIter<'a> { + fn entities_iter(&self) -> Self::EntitiesIter<'_> { self.entities.iter() } } diff --git a/stockton-levels/src/q3/faces.rs b/stockton-levels/src/q3/faces.rs index a53af6e..ab768ce 100644 --- a/stockton-levels/src/q3/faces.rs +++ b/stockton-levels/src/q3/faces.rs @@ -15,16 +15,15 @@ // You should have received a copy of the GNU General Public License // along with stockton-bsp. If not, see <http://www.gnu.org/licenses/>. -use crate::helpers::{slice_to_i32, slice_to_u32, slice_to_vec2ui, slice_to_vec3}; -use crate::types::{Result, ParseError}; -use na::Vector3; -use crate::traits::faces::*; use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::helpers::{slice_to_i32, slice_to_u32, slice_to_vec2ui, slice_to_vec3}; +use crate::traits::faces::*; +use crate::types::{ParseError, Result}; +use na::Vector3; const FACE_SIZE: usize = (4 * 8) + (4 * 2) + (4 * 2) + (4 * 3) + ((4 * 2) * 3) + (4 * 3) + (4 * 2); - pub fn from_data( data: &[u8], n_textures: u32, @@ -53,7 +52,6 @@ pub fn from_data( Ok(faces.into_boxed_slice()) } - fn face_from_slice( data: &[u8], n_textures: u32, @@ -125,9 +123,9 @@ fn face_from_slice( // map_vecs let mut map_vecs = [Vector3::new(0.0, 0.0, 0.0); 2]; - for n in 0..2 { + for (n, map_vec) in map_vecs.iter_mut().enumerate() { let offset = 60 + (n * 3 * 4); - map_vecs[n] = slice_to_vec3(&data[offset..offset + 12]); + *map_vec = slice_to_vec3(&data[offset..offset + 12]); } // normal & size @@ -150,11 +148,10 @@ fn face_from_slice( }) } - impl<T: CoordSystem> HasFaces<T> for Q3BSPFile<T> { type FacesIter<'a> = std::slice::Iter<'a, Face>; - - fn faces_iter<'a>(&'a self) -> Self::FacesIter<'a> { + + fn faces_iter(&self) -> Self::FacesIter<'_> { self.faces.iter() } @@ -162,8 +159,7 @@ impl<T: CoordSystem> HasFaces<T> for Q3BSPFile<T> { self.faces.len() as u32 } - fn get_face<'a>(&'a self, index: u32) -> &'a Face { + fn get_face(&self, index: u32) -> &Face { &self.faces[index as usize] } - } diff --git a/stockton-levels/src/q3/file.rs b/stockton-levels/src/q3/file.rs index ecce783..7906823 100644 --- a/stockton-levels/src/q3/file.rs +++ b/stockton-levels/src/q3/file.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -21,126 +21,142 @@ use bitvec::prelude::*; use std::marker::PhantomData; use self::header::Header; -use crate::types::Result; use crate::coords::*; +use crate::types::Result; use super::*; -use crate::traits::textures::Texture; -use crate::traits::entities::Entity; -use crate::traits::planes::Plane; -use crate::traits::vertices::{Vertex, MeshVert}; -use crate::traits::light_maps::LightMap; -use crate::traits::light_vols::LightVol; use crate::traits::brushes::Brush; use crate::traits::effects::Effect; +use crate::traits::entities::Entity; use crate::traits::faces::Face; -use crate::traits::tree::BSPNode; +use crate::traits::light_maps::LightMap; +use crate::traits::light_vols::LightVol; use crate::traits::models::Model; +use crate::traits::planes::Plane; +use crate::traits::textures::Texture; +use crate::traits::tree::BSPNode; +use crate::traits::vertices::{MeshVert, Vertex}; /// A parsed Quake 3 BSP File. pub struct Q3BSPFile<T: CoordSystem> { - pub(crate) visdata: Box<[BitBox<Local, u8>]>, - pub(crate) textures: Box<[Texture]>, - pub(crate) entities: Box<[Entity]>, - pub(crate) planes: Box<[Plane]>, - pub(crate) vertices: Box<[Vertex]>, - pub(crate) meshverts: Box<[MeshVert]>, - pub(crate) light_maps: Box<[LightMap]>, - pub(crate) light_vols: Box<[LightVol]>, - pub(crate) brushes: Box<[Brush]>, - pub(crate) effects: Box<[Effect]>, - pub(crate) faces: Box<[Face]>, - pub(crate) models: Box<[Model]>, - pub(crate) tree_root: BSPNode, - _phantom: PhantomData<T> + pub(crate) visdata: Box<[BitBox<Local, u8>]>, + pub(crate) textures: Box<[Texture]>, + pub(crate) entities: Box<[Entity]>, + pub(crate) planes: Box<[Plane]>, + pub(crate) vertices: Box<[Vertex]>, + pub(crate) meshverts: Box<[MeshVert]>, + pub(crate) light_maps: Box<[LightMap]>, + pub(crate) light_vols: Box<[LightVol]>, + pub(crate) brushes: Box<[Brush]>, + pub(crate) effects: Box<[Effect]>, + pub(crate) faces: Box<[Face]>, + pub(crate) models: Box<[Model]>, + pub(crate) tree_root: BSPNode, + _phantom: PhantomData<T>, } impl Q3BSPFile<Q3System> { - /// Parse `data` as a quake 3 bsp file. - pub fn parse_file(data: &[u8]) -> Result<Q3BSPFile<Q3System>> { - let header = Header::from(data)?; - - let entities = entities::from_data(header.get_lump(&data, 0))?; - let textures = textures::from_data(header.get_lump(&data, 1))?; - let planes = planes::from_data(header.get_lump(&data, 2))?; - let vertices = vertices::verts_from_data(header.get_lump(&data, 10))?; - let meshverts = vertices::meshverts_from_data(header.get_lump(&data, 11))?; - let light_maps = light_maps::from_data(header.get_lump(&data, 14))?; - let light_vols = light_vols::from_data(header.get_lump(&data, 15))?; - let visdata = visdata::from_data(header.get_lump(&data, 16))?; - let brushes = brushes::from_data( - header.get_lump(&data, 8), - header.get_lump(&data, 9), - textures.len() as u32, - planes.len() as u32 - )?; - let effects = effects::from_data(header.get_lump(&data, 12), brushes.len() as u32)?; - let faces = faces::from_data( - header.get_lump(&data, 13), - textures.len() as u32, - effects.len() as u32, - vertices.len() as u32, - meshverts.len() as u32, - light_maps.len() as u32 - )?; - - let tree_root = tree::from_data( - header.get_lump(&data, 3), - header.get_lump(&data, 4), - header.get_lump(&data, 5), - header.get_lump(&data, 6), - faces.len() as u32, - brushes.len() as u32 - )?; - - let models = models::from_data(header.get_lump(&data, 7), faces.len() as u32, brushes.len() as u32)?; - - Ok(Q3BSPFile { - visdata, textures, entities, planes, vertices, meshverts, light_maps, - light_vols, brushes, effects, faces, tree_root, models, - _phantom: PhantomData - }) - } + /// Parse `data` as a quake 3 bsp file. + pub fn parse_file(data: &[u8]) -> Result<Q3BSPFile<Q3System>> { + let header = Header::from(data)?; + + let entities = entities::from_data(header.get_lump(&data, 0))?; + let textures = textures::from_data(header.get_lump(&data, 1))?; + let planes = planes::from_data(header.get_lump(&data, 2))?; + let vertices = vertices::verts_from_data(header.get_lump(&data, 10))?; + let meshverts = vertices::meshverts_from_data(header.get_lump(&data, 11))?; + let light_maps = light_maps::from_data(header.get_lump(&data, 14))?; + let light_vols = light_vols::from_data(header.get_lump(&data, 15))?; + let visdata = visdata::from_data(header.get_lump(&data, 16))?; + let brushes = brushes::from_data( + header.get_lump(&data, 8), + header.get_lump(&data, 9), + textures.len() as u32, + planes.len() as u32, + )?; + let effects = effects::from_data(header.get_lump(&data, 12), brushes.len() as u32)?; + let faces = faces::from_data( + header.get_lump(&data, 13), + textures.len() as u32, + effects.len() as u32, + vertices.len() as u32, + meshverts.len() as u32, + light_maps.len() as u32, + )?; + + let tree_root = tree::from_data( + header.get_lump(&data, 3), + header.get_lump(&data, 4), + header.get_lump(&data, 5), + header.get_lump(&data, 6), + faces.len() as u32, + brushes.len() as u32, + )?; + + let models = models::from_data( + header.get_lump(&data, 7), + faces.len() as u32, + brushes.len() as u32, + )?; + + Ok(Q3BSPFile { + visdata, + textures, + entities, + planes, + vertices, + meshverts, + light_maps, + light_vols, + brushes, + effects, + faces, + tree_root, + models, + _phantom: PhantomData, + }) + } } impl<T: CoordSystem> Q3BSPFile<T> { - pub fn swizzle_to<D: CoordSystem>(mut self) -> Q3BSPFile<D> - where Swizzler: SwizzleFromTo<T, D> { - - for vertex in self.vertices.iter_mut() { - Swizzler::swizzle(&mut vertex.normal); - Swizzler::swizzle(&mut vertex.position); - } - - for model in self.models.iter_mut() { - Swizzler::swizzle(&mut model.mins); - Swizzler::swizzle(&mut model.maxs); - } - - for face in self.faces.iter_mut() { - Swizzler::swizzle(&mut face.normal); - } - - for plane in self.planes.iter_mut() { - Swizzler::swizzle(&mut plane.normal); - } - - // TODO: Possibly don't need to move? - Q3BSPFile { - visdata: self.visdata, - textures: self.textures, - entities: self.entities, - planes: self.planes, - vertices: self.vertices, - meshverts: self.meshverts, - light_maps: self.light_maps, - light_vols: self.light_vols, - brushes: self.brushes, - effects: self.effects, - faces: self.faces, - tree_root: self.tree_root, - models: self.models, - _phantom: PhantomData - } - } -}
\ No newline at end of file + pub fn swizzle_to<D: CoordSystem>(mut self) -> Q3BSPFile<D> + where + Swizzler: SwizzleFromTo<T, D>, + { + for vertex in self.vertices.iter_mut() { + Swizzler::swizzle(&mut vertex.normal); + Swizzler::swizzle(&mut vertex.position); + } + + for model in self.models.iter_mut() { + Swizzler::swizzle(&mut model.mins); + Swizzler::swizzle(&mut model.maxs); + } + + for face in self.faces.iter_mut() { + Swizzler::swizzle(&mut face.normal); + } + + for plane in self.planes.iter_mut() { + Swizzler::swizzle(&mut plane.normal); + } + + // TODO: Possibly don't need to move? + Q3BSPFile { + visdata: self.visdata, + textures: self.textures, + entities: self.entities, + planes: self.planes, + vertices: self.vertices, + meshverts: self.meshverts, + light_maps: self.light_maps, + light_vols: self.light_vols, + brushes: self.brushes, + effects: self.effects, + faces: self.faces, + tree_root: self.tree_root, + models: self.models, + _phantom: PhantomData, + } + } +} diff --git a/stockton-levels/src/q3/header.rs b/stockton-levels/src/q3/header.rs index d4e4f2f..46cfdac 100644 --- a/stockton-levels/src/q3/header.rs +++ b/stockton-levels/src/q3/header.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -13,8 +13,8 @@ // 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::convert::TryInto; use crate::types::{ParseError, Result}; +use std::convert::TryInto; const MAGIC_HEADER: &[u8] = &[0x49, 0x42, 0x53, 0x50]; const HEADER_LEN: usize = 4 + 4 + (17 * 4 * 2); @@ -80,4 +80,4 @@ impl Header { &buf[entry.offset as usize..entry.offset as usize + entry.length as usize] } -}
\ No newline at end of file +} diff --git a/stockton-levels/src/q3/light_maps.rs b/stockton-levels/src/q3/light_maps.rs index 8fc1c83..2d0fcc6 100644 --- a/stockton-levels/src/q3/light_maps.rs +++ b/stockton-levels/src/q3/light_maps.rs @@ -15,10 +15,10 @@ // You should have received a copy of the GNU General Public License // along with stockton-bsp. If not, see <http://www.gnu.org/licenses/>. -use crate::types::{Result, RGB, ParseError}; -use crate::traits::light_maps::*; use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::traits::light_maps::*; +use crate::types::{ParseError, Result, RGB}; /// The size of one LightMap const LIGHTMAP_SIZE: usize = 128 * 128 * 3; @@ -35,10 +35,10 @@ pub fn from_data(data: &[u8]) -> Result<Box<[LightMap]>> { let raw = &data[n * LIGHTMAP_SIZE..(n + 1) * LIGHTMAP_SIZE]; let mut map: [[RGB; 128]; 128] = [[RGB::white(); 128]; 128]; - for x in 0..128 { - for y in 0..128 { + for (x, outer) in map.iter_mut().enumerate() { + for (y, inner) in outer.iter_mut().enumerate() { let offset = (x * 128 * 3) + (y * 3); - map[x][y] = RGB::from_slice(&raw[offset..offset + 3]); + *inner = RGB::from_slice(&raw[offset..offset + 3]); } } maps.push(LightMap { map }) @@ -50,11 +50,11 @@ pub fn from_data(data: &[u8]) -> Result<Box<[LightMap]>> { impl<T: CoordSystem> HasLightMaps for Q3BSPFile<T> { type LightMapsIter<'a> = std::slice::Iter<'a, LightMap>; - fn lightmaps_iter<'a>(&'a self) -> Self::LightMapsIter<'a> { + fn lightmaps_iter(&self) -> Self::LightMapsIter<'_> { self.light_maps.iter() } - fn get_lightmap<'a>(&'a self, index: u32) -> &'a LightMap { + fn get_lightmap(&self, index: u32) -> &LightMap { &self.light_maps[index as usize] } -}
\ No newline at end of file +} diff --git a/stockton-levels/src/q3/light_vols.rs b/stockton-levels/src/q3/light_vols.rs index d5bbadf..bec3bea 100644 --- a/stockton-levels/src/q3/light_vols.rs +++ b/stockton-levels/src/q3/light_vols.rs @@ -17,10 +17,10 @@ use std::convert::TryInto; -use crate::types::{Result, ParseError, RGB}; -use crate::traits::light_vols::*; use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::traits::light_vols::*; +use crate::types::{ParseError, Result, RGB}; const VOL_LENGTH: usize = (3 * 2) + 2; @@ -43,15 +43,14 @@ pub fn from_data(data: &[u8]) -> Result<Box<[LightVol]>> { Ok(vols.into_boxed_slice()) } - impl<T: CoordSystem> HasLightVols for Q3BSPFile<T> { type LightVolsIter<'a> = std::slice::Iter<'a, LightVol>; - fn lightvols_iter<'a>(&'a self) -> Self::LightVolsIter<'a> { + fn lightvols_iter(&self) -> Self::LightVolsIter<'_> { self.light_vols.iter() } - fn get_lightvol<'a>(&'a self, index: u32) -> &'a LightVol { + fn get_lightvol(&self, index: u32) -> &LightVol { &self.light_vols[index as usize] } } diff --git a/stockton-levels/src/q3/mod.rs b/stockton-levels/src/q3/mod.rs index 72634f8..f7d3c86 100644 --- a/stockton-levels/src/q3/mod.rs +++ b/stockton-levels/src/q3/mod.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -15,19 +15,19 @@ //! Parsing data from Q3 and similar BSPs -mod visdata; -mod header; -mod textures; -mod entities; -mod planes; -mod vertices; -mod light_maps; -mod light_vols; mod brushes; mod effects; +mod entities; mod faces; -mod tree; -mod models; pub mod file; +mod header; +mod light_maps; +mod light_vols; +mod models; +mod planes; +mod textures; +mod tree; +mod vertices; +mod visdata; -pub use self::file::Q3BSPFile;
\ No newline at end of file +pub use self::file::Q3BSPFile; diff --git a/stockton-levels/src/q3/models.rs b/stockton-levels/src/q3/models.rs index 75f42fa..b197521 100644 --- a/stockton-levels/src/q3/models.rs +++ b/stockton-levels/src/q3/models.rs @@ -15,19 +15,15 @@ // You should have received a copy of the GNU General Public License // along with stockton-bsp. If not, see <http://www.gnu.org/licenses/>. -use crate::helpers::{slice_to_u32, slice_to_vec3}; -use crate::types::{Result, ParseError}; +use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::helpers::{slice_to_u32, slice_to_vec3}; use crate::traits::models::*; -use super::Q3BSPFile; +use crate::types::{ParseError, Result}; const MODEL_SIZE: usize = (4 * 3 * 2) + (4 * 4); -pub fn from_data( - data: &[u8], - n_faces: u32, - n_brushes: u32, -) -> Result<Box<[Model]>> { +pub fn from_data(data: &[u8], n_faces: u32, n_brushes: u32) -> Result<Box<[Model]>> { if data.len() % MODEL_SIZE != 0 { return Err(ParseError::Invalid); } @@ -48,7 +44,7 @@ pub fn from_data( return Err(ParseError::Invalid); } - start..start+n + start..start + n }; let brushes_idx = { @@ -59,7 +55,7 @@ pub fn from_data( return Err(ParseError::Invalid); } - start..start+n + start..start + n }; models.push(Model { @@ -73,15 +69,14 @@ pub fn from_data( Ok(models.into_boxed_slice()) } - impl<T: CoordSystem> HasModels<T> for Q3BSPFile<T> { type ModelsIter<'a> = std::slice::Iter<'a, Model>; - fn models_iter<'a>(&'a self) -> Self::ModelsIter<'a> { + fn models_iter(&self) -> Self::ModelsIter<'_> { self.models.iter() } - fn get_model<'a>(&'a self, index: u32) -> &'a Model { + fn get_model(&self, index: u32) -> &Model { &self.models[index as usize] } } diff --git a/stockton-levels/src/q3/planes.rs b/stockton-levels/src/q3/planes.rs index 618a441..7acc872 100644 --- a/stockton-levels/src/q3/planes.rs +++ b/stockton-levels/src/q3/planes.rs @@ -17,22 +17,20 @@ const PLANE_SIZE: usize = (4 * 3) + 4; -use crate::helpers::{slice_to_f32, slice_to_vec3}; -use crate::types::{Result, ParseError}; -use crate::traits::planes::*; use super::Q3BSPFile; use crate::coords::CoordSystem; +use crate::helpers::{slice_to_f32, slice_to_vec3}; +use crate::traits::planes::*; +use crate::types::{ParseError, Result}; /// Parse a lump of planes. /// A lump is (data length / plane size) planes long pub fn from_data(data: &[u8]) -> Result<Box<[Plane]>> { - let length = data.len() / PLANE_SIZE; if data.is_empty() || data.len() % PLANE_SIZE != 0 || length % 2 != 0 { return Err(ParseError::Invalid); } - let mut planes = Vec::with_capacity(length / 2); for n in 0..length { let offset = n * PLANE_SIZE; @@ -49,11 +47,11 @@ pub fn from_data(data: &[u8]) -> Result<Box<[Plane]>> { impl<T: CoordSystem> HasPlanes<T> for Q3BSPFile<T> { type PlanesIter<'a> = std::slice::Iter<'a, Plane>; - fn planes_iter<'a>(&'a self) -> Self::PlanesIter<'a> { + fn planes_iter(&self) -> Self::PlanesIter<'_> { self.planes.iter() } - fn get_plane<'a>(&'a self, idx: u32) -> &'a Plane { + fn get_plane(&self, idx: u32) -> &Plane { &self.planes[idx as usize] } -}
\ No newline at end of file +} diff --git a/stockton-levels/src/q3/textures.rs b/stockton-levels/src/q3/textures.rs index e317898..5f8a19f 100644 --- a/stockton-levels/src/q3/textures.rs +++ b/stockton-levels/src/q3/textures.rs @@ -18,10 +18,10 @@ use std::str; use super::Q3BSPFile; -use crate::traits::textures::*; -use crate::helpers::slice_to_u32; -use crate::types::{Result, ParseError}; use crate::coords::CoordSystem; +use crate::helpers::slice_to_u32; +use crate::traits::textures::*; +use crate::types::{ParseError, Result}; const TEXTURE_LUMP_SIZE: usize = 64 + 4 + 4; @@ -42,9 +42,16 @@ pub fn from_data(lump: &[u8]) -> Result<Box<[Texture]>> { for n in 0..length { let offset = n * TEXTURE_LUMP_SIZE; textures.push(Texture { - name: str::from_utf8(&lump[offset..offset + 64]).map_err(|_| ParseError::Invalid)?.trim_matches('\0').to_owned(), - surface: SurfaceFlags::from_bits_truncate(slice_to_u32(&lump[offset + 64..offset + 68])), - contents: ContentsFlags::from_bits_truncate(slice_to_u32(&lump[offset + 68..offset + 72])), + name: str::from_utf8(&lump[offset..offset + 64]) + .map_err(|_| ParseError::Invalid)? + .trim_matches('\0') + .to_owned(), + surface: SurfaceFlags::from_bits_truncate(slice_to_u32( + &lump[offset + 64..offset + 68], + )), + contents: ContentsFlags::from_bits_truncate(slice_to_u32( + &lump[offset + 68..offset + 72], + )), }); } @@ -54,11 +61,11 @@ pub fn from_data(lump: &[u8]) -> Result<Box<[Texture]>> { impl<T: CoordSystem> HasTextures for Q3BSPFile<T> { type TexturesIter<'a> = std::slice::Iter<'a, Texture>; - fn textures_iter<'a>(&'a self) -> Self::TexturesIter<'a> { + fn textures_iter(&self) -> Self::TexturesIter<'_> { self.textures.iter() } - fn get_texture<'a>(&'a self, idx: u32) -> &'a Texture { + fn get_texture(&self, idx: u32) -> &Texture { &self.textures[idx as usize] } } @@ -84,10 +91,7 @@ fn textures_single_texture() { lump[0].surface, SurfaceFlags::NO_DAMAGE | SurfaceFlags::SLICK | SurfaceFlags::FLESH | SurfaceFlags::DUST ); - assert_eq!( - lump[0].contents, - ContentsFlags::SOLID | ContentsFlags::LAVA - ); + assert_eq!(lump[0].contents, ContentsFlags::SOLID | ContentsFlags::LAVA); } #[test] @@ -140,13 +144,7 @@ fn textures_multiple_textures() { SurfaceFlags::POINT_LIGHT | SurfaceFlags::SKIP ); - assert_eq!( - lump[0].contents, - ContentsFlags::SOLID | ContentsFlags::LAVA - ); + assert_eq!(lump[0].contents, ContentsFlags::SOLID | ContentsFlags::LAVA); assert_eq!(lump[1].contents, ContentsFlags::SOLID); - assert_eq!( - lump[2].contents, - ContentsFlags::SOLID | ContentsFlags::FOG - ); + assert_eq!(lump[2].contents, ContentsFlags::SOLID | ContentsFlags::FOG); } diff --git a/stockton-levels/src/q3/tree.rs b/stockton-levels/src/q3/tree.rs index 4d49e76..bc22b22 100644 --- a/stockton-levels/src/q3/tree.rs +++ b/stockton-levels/src/q3/tree.rs @@ -17,11 +17,11 @@ //! Parses the BSP tree into a usable format +use super::Q3BSPFile; use crate::coords::CoordSystem; -use crate::helpers::{slice_to_u32, slice_to_i32, slice_to_vec3i}; -use crate::types::{ParseError, Result}; +use crate::helpers::{slice_to_i32, slice_to_u32, slice_to_vec3i}; use crate::traits::tree::*; -use super::Q3BSPFile; +use crate::types::{ParseError, Result}; const NODE_SIZE: usize = 4 + (4 * 2) + (4 * 3) + (4 * 3); const LEAF_SIZE: usize = 4 * 6 + (4 * 3 * 2); @@ -39,18 +39,17 @@ pub fn from_data( } Ok(compile_node( - 0, - nodes, - leaves, - leaf_faces, - leaf_brushes, - n_faces, - n_brushes, - )?, - ) + 0, + nodes, + leaves, + leaf_faces, + leaf_brushes, + n_faces, + n_brushes, + )?) } - /// Internal function. Visits given node and all its children. Used to recursively build tree. +/// Internal function. Visits given node and all its children. Used to recursively build tree. fn compile_node( i: i32, nodes: &[u8], @@ -93,7 +92,7 @@ fn compile_node( let start = slice_to_u32(&raw[40..44]) as usize; let n = slice_to_u32(&raw[44..48]) as usize; let mut brushes = Vec::with_capacity(n); - + if n > 0 { if start + n > leaf_brushes.len() / 4 { return Err(ParseError::Invalid); @@ -163,7 +162,7 @@ fn compile_node( } impl<T: CoordSystem> HasBSPTree<T> for Q3BSPFile<T> { - fn get_bsp_root<'a>(&'a self) -> &'a BSPNode { + fn get_bsp_root(&self) -> &BSPNode { &self.tree_root } -}
\ No newline at end of file +} diff --git a/stockton-levels/src/q3/vertices.rs b/stockton-levels/src/q3/vertices.rs index d517ede..649d0d5 100644 --- a/stockton-levels/src/q3/vertices.rs +++ b/stockton-levels/src/q3/vertices.rs @@ -18,10 +18,10 @@ use std::convert::TryInto; use super::Q3BSPFile; +use crate::coords::CoordSystem; use crate::helpers::{slice_to_u32, slice_to_vec3}; -use crate::types::{Result, ParseError, RGBA}; use crate::traits::vertices::*; -use crate::coords::CoordSystem; +use crate::types::{ParseError, Result, RGBA}; /// The size of one vertex const VERTEX_SIZE: usize = (4 * 3) + (2 * 2 * 4) + (4 * 3) + 4; @@ -56,7 +56,6 @@ pub fn meshverts_from_data(data: &[u8]) -> Result<Box<[MeshVert]>> { } let length = data.len() / 4; - let mut meshverts = Vec::with_capacity(length as usize); for n in 0..length { meshverts.push(slice_to_u32(&data[n * 4..(n + 1) * 4])) @@ -68,11 +67,11 @@ pub fn meshverts_from_data(data: &[u8]) -> Result<Box<[MeshVert]>> { impl<T: CoordSystem> HasVertices<T> for Q3BSPFile<T> { type VerticesIter<'a> = std::slice::Iter<'a, Vertex>; - fn vertices_iter<'a>(&'a self) -> Self::VerticesIter<'a> { + fn vertices_iter(&self) -> Self::VerticesIter<'_> { self.vertices.iter() } - fn get_vertex<'a>(&'a self, index: u32) -> &'a Vertex { + fn get_vertex(&self, index: u32) -> &Vertex { &self.vertices[index as usize] } } @@ -80,11 +79,11 @@ impl<T: CoordSystem> HasVertices<T> for Q3BSPFile<T> { impl<T: CoordSystem> HasMeshVerts<T> for Q3BSPFile<T> { type MeshVertsIter<'a> = std::slice::Iter<'a, MeshVert>; - fn meshverts_iter<'a>(&'a self) -> Self::MeshVertsIter<'a> { + fn meshverts_iter(&self) -> Self::MeshVertsIter<'_> { self.meshverts.iter() } fn get_meshvert<'a>(&self, index: u32) -> MeshVert { self.meshverts[index as usize] } -}
\ No newline at end of file +} diff --git a/stockton-levels/src/q3/visdata.rs b/stockton-levels/src/q3/visdata.rs index fd48415..92b61d5 100644 --- a/stockton-levels/src/q3/visdata.rs +++ b/stockton-levels/src/q3/visdata.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -14,22 +14,21 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Parses visdata from Q3 BSPs. - -use std::vec::IntoIter; use bitvec::prelude::*; +use std::vec::IntoIter; -use crate::types::{Result, ParseError}; -use crate::traits::visdata::*; -use crate::helpers::slice_to_i32; use super::file::Q3BSPFile; use crate::coords::CoordSystem; +use crate::helpers::slice_to_i32; +use crate::traits::visdata::*; +use crate::types::{ParseError, Result}; /// Stores cluster-to-cluster visibility information. pub fn from_data(data: &[u8]) -> Result<Box<[BitBox<Local, u8>]>> { if data.len() < 8 { return Err(ParseError::Invalid); } - + let n_vecs = slice_to_i32(&data[0..4]) as usize; let size_vecs = slice_to_i32(&data[4..8]) as usize; @@ -53,7 +52,7 @@ impl<T: CoordSystem> HasVisData for Q3BSPFile<T> { fn all_visible_from(&self, from: ClusterId) -> Self::VisibleIterator { let mut visible = vec![]; - for (idx,val) in self.visdata[from as usize].iter().enumerate() { + for (idx, val) in self.visdata[from as usize].iter().enumerate() { if *val { visible.push(idx as u32); } @@ -66,4 +65,3 @@ impl<T: CoordSystem> HasVisData for Q3BSPFile<T> { self.visdata[from as usize][dest as usize] } } - diff --git a/stockton-levels/src/traits/brushes.rs b/stockton-levels/src/traits/brushes.rs index 30ab420..a281394 100644 --- a/stockton-levels/src/traits/brushes.rs +++ b/stockton-levels/src/traits/brushes.rs @@ -39,6 +39,6 @@ pub struct BrushSide { pub trait HasBrushes<S: CoordSystem>: HasPlanes<S> { type BrushesIter<'a>: Iterator<Item = &'a Brush>; - fn brushes_iter<'a>(&'a self) -> Self::BrushesIter<'a>; - fn get_brush<'a>(&'a self, index: u32) -> &'a Brush; + fn brushes_iter(&self) -> Self::BrushesIter<'_>; + fn get_brush(&self, index: u32) -> &Brush; } diff --git a/stockton-levels/src/traits/effects.rs b/stockton-levels/src/traits/effects.rs index dec2122..73f55bd 100644 --- a/stockton-levels/src/traits/effects.rs +++ b/stockton-levels/src/traits/effects.rs @@ -25,14 +25,12 @@ pub struct Effect { pub name: String, /// The brush used for this effect - pub brush_idx: u32 - - // todo: unknown: i32 + pub brush_idx: u32, // todo: unknown: i32 } pub trait HasEffects<S: CoordSystem>: HasBrushes<S> { type EffectsIter<'a>: Iterator<Item = &'a Effect>; - fn effects_iter<'a>(&'a self) -> Self::EffectsIter<'a>; - fn get_effect<'a>(&'a self, index: u32) -> &'a Effect; + fn effects_iter(&self) -> Self::EffectsIter<'_>; + fn get_effect(&self, index: u32) -> &Effect; } diff --git a/stockton-levels/src/traits/entities.rs b/stockton-levels/src/traits/entities.rs index 706f25a..e1370ed 100644 --- a/stockton-levels/src/traits/entities.rs +++ b/stockton-levels/src/traits/entities.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -13,8 +13,8 @@ // 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; use std::collections::HashMap; +use std::iter::Iterator; #[derive(Debug, Clone, PartialEq)] /// A game entity @@ -23,7 +23,7 @@ pub struct Entity { } pub trait HasEntities { - type EntitiesIter<'a>: Iterator<Item = &'a Entity>; + type EntitiesIter<'a>: Iterator<Item = &'a Entity>; - fn entities_iter<'a>(&'a self) -> Self::EntitiesIter<'a>; -}
\ No newline at end of file + fn entities_iter(&self) -> Self::EntitiesIter<'_>; +} diff --git a/stockton-levels/src/traits/faces.rs b/stockton-levels/src/traits/faces.rs index 50d2b7d..3b9fbdf 100644 --- a/stockton-levels/src/traits/faces.rs +++ b/stockton-levels/src/traits/faces.rs @@ -15,10 +15,10 @@ // You should have received a copy of the GNU General Public License // along with stockton-bsp. If not, see <http://www.gnu.org/licenses/>. -use std::ops::Range; use na::{Vector2, Vector3}; +use std::ops::Range; -use super::{HasEffects, HasTextures, HasLightMaps, HasMeshVerts}; +use super::{HasEffects, HasLightMaps, HasMeshVerts, HasTextures}; use crate::coords::CoordSystem; #[derive(Debug, Clone, Copy, PartialEq)] @@ -43,15 +43,17 @@ pub struct Face { pub map_size: Vector2<u32>, pub map_origin: Vector3<f32>, pub map_vecs: [Vector3<f32>; 2], - + pub normal: Vector3<f32>, pub size: Vector2<u32>, } -pub trait HasFaces<S: CoordSystem>: HasTextures + HasEffects<S> + HasLightMaps + HasMeshVerts<S> { +pub trait HasFaces<S: CoordSystem>: + HasTextures + HasEffects<S> + HasLightMaps + HasMeshVerts<S> +{ type FacesIter<'a>: Iterator<Item = &'a Face>; - fn faces_iter<'a>(&'a self) -> Self::FacesIter<'a>; + fn faces_iter(&self) -> Self::FacesIter<'_>; fn faces_len(&self) -> u32; - fn get_face<'a>(&'a self, index: u32) -> &'a Face; + fn get_face(&self, index: u32) -> &Face; } diff --git a/stockton-levels/src/traits/light_maps.rs b/stockton-levels/src/traits/light_maps.rs index 9b30d91..59dc27f 100644 --- a/stockton-levels/src/traits/light_maps.rs +++ b/stockton-levels/src/traits/light_maps.rs @@ -57,6 +57,6 @@ impl fmt::Debug for LightMap { pub trait HasLightMaps { type LightMapsIter<'a>: Iterator<Item = &'a LightMap>; - fn lightmaps_iter<'a>(&'a self) -> Self::LightMapsIter<'a>; - fn get_lightmap<'a>(&'a self, index: u32) -> &'a LightMap; -}
\ No newline at end of file + fn lightmaps_iter(&self) -> Self::LightMapsIter<'_>; + fn get_lightmap(&self, index: u32) -> &LightMap; +} diff --git a/stockton-levels/src/traits/light_vols.rs b/stockton-levels/src/traits/light_vols.rs index 8e75401..871f028 100644 --- a/stockton-levels/src/traits/light_vols.rs +++ b/stockton-levels/src/traits/light_vols.rs @@ -27,6 +27,6 @@ pub struct LightVol { pub trait HasLightVols { type LightVolsIter<'a>: Iterator<Item = &'a LightVol>; - fn lightvols_iter<'a>(&'a self) -> Self::LightVolsIter<'a>; - fn get_lightvol<'a>(&'a self, index: u32) -> &'a LightVol; + fn lightvols_iter(&self) -> Self::LightVolsIter<'_>; + fn get_lightvol(&self, index: u32) -> &LightVol; } diff --git a/stockton-levels/src/traits/mod.rs b/stockton-levels/src/traits/mod.rs index 15bac30..3ee47b4 100644 --- a/stockton-levels/src/traits/mod.rs +++ b/stockton-levels/src/traits/mod.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -14,28 +14,28 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Traits for parts of files that can exist -pub mod visdata; -pub mod entities; -pub mod textures; -pub mod planes; -pub mod vertices; -pub mod light_maps; -pub mod light_vols; pub mod brushes; pub mod effects; +pub mod entities; pub mod faces; -pub mod tree; +pub mod light_maps; +pub mod light_vols; pub mod models; +pub mod planes; +pub mod textures; +pub mod tree; +pub mod vertices; +pub mod visdata; -pub use self::visdata::HasVisData; -pub use self::textures::HasTextures; -pub use self::entities::HasEntities; -pub use self::planes::HasPlanes; -pub use self::vertices::{HasVertices, HasMeshVerts}; -pub use self::light_maps::HasLightMaps; -pub use self::light_vols::HasLightVols; pub use self::brushes::HasBrushes; pub use self::effects::HasEffects; +pub use self::entities::HasEntities; pub use self::faces::HasFaces; +pub use self::light_maps::HasLightMaps; +pub use self::light_vols::HasLightVols; +pub use self::models::HasModels; +pub use self::planes::HasPlanes; +pub use self::textures::HasTextures; pub use self::tree::HasBSPTree; -pub use self::models::HasModels;
\ No newline at end of file +pub use self::vertices::{HasMeshVerts, HasVertices}; +pub use self::visdata::HasVisData; diff --git a/stockton-levels/src/traits/models.rs b/stockton-levels/src/traits/models.rs index 4f755f3..1f868ad 100644 --- a/stockton-levels/src/traits/models.rs +++ b/stockton-levels/src/traits/models.rs @@ -18,7 +18,7 @@ use na::Vector3; use std::ops::Range; -use super::{HasFaces, HasBrushes}; +use super::{HasBrushes, HasFaces}; use crate::coords::CoordSystem; #[derive(Debug, Clone)] @@ -32,6 +32,6 @@ pub struct Model { pub trait HasModels<S: CoordSystem>: HasFaces<S> + HasBrushes<S> { type ModelsIter<'a>: Iterator<Item = &'a Model>; - fn models_iter<'a>(&'a self) -> Self::ModelsIter<'a>; - fn get_model<'a>(&'a self, index: u32) -> &'a Model; + fn models_iter(&self) -> Self::ModelsIter<'_>; + fn get_model(&self, index: u32) -> &Model; } diff --git a/stockton-levels/src/traits/planes.rs b/stockton-levels/src/traits/planes.rs index c024815..e827a31 100644 --- a/stockton-levels/src/traits/planes.rs +++ b/stockton-levels/src/traits/planes.rs @@ -15,9 +15,9 @@ // You should have received a copy of the GNU General Public License // along with rust-bsp. If not, see <http://www.gnu.org/licenses/>. -use std::iter::Iterator; -use na::Vector3; use crate::coords::CoordSystem; +use na::Vector3; +use std::iter::Iterator; /// The planes lump from a BSP file. /// Found at lump index 2 in a q3 bsp. @@ -39,6 +39,6 @@ pub struct Plane { pub trait HasPlanes<S: CoordSystem> { type PlanesIter<'a>: Iterator<Item = &'a Plane>; - fn planes_iter<'a>(&'a self) -> Self::PlanesIter<'a>; - fn get_plane<'a>(&'a self, idx: u32) -> &'a Plane; -}
\ No newline at end of file + fn planes_iter(&self) -> Self::PlanesIter<'_>; + fn get_plane(&self, idx: u32) -> &Plane; +} diff --git a/stockton-levels/src/traits/textures.rs b/stockton-levels/src/traits/textures.rs index 16213ea..a9c9bc9 100644 --- a/stockton-levels/src/traits/textures.rs +++ b/stockton-levels/src/traits/textures.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -23,21 +23,20 @@ pub struct Texture { pub contents: ContentsFlags, } - bitflags!( /// Extracted from the Q3 arena engine code. /// https://github.com/id-Software/Quake-III-Arena/blob/master/code/game/surfaceflags.h pub struct SurfaceFlags: u32 { /// never give falling damage const NO_DAMAGE = 0x1; - + /// affects game physics const SLICK = 0x2; - + /// lighting from environment map const SKY = 0x4; - /// don't make missile explosions + /// don't make missile explosions const NO_IMPACT = 0x10; /// function as a ladder @@ -45,43 +44,43 @@ bitflags!( /// don't leave missile marks const NO_MARKS = 0x20; - + /// make flesh sounds and effects const FLESH = 0x40; - + /// don't generate a drawsurface at all const NODRAW = 0x80; - + /// make a primary bsp splitter const HINT = 0x01_00; - + /// completely ignore, allowing non-closed brushes const SKIP = 0x02_00; - + /// surface doesn't need a lightmap const NO_LIGHT_MAP = 0x04_00; - + /// generate lighting info at vertexes const POINT_LIGHT = 0x08_00; - + /// clanking footsteps const METAL_STEPS = 0x10_00; - + /// no footstep sounds const NO_STEPS = 0x20_00; - + /// don't collide against curves with this set const NON_SOLID = 0x40_00; - + /// act as a light filter during q3map -light const LIGHT_FILTER = 0x80_00; - + /// do per-pixel light shadow casting in q3map const ALPHA_SHADOW = 0x01_00_00; - + /// don't dlight even if solid (solid lava, skies) const NO_DLIGHT = 0x02_00_00; - + /// leave a dust trail when walking on this surface const DUST = 0x04_00_00; } @@ -116,7 +115,7 @@ bitflags!( const DO_NOT_ENTER = 0x20_00_00; const BOT_CLIP = 0x40_00_00; const MOVER = 0x80_00_00; - + // removed before bsping an entity const ORIGIN = 0x01_00_00_00; @@ -125,18 +124,18 @@ bitflags!( /// brush not used for the bsp const DETAIL = 0x08_00_00_00; - + /// brush not used for the bsp const CORPSE = 0x04_00_00_00; /// brushes used for the bsp const STRUCTURAL = 0x10_00_00_00; - + /// don't consume surface fragments inside const TRANSLUCENT = 0x20_00_00_00; const TRIGGER = 0x40_00_00_00; - + /// don't leave bodies or items (death fog, lava) const NODROP = 0x80_00_00_00; } @@ -145,6 +144,6 @@ bitflags!( pub trait HasTextures { type TexturesIter<'a>: Iterator<Item = &'a Texture>; - fn textures_iter<'a>(&'a self) -> Self::TexturesIter<'a>; - fn get_texture<'a>(&'a self, idx: u32) -> &'a Texture; + fn textures_iter(&self) -> Self::TexturesIter<'_>; + fn get_texture(&self, idx: u32) -> &Texture; } diff --git a/stockton-levels/src/traits/tree.rs b/stockton-levels/src/traits/tree.rs index c56576a..89be1c1 100644 --- a/stockton-levels/src/traits/tree.rs +++ b/stockton-levels/src/traits/tree.rs @@ -17,9 +17,9 @@ //! Parses the BSP tree into a usable format -use na::Vector3; -use super::{HasFaces, HasBrushes, HasVisData}; +use super::{HasBrushes, HasFaces, HasVisData}; use crate::coords::CoordSystem; +use na::Vector3; /// A node in a BSP tree. /// Either has two children *or* a leaf entry. @@ -28,13 +28,13 @@ pub struct BSPNode { pub plane_idx: u32, pub min: Vector3<i32>, pub max: Vector3<i32>, - pub value: BSPNodeValue + pub value: BSPNodeValue, } #[derive(Debug, Clone)] pub enum BSPNodeValue { - Leaf (BSPLeaf), - Children (Box<BSPNode>, Box<BSPNode>) + Leaf(BSPLeaf), + Children(Box<BSPNode>, Box<BSPNode>), } /// A leaf in a BSP tree. @@ -48,5 +48,5 @@ pub struct BSPLeaf { } pub trait HasBSPTree<S: CoordSystem>: HasFaces<S> + HasBrushes<S> + HasVisData { - fn get_bsp_root<'a>(&'a self) -> &'a BSPNode; -}
\ No newline at end of file + fn get_bsp_root(&self) -> &BSPNode; +} diff --git a/stockton-levels/src/traits/vertices.rs b/stockton-levels/src/traits/vertices.rs index 7b14c98..ddf315b 100644 --- a/stockton-levels/src/traits/vertices.rs +++ b/stockton-levels/src/traits/vertices.rs @@ -15,12 +15,11 @@ // You should have received a copy of the GNU General Public License // along with stockton-bsp. If not, see <http://www.gnu.org/licenses/>. -use crate::helpers::{slice_to_f32}; use crate::coords::CoordSystem; +use crate::helpers::slice_to_f32; use crate::types::RGBA; use na::Vector3; - /// A vertex, used to describe a face. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vertex { @@ -54,17 +53,17 @@ pub type MeshVert = u32; pub trait HasVertices<S: CoordSystem> { type VerticesIter<'a>: Iterator<Item = &'a Vertex>; - fn vertices_iter<'a>(&'a self) -> Self::VerticesIter<'a>; - fn get_vertex<'a>(&'a self, index: u32) -> &'a Vertex; + fn vertices_iter(&self) -> Self::VerticesIter<'_>; + fn get_vertex(&self, index: u32) -> &Vertex; } pub trait HasMeshVerts<S: CoordSystem>: HasVertices<S> { type MeshVertsIter<'a>: Iterator<Item = &'a MeshVert>; - fn meshverts_iter<'a>(&'a self) -> Self::MeshVertsIter<'a>; + fn meshverts_iter(&self) -> Self::MeshVertsIter<'_>; fn get_meshvert(&self, index: u32) -> MeshVert; - fn resolve_meshvert<'a>(&'a self, index: u32, base: u32) -> &'a Vertex { + fn resolve_meshvert(&self, index: u32, base: u32) -> &Vertex { self.get_vertex(self.get_meshvert(index) + base) } -}
\ No newline at end of file +} diff --git a/stockton-levels/src/traits/visdata.rs b/stockton-levels/src/traits/visdata.rs index 92ba9b8..749000b 100644 --- a/stockton-levels/src/traits/visdata.rs +++ b/stockton-levels/src/traits/visdata.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -18,12 +18,12 @@ use std::iter::Iterator; pub type ClusterId = u32; pub trait HasVisData { - /// The iterator returned from all_visible_from - type VisibleIterator: Iterator<Item = ClusterId>; + /// The iterator returned from all_visible_from + type VisibleIterator: Iterator<Item = ClusterId>; - /// Returns an iterator of all clusters visible from the given Cluster ID - fn all_visible_from(&self, from: ClusterId) -> Self::VisibleIterator; + /// Returns an iterator of all clusters visible from the given Cluster ID + fn all_visible_from(&self, from: ClusterId) -> Self::VisibleIterator; - /// Returns true if `dest` is visible from `from`. - fn cluster_visible_from(&self, from: ClusterId, dest: ClusterId) -> bool; -}
\ No newline at end of file + /// Returns true if `dest` is visible from `from`. + fn cluster_visible_from(&self, from: ClusterId, dest: ClusterId) -> bool; +} diff --git a/stockton-levels/src/types.rs b/stockton-levels/src/types.rs index 7bccd23..6f19b3d 100644 --- a/stockton-levels/src/types.rs +++ b/stockton-levels/src/types.rs @@ -86,8 +86,8 @@ impl RGB { /// An error encountered while parsing. pub enum ParseError { Unsupported, - Invalid + Invalid, } /// Standard result type. -pub type Result<T> = std::result::Result<T, ParseError>;
\ No newline at end of file +pub type Result<T> = std::result::Result<T, ParseError>; diff --git a/stockton-render/src/culling.rs b/stockton-render/src/culling.rs index 8240701..dbe5a1e 100644 --- a/stockton-render/src/culling.rs +++ b/stockton-render/src/culling.rs @@ -20,59 +20,61 @@ use stockton_levels::prelude::*; use stockton_levels::traits::tree::{BSPNode, BSPNodeValue}; use stockton_types::Vector3; - /// Get the visible faces according to visdata and frustum culling // TODO: Write this. For now, just render all faces pub fn get_visible_faces<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T) -> Vec<u32> { - let vis_cluster = get_cluster_id(pos, file); - - if (vis_cluster & 0x80000000) != 0 { // Negative = Invalid camera position - return vec![]; - } + let vis_cluster = get_cluster_id(pos, file); + + if (vis_cluster & 0x80000000) != 0 { + // Negative = Invalid camera position + return vec![]; + } - let mut visible = Vec::with_capacity(file.faces_len() as usize); - walk_bsp_tree(file.get_bsp_root(), vis_cluster, &mut visible, file); + let mut visible = Vec::with_capacity(file.faces_len() as usize); + walk_bsp_tree(file.get_bsp_root(), vis_cluster, &mut visible, file); - return visible; + visible } -pub fn walk_bsp_tree<X: CoordSystem, T: MinBSPFeatures<X>>(node: &BSPNode, vis_cluster: u32, visible_faces: &mut Vec<u32>, file: &T) -> () { - if let BSPNodeValue::Children(front, back) = &node.value { - walk_bsp_tree(back, vis_cluster, visible_faces, file); - walk_bsp_tree(front, vis_cluster, visible_faces, file); - } else if let BSPNodeValue::Leaf(leaf) = &node.value { - if (leaf.cluster_id & 0x80000000) != 0 { // Negative means invalid leaf - return; - } else if file.cluster_visible_from(vis_cluster, leaf.cluster_id) { - for face_idx in leaf.faces_idx.iter() { - // TODO: Culling or something - visible_faces.push(*face_idx); - } - } - } +pub fn walk_bsp_tree<X: CoordSystem, T: MinBSPFeatures<X>>( + node: &BSPNode, + vis_cluster: u32, + visible_faces: &mut Vec<u32>, + file: &T, +) { + if let BSPNodeValue::Children(front, back) = &node.value { + walk_bsp_tree(back, vis_cluster, visible_faces, file); + walk_bsp_tree(front, vis_cluster, visible_faces, file); + } else if let BSPNodeValue::Leaf(leaf) = &node.value { + if (leaf.cluster_id & 0x80000000) != 0 { + // Negative means invalid leaf + return; + } else if file.cluster_visible_from(vis_cluster, leaf.cluster_id) { + for face_idx in leaf.faces_idx.iter() { + // TODO: Culling or something + visible_faces.push(*face_idx); + } + } + } } -/// Get the viscluster pos lies in +/// Get the viscluster pos lies in fn get_cluster_id<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T) -> u32 { - let mut node = file.get_bsp_root(); - loop { - if let BSPNodeValue::Children(front, back) = &node.value { - let plane = file.get_plane(node.plane_idx); - let dist = plane.normal.dot(&pos) - plane.dist; + let mut node = file.get_bsp_root(); + while let BSPNodeValue::Children(front, back) = &node.value { + let plane = file.get_plane(node.plane_idx); + let dist = plane.normal.dot(&pos) - plane.dist; - if dist >= 0.0 { - node = front; - } else { - node = back; - } - } else { - break; - } - } + if dist >= 0.0 { + node = front; + } else { + node = back; + } + } - if let BSPNodeValue::Leaf(leaf) = &node.value { - leaf.cluster_id - } else { - panic!("should have had a leaf but didn't"); - } -}
\ No newline at end of file + if let BSPNodeValue::Leaf(leaf) = &node.value { + leaf.cluster_id + } else { + panic!("should have had a leaf but didn't"); + } +} diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs index cbe56f4..9b3e726 100644 --- a/stockton-render/src/draw/buffer.rs +++ b/stockton-render/src/draw/buffer.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Oscar Shrimpton +// Copyright (C) 2019 Oscar Shrimpton // 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 @@ -13,17 +13,17 @@ // 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 core::mem::{size_of, ManuallyDrop}; +use std::convert::TryInto; use std::iter::once; use std::ops::{Index, IndexMut}; -use std::convert::TryInto; -use core::mem::{ManuallyDrop, size_of}; use hal::prelude::*; use hal::{ - MemoryTypeId, - buffer::Usage, - memory::{Properties, Segment}, - queue::Submission + buffer::Usage, + memory::{Properties, Segment}, + queue::Submission, + MemoryTypeId, }; use crate::error::CreationError; @@ -31,193 +31,224 @@ use crate::types::*; /// Create a buffer of the given specifications, allocating more device memory. // TODO: Use a different memory allocator? -pub(crate) fn create_buffer(device: &mut Device, - adapter: &Adapter, - usage: Usage, - properties: Properties, - size: u64) -> Result<(Buffer, Memory), CreationError> { - let mut buffer = unsafe { device - .create_buffer(size, usage) } - .map_err(|e| CreationError::BufferError (e))?; - - let requirements = unsafe { device.get_buffer_requirements(&buffer) }; - let memory_type_id = adapter.physical_device - .memory_properties().memory_types - .iter().enumerate() - .find(|&(id, memory_type)| { - requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(properties) - }) - .map(|(id, _)| MemoryTypeId(id)) - .ok_or(CreationError::BufferNoMemory)?; - - let memory = unsafe {device - .allocate_memory(memory_type_id, requirements.size) } - .map_err(|_| CreationError::OutOfMemoryError)?; - - unsafe { device - .bind_buffer_memory(&memory, 0, &mut buffer) } - .map_err(|_| CreationError::BufferNoMemory)?; - - Ok((buffer, memory)) +pub(crate) fn create_buffer( + device: &mut Device, + adapter: &Adapter, + usage: Usage, + properties: Properties, + size: u64, +) -> Result<(Buffer, Memory), CreationError> { + let mut buffer = + unsafe { device.create_buffer(size, usage) }.map_err(CreationError::BufferError)?; + + let requirements = unsafe { device.get_buffer_requirements(&buffer) }; + let memory_type_id = adapter + .physical_device + .memory_properties() + .memory_types + .iter() + .enumerate() + .find(|&(id, memory_type)| { + requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(properties) + }) + .map(|(id, _)| MemoryTypeId(id)) + .ok_or(CreationError::BufferNoMemory)?; + + let memory = unsafe { device.allocate_memory(memory_type_id, requirements.size) } + .map_err(|_| CreationError::OutOfMemoryError)?; + + unsafe { device.bind_buffer_memory(&memory, 0, &mut buffer) } + .map_err(|_| CreationError::BufferNoMemory)?; + + Ok((buffer, memory)) } /// A buffer that can be modified by the CPU pub trait ModifiableBuffer: IndexMut<usize> { - /// Get a handle to the underlying GPU buffer - fn get_buffer<'a>(&'a mut self) -> &'a Buffer; - - /// Commit all changes to GPU memory, returning a handle to the GPU buffer - fn commit<'a>(&'a mut self, device: &Device, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool) -> &'a Buffer; + /// Get a handle to the underlying GPU buffer + fn get_buffer(&mut self) -> &Buffer; + + /// Commit all changes to GPU memory, returning a handle to the GPU buffer + fn commit<'a>( + &'a mut self, + device: &Device, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + ) -> &'a Buffer; } /// A GPU buffer that is written to using a staging buffer pub struct StagedBuffer<'a, T: Sized> { - /// CPU-visible buffer - staged_buffer: ManuallyDrop<Buffer>, + /// CPU-visible buffer + staged_buffer: ManuallyDrop<Buffer>, - /// CPU-visible memory - staged_memory: ManuallyDrop<Memory>, + /// CPU-visible memory + staged_memory: ManuallyDrop<Memory>, - /// GPU Buffer - buffer: ManuallyDrop<Buffer>, + /// GPU Buffer + buffer: ManuallyDrop<Buffer>, - /// GPU Memory - memory: ManuallyDrop<Memory>, + /// GPU Memory + memory: ManuallyDrop<Memory>, - /// Where staged buffer is mapped in CPU memory - staged_mapped_memory: &'a mut [T], + /// Where staged buffer is mapped in CPU memory + staged_mapped_memory: &'a mut [T], - /// If staged memory has been changed since last `commit` - staged_is_dirty: bool, + /// If staged memory has been changed since last `commit` + staged_is_dirty: bool, - /// The highest index in the buffer that's been written to. - pub highest_used: usize + /// The highest index in the buffer that's been written to. + pub highest_used: usize, } - impl<'a, T: Sized> StagedBuffer<'a, T> { - /// size is the size in T - pub fn new(device: &mut Device, adapter: &Adapter, usage: Usage, size: u64) -> Result<Self, CreationError> { - // Convert size to bytes - let size_bytes = size * size_of::<T>() as u64; - - // Get CPU-visible buffer - let (staged_buffer, staged_memory) = create_buffer(device, adapter, Usage::TRANSFER_SRC, Properties::CPU_VISIBLE, size_bytes)?; - - // Get GPU Buffer - let (buffer, memory) = create_buffer(device, adapter, Usage::TRANSFER_DST | usage, Properties::DEVICE_LOCAL, size_bytes)?; - - // Map it somewhere and get a slice to that memory - let staged_mapped_memory = unsafe { - let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); // TODO - - std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap()) - }; - - Ok(StagedBuffer { - staged_buffer: ManuallyDrop::new(staged_buffer), - staged_memory: ManuallyDrop::new(staged_memory), - buffer: ManuallyDrop::new(buffer), - memory: ManuallyDrop::new(memory), - staged_mapped_memory, - staged_is_dirty: false, - highest_used: 0 - }) - } - - /// Call this before dropping - pub(crate) fn deactivate(mut self, device: &mut Device) { - unsafe { - device.unmap_memory(&self.staged_memory); - - device.free_memory(ManuallyDrop::take(&mut self.staged_memory)); - device.destroy_buffer(ManuallyDrop::take(&mut self.staged_buffer)); - - device.free_memory(ManuallyDrop::take(&mut self.memory)); - device.destroy_buffer(ManuallyDrop::take(&mut self.buffer)); - }; - } + /// size is the size in T + pub fn new( + device: &mut Device, + adapter: &Adapter, + usage: Usage, + size: u64, + ) -> Result<Self, CreationError> { + // Convert size to bytes + let size_bytes = size * size_of::<T>() as u64; + + // Get CPU-visible buffer + let (staged_buffer, staged_memory) = create_buffer( + device, + adapter, + Usage::TRANSFER_SRC, + Properties::CPU_VISIBLE, + size_bytes, + )?; + + // Get GPU Buffer + let (buffer, memory) = create_buffer( + device, + adapter, + Usage::TRANSFER_DST | usage, + Properties::DEVICE_LOCAL, + size_bytes, + )?; + + // Map it somewhere and get a slice to that memory + let staged_mapped_memory = unsafe { + let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); // TODO + + std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap()) + }; + + Ok(StagedBuffer { + staged_buffer: ManuallyDrop::new(staged_buffer), + staged_memory: ManuallyDrop::new(staged_memory), + buffer: ManuallyDrop::new(buffer), + memory: ManuallyDrop::new(memory), + staged_mapped_memory, + staged_is_dirty: false, + highest_used: 0, + }) + } + + /// Call this before dropping + pub(crate) fn deactivate(mut self, device: &mut Device) { + unsafe { + device.unmap_memory(&self.staged_memory); + + device.free_memory(ManuallyDrop::take(&mut self.staged_memory)); + device.destroy_buffer(ManuallyDrop::take(&mut self.staged_buffer)); + + device.free_memory(ManuallyDrop::take(&mut self.memory)); + device.destroy_buffer(ManuallyDrop::take(&mut self.buffer)); + }; + } } -impl <'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { - fn get_buffer<'b>(&'b mut self) -> &'b Buffer { - &self.buffer - } - - fn commit<'b>(&'b mut self, device: &Device, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool) -> &'b Buffer { - // Only commit if there's changes to commit. - if self.staged_is_dirty { - - // Flush mapped memory to ensure the staged buffer is filled - unsafe { - use std::ops::Deref; - device.flush_mapped_memory_ranges(once((self.staged_memory.deref(), Segment::ALL))).unwrap(); - } - - // Copy from staged to buffer - let buf = unsafe { - use hal::command::{CommandBufferFlags, BufferCopy}; - // Get a command buffer - let mut buf = command_pool.allocate_one(hal::command::Level::Primary); - - // Put in our copy command - buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - buf.copy_buffer(&self.staged_buffer, &self.buffer, &[ - BufferCopy { - src: 0, - dst: 0, - size: ((self.highest_used + 1) * size_of::<T>()) as u64 - } - ]); - buf.finish(); - - buf - }; - - // Submit it and wait for completion - // TODO: We could use more semaphores or something? - // TODO: Better error handling - unsafe { - let copy_finished = device.create_fence(false).unwrap(); - command_queue.submit::<_, _, Semaphore, _, _>(Submission { - command_buffers: &[&buf], - wait_semaphores: std::iter::empty::<_>(), - signal_semaphores: std::iter::empty::<_>() - }, Some(©_finished)); - - device - .wait_for_fence(©_finished, core::u64::MAX).unwrap(); - - // Destroy temporary resources - device.destroy_fence(copy_finished); - command_pool.free(once(buf)); - } - - self.staged_is_dirty = false; - } - - &self.buffer - } +impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { + fn get_buffer(&mut self) -> &Buffer { + &self.buffer + } + + fn commit<'b>( + &'b mut self, + device: &Device, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + ) -> &'b Buffer { + // Only commit if there's changes to commit. + if self.staged_is_dirty { + // Flush mapped memory to ensure the staged buffer is filled + unsafe { + use std::ops::Deref; + device + .flush_mapped_memory_ranges(once((self.staged_memory.deref(), Segment::ALL))) + .unwrap(); + } + + // Copy from staged to buffer + let buf = unsafe { + use hal::command::{BufferCopy, CommandBufferFlags}; + // Get a command buffer + let mut buf = command_pool.allocate_one(hal::command::Level::Primary); + + // Put in our copy command + buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); + buf.copy_buffer( + &self.staged_buffer, + &self.buffer, + &[BufferCopy { + src: 0, + dst: 0, + size: ((self.highest_used + 1) * size_of::<T>()) as u64, + }], + ); + buf.finish(); + + buf + }; + + // Submit it and wait for completion + // TODO: We could use more semaphores or something? + // TODO: Better error handling + unsafe { + let copy_finished = device.create_fence(false).unwrap(); + command_queue.submit::<_, _, Semaphore, _, _>( + Submission { + command_buffers: &[&buf], + wait_semaphores: std::iter::empty::<_>(), + signal_semaphores: std::iter::empty::<_>(), + }, + Some(©_finished), + ); + + device + .wait_for_fence(©_finished, core::u64::MAX) + .unwrap(); + + // Destroy temporary resources + device.destroy_fence(copy_finished); + command_pool.free(once(buf)); + } + + self.staged_is_dirty = false; + } + + &self.buffer + } } impl<'a, T: Sized> Index<usize> for StagedBuffer<'a, T> { - type Output = T; + type Output = T; - fn index(&self, index: usize) -> &Self::Output { - &self.staged_mapped_memory[index] - } + fn index(&self, index: usize) -> &Self::Output { + &self.staged_mapped_memory[index] + } } impl<'a, T: Sized> IndexMut<usize> for StagedBuffer<'a, T> { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.staged_is_dirty = true; - if index > self.highest_used { - self.highest_used = index; - } - &mut self.staged_mapped_memory[index] - } -}
\ No newline at end of file + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.staged_is_dirty = true; + if index > self.highest_used { + self.highest_used = index; + } + &mut self.staged_mapped_memory[index] + } +} diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs index 5737928..cb465fd 100644 --- a/stockton-render/src/draw/camera.rs +++ b/stockton-render/src/draw/camera.rs @@ -15,10 +15,10 @@ //! Things related to converting 3D world space to 2D screen space -use stockton_types::{Vector3, Matrix4}; +use stockton_types::{Matrix4, Vector3}; -use std::f32::consts::PI; use na::{look_at_lh, perspective_lh_zo, Mat4, Vec4}; +use std::f32::consts::PI; /// 90 degrees in radians const R89: f32 = (PI / 180.0) * 89.0; @@ -30,201 +30,204 @@ const R90: f32 = PI / 2.0; const R180: f32 = PI; 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() - ) + 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(), + ) } pub struct CameraSettings { - /// Position of the camera (world units) - pub position: Vector3, + /// Position of the camera (world units) + pub position: Vector3, - /// Rotation of the camera (euler angles in radians) - pub rotation: Vector3, + /// Rotation of the camera (euler angles in radians) + pub rotation: Vector3, - /// The up direction (normalized) - pub up: Vector3, + /// The up direction (normalized) + pub up: Vector3, - /// FOV (radians) - pub fov: f32, + /// FOV (radians) + pub fov: f32, - /// Near clipping plane (world units) - pub near: f32, + /// Near clipping plane (world units) + pub near: f32, - /// Far clipping plane (world units) - pub far: f32, + /// Far clipping plane (world units) + pub far: f32, } /// Holds settings related to the projection of world space to screen space /// Also holds maths for generating important matrices pub struct WorkingCamera { - /// Settings for the camera - settings: CameraSettings, + /// Settings for the camera + settings: CameraSettings, - /// Aspect ratio as a fraction - aspect_ratio: f32, + /// Aspect ratio as a fraction + aspect_ratio: f32, - /// Cached view projection matrix - vp_matrix: Mat4, + /// Cached view projection matrix + vp_matrix: Mat4, - /// If true, cached value needs updated - is_dirty: bool + /// If true, cached value needs updated + is_dirty: bool, } impl WorkingCamera { - /// Return a camera with default settings - pub fn defaults(aspect_ratio: f32) -> WorkingCamera { - WorkingCamera::with_settings(CameraSettings { - position: Vector3::new(0.0, 0.0, 0.0), - rotation: Vector3::new(0.0, R90, 0.0), - up: Vector3::new(0.0, 1.0, 0.0), - fov: f32::to_radians(90.0), - near: 0.1, - far: 1024.0, - }, aspect_ratio) - } - - /// Return a camera with the given settings - pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera { - WorkingCamera { - aspect_ratio, - settings, - vp_matrix: Mat4::identity(), - is_dirty: true - } - } - - /// Get the VP matrix, updating cache if needed - pub fn get_matrix<'a>(&'a mut self) -> &'a Mat4 { - // Update matrix if needed - if self.is_dirty { - self.vp_matrix = self.calc_vp_matrix(); - self.is_dirty = false; - } - - // Return the matrix - &self.vp_matrix - } - - /// Returns a matrix that transforms from world space to screen space - fn calc_vp_matrix(&self) -> Matrix4 { - // Get look direction from euler angles - let direction = euler_to_direction(&self.settings.rotation); - - // Converts world space to camera space - let view_matrix = look_at_lh( - &self.settings.position, - &(direction + &self.settings.position), - &self.settings.up - ); - - // Converts camera space to screen space - let projection_matrix = { - let mut temp = perspective_lh_zo( - self.aspect_ratio, - self.settings.fov, - self.settings.near, - self.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 - } - - /// Update the aspect ratio - pub fn update_aspect_ratio(&mut self, new: f32) { - self.aspect_ratio = new; - self.is_dirty = true; - } - - /// Apply rotation of the camera - /// `euler` should be euler angles in degrees - pub fn rotate(&mut self, euler: Vector3) { - // TODO - self.settings.rotation += euler; - - // Clamp -pi/2 < pitch < pi/2 - if self.settings.rotation.x > R89 { - self.settings.rotation.x = R89; - } else if self.settings.rotation.x <= -R89 { - self.settings.rotation.x = -R89; - } - - // -pi < yaw <= pi - if self.settings.rotation.y <= -R180 { - self.settings.rotation.y = R180 - self.settings.rotation.y % -R180; - } else if self.settings.rotation.y > 180.0 { - self.settings.rotation.y = -R180 + self.settings.rotation.y % R180; - } - - self.is_dirty = true; - } - - /// Move the camera by `delta`, relative to the camera's rotation - pub fn move_camera_relative(&mut self, delta: Vector3) { - let rot_matrix = Mat4::from_euler_angles( - -self.settings.rotation.x, - self.settings.rotation.y, - self.settings.rotation.z - ); - - let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0); - self.settings.position.x += new.x; - self.settings.position.y += new.y; - self.settings.position.z += new.z; - - self.is_dirty = true; - } - - pub fn camera_pos(&self) -> Vector3 { - self.settings.position - } + /// Return a camera with default settings + pub fn defaults(aspect_ratio: f32) -> WorkingCamera { + WorkingCamera::with_settings( + CameraSettings { + position: Vector3::new(0.0, 0.0, 0.0), + rotation: Vector3::new(0.0, R90, 0.0), + up: Vector3::new(0.0, 1.0, 0.0), + fov: f32::to_radians(90.0), + near: 0.1, + far: 1024.0, + }, + aspect_ratio, + ) + } + + /// Return a camera with the given settings + pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera { + WorkingCamera { + aspect_ratio, + settings, + vp_matrix: Mat4::identity(), + is_dirty: true, + } + } + + /// Get the VP matrix, updating cache if needed + pub fn get_matrix(&mut self) -> &Mat4 { + // Update matrix if needed + if self.is_dirty { + self.vp_matrix = self.calc_vp_matrix(); + self.is_dirty = false; + } + + // Return the matrix + &self.vp_matrix + } + + /// Returns a matrix that transforms from world space to screen space + fn calc_vp_matrix(&self) -> Matrix4 { + // Get look direction from euler angles + let direction = euler_to_direction(&self.settings.rotation); + + // Converts world space to camera space + let view_matrix = look_at_lh( + &self.settings.position, + &(self.settings.position + direction), + &self.settings.up, + ); + + // Converts camera space to screen space + let projection_matrix = { + let mut temp = perspective_lh_zo( + self.aspect_ratio, + self.settings.fov, + self.settings.near, + self.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 + } + + /// Update the aspect ratio + pub fn update_aspect_ratio(&mut self, new: f32) { + self.aspect_ratio = new; + self.is_dirty = true; + } + + /// Apply rotation of the camera + /// `euler` should be euler angles in degrees + pub fn rotate(&mut self, euler: Vector3) { + // TODO + self.settings.rotation += euler; + + // Clamp -pi/2 < pitch < pi/2 + if self.settings.rotation.x > R89 { + self.settings.rotation.x = R89; + } else if self.settings.rotation.x <= -R89 { + self.settings.rotation.x = -R89; + } + + // -pi < yaw <= pi + if self.settings.rotation.y <= -R180 { + self.settings.rotation.y = R180 - self.settings.rotation.y % -R180; + } else if self.settings.rotation.y > 180.0 { + self.settings.rotation.y = -R180 + self.settings.rotation.y % R180; + } + + self.is_dirty = true; + } + + /// Move the camera by `delta`, relative to the camera's rotation + pub fn move_camera_relative(&mut self, delta: Vector3) { + let rot_matrix = Mat4::from_euler_angles( + -self.settings.rotation.x, + self.settings.rotation.y, + self.settings.rotation.z, + ); + + let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0); + self.settings.position.x += new.x; + self.settings.position.y += new.y; + self.settings.position.z += new.z; + + self.is_dirty = true; + } + + pub fn camera_pos(&self) -> Vector3 { + self.settings.position + } } #[cfg(test)] mod tests { - use stockton_types::Matrix4; -use stockton_types::Vector3; - use draw::camera::WorkingCamera; + use draw::camera::WorkingCamera; + use stockton_types::Matrix4; + use stockton_types::Vector3; - fn contains_nan(mat: &Matrix4) -> bool{ - for x in mat.iter() { - if *x == std::f32::NAN { - return true; - } - } - return false; - } + fn contains_nan(mat: &Matrix4) -> bool { + for x in mat.iter() { + if (*x).is_nan() { + return true; + } + } + false + } - #[test] - fn camera_vp() { - let mut camera = WorkingCamera::defaults(16.0 / 9.0); + #[test] + fn camera_vp() { + let mut camera = WorkingCamera::defaults(16.0 / 9.0); - let old = camera.calc_vp_matrix(); - println!("initial vp matrix: {:?}", old); + let old = camera.calc_vp_matrix(); + println!("initial vp matrix: {:?}", old); - assert!(!contains_nan(&old), "No NaNs for initial matrix"); + assert!(!contains_nan(&old), "No NaNs for initial matrix"); - // Do a 180 - camera.rotate(Vector3::new(0.0, 180.0, 0.0)); + // Do a 180 + camera.rotate(Vector3::new(0.0, 180.0, 0.0)); - let new = camera.calc_vp_matrix(); - assert!(!contains_nan(&new), "No NaNs after rotating"); + let new = camera.calc_vp_matrix(); + assert!(!contains_nan(&new), "No NaNs after rotating"); - println!("new vp matrix: {:?}", new); + println!("new vp matrix: {:?}", new); - assert!(old != new, "VP Matrix changes when camera rotates"); - } + assert!(old != new, "VP Matrix changes when camera rotates"); + } } diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index ed5b5ec..3fff042 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -17,36 +17,29 @@ //! In the end, this takes in a depth-sorted list of faces and a map file and renders them. //! You'll need something else to actually find/sort the faces though. - use std::{ - mem::{ManuallyDrop, size_of}, - ops::Deref, - borrow::Borrow, - convert::TryInto + borrow::Borrow, + convert::TryInto, + mem::{size_of, ManuallyDrop}, + ops::Deref, }; use arrayvec::ArrayVec; -use hal::{ - prelude::*, - pool::CommandPoolCreateFlags -}; +use hal::{pool::CommandPoolCreateFlags, prelude::*}; use log::debug; use winit::window::Window; -use stockton_types::{Vector2, Vector3}; use stockton_levels::prelude::*; use stockton_levels::traits::faces::FaceType; +use stockton_types::{Vector2, Vector3}; -use crate::{ - types::*, - error -}; use super::{ - target::{TargetChain, SwapchainProperties}, - camera::WorkingCamera, - texture::TextureStore, - buffer::{StagedBuffer, ModifiableBuffer} + buffer::{ModifiableBuffer, StagedBuffer}, + camera::WorkingCamera, + target::{SwapchainProperties, TargetChain}, + texture::TextureStore, }; +use crate::{error, types::*}; /// Entry point name for shaders const ENTRY_NAME: &str = "main"; @@ -65,605 +58,697 @@ const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); /// Represents a point of a triangle, including UV and texture information. #[derive(Debug, Clone, Copy)] -pub struct UVPoint (pub Vector3, pub i32, pub Vector2); +pub struct UVPoint(pub Vector3, pub i32, pub Vector2); /// Contains all the hal related stuff. /// In the end, this takes in a depth-sorted list of faces and a map file and renders them. // TODO: Settings for clear colour, buffer sizes, etc pub struct RenderingContext<'a> { - // Parents for most of these things + // Parents for most of these things + /// Vulkan Instance + instance: ManuallyDrop<back::Instance>, - /// Vulkan Instance - instance: ManuallyDrop<back::Instance>, + /// Device we're using + device: ManuallyDrop<Device>, - /// Device we're using - device: ManuallyDrop<Device>, + /// Adapter we're using + adapter: Adapter, - /// Adapter we're using - adapter: Adapter, + // Render destination + /// Surface to draw to + surface: ManuallyDrop<Surface>, - // Render destination + /// Swapchain and stuff + target_chain: ManuallyDrop<TargetChain>, - /// Surface to draw to - surface: ManuallyDrop<Surface>, + // Pipeline + /// Our main render pass + renderpass: ManuallyDrop<RenderPass>, - /// Swapchain and stuff - target_chain: ManuallyDrop<TargetChain>, + /// The layout of our main graphics pipeline + pipeline_layout: ManuallyDrop<PipelineLayout>, - // Pipeline + /// Our main graphics pipeline + pipeline: ManuallyDrop<GraphicsPipeline>, - /// Our main render pass - renderpass: ManuallyDrop<RenderPass>, + // Command pool and buffers + /// The command pool used for our buffers + cmd_pool: ManuallyDrop<CommandPool>, - /// The layout of our main graphics pipeline - pipeline_layout: ManuallyDrop<PipelineLayout>, + /// The queue group our buffers belong to + queue_group: QueueGroup, - /// Our main graphics pipeline - pipeline: ManuallyDrop<GraphicsPipeline>, + /// Texture store + texture_store: ManuallyDrop<TextureStore>, - // Command pool and buffers + /// (Staged) Vertex Buffer + pub vert_buffer: ManuallyDrop<StagedBuffer<'a, UVPoint>>, - /// The command pool used for our buffers - cmd_pool: ManuallyDrop<CommandPool>, + /// (Staged) Index Buffer + pub index_buffer: ManuallyDrop<StagedBuffer<'a, (u16, u16, u16)>>, - /// The queue group our buffers belong to - queue_group: QueueGroup, + /// Our camera settings + camera: WorkingCamera, - /// Texture store - texture_store: ManuallyDrop<TextureStore>, + /// The vertex shader module + vs_module: ManuallyDrop<ShaderModule>, - /// (Staged) Vertex Buffer - pub vert_buffer: ManuallyDrop<StagedBuffer<'a, UVPoint>>, - - /// (Staged) Index Buffer - pub index_buffer: ManuallyDrop<StagedBuffer<'a, (u16, u16, u16)>>, - - /// Our camera settings - camera: WorkingCamera, - - /// The vertex shader module - vs_module: ManuallyDrop<ShaderModule>, - - /// The fragment shader module - fs_module: ManuallyDrop<ShaderModule> + /// The fragment shader module + fs_module: ManuallyDrop<ShaderModule>, } impl<'a> RenderingContext<'a> { - /// Create a new RenderingContext for the given window. - pub fn new<T: HasTextures>(window: &Window, file: &T) -> Result<Self, error::CreationError> { - // Create surface - let (instance, mut surface, mut adapters) = unsafe { - use hal::Instance; - - let instance = back::Instance::create("stockton", 1).map_err(|_| error::CreationError::WindowError)?; - let surface = instance.create_surface(window).map_err(|_| error::CreationError::WindowError)?; - let adapters = instance.enumerate_adapters(); - - (instance, surface, adapters) - }; - - // TODO: Properly figure out which adapter to use - let mut adapter = adapters.remove(0); - - // Device & Queue group - let (mut device, mut queue_group) = { - let family = adapter - .queue_families - .iter() - .find(|family| { - surface.supports_queue_family(family) && family.queue_type().supports_graphics() - }) - .unwrap(); - - let mut gpu = unsafe { - adapter - .physical_device - .open(&[(family, &[1.0])], hal::Features::empty()) - .unwrap() - }; - - (gpu.device, gpu.queue_groups.pop().unwrap()) - }; - - // Figure out what our swapchain will look like - let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface).map_err(|_| error::CreationError::BadSurface)?; - - debug!("Detected following swapchain properties: {:?}", swapchain_properties); - - // Command pool - let mut cmd_pool = unsafe { - device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) - }.map_err(|_| error::CreationError::OutOfMemoryError)?; - - // Renderpass - let renderpass = { - use hal::{ - pass::*, - pso::PipelineStage, - image::{Access, Layout}, - memory::Dependencies - }; - - let img_attachment = Attachment { - format: Some(swapchain_properties.format), - samples: 1, - ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), - stencil_ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare), - layouts: Layout::Undefined..Layout::Present - }; - - let depth_attachment = Attachment { - format: Some(swapchain_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 - }; - - let subpass = SubpassDesc { - colors: &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)), - inputs: &[], - resolves: &[], - preserves: &[] - }; - - let in_dependency = SubpassDependency { - flags: Dependencies::empty(), - passes: None..Some(0), - stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT.. - (PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS), - accesses: Access::empty() - ..(Access::COLOR_ATTACHMENT_READ - | Access::COLOR_ATTACHMENT_WRITE - | Access::DEPTH_STENCIL_ATTACHMENT_READ - | Access::DEPTH_STENCIL_ATTACHMENT_WRITE) - }; - - let out_dependency = SubpassDependency { - flags: Dependencies::empty(), - passes: Some(0)..None, - stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS.. - PipelineStage::COLOR_ATTACHMENT_OUTPUT, - accesses: (Access::COLOR_ATTACHMENT_READ - | Access::COLOR_ATTACHMENT_WRITE - | Access::DEPTH_STENCIL_ATTACHMENT_READ - | Access::DEPTH_STENCIL_ATTACHMENT_WRITE).. - Access::empty() - }; - - unsafe { device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[in_dependency, out_dependency]) } - .map_err(|_| error::CreationError::OutOfMemoryError)? - }; - - // Subpass - let subpass = hal::pass::Subpass { - index: 0, - main_pass: &renderpass - }; - - // Camera - // TODO: Settings - let ratio = swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32; - let camera = WorkingCamera::defaults(ratio); - - - // Vertex and index buffers - let (vert_buffer, index_buffer) = { - use hal::buffer::Usage; - - let vert = StagedBuffer::new(&mut device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE)?; - let index = StagedBuffer::new(&mut device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE)?; - - (vert, index) - }; - - // Texture store - let texture_store = TextureStore::new(&mut device, - &mut adapter, - &mut queue_group.queues[0], - &mut cmd_pool, file)?; - - let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); - descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref()); - - // Graphics pipeline - let (pipeline_layout, pipeline, vs_module, fs_module) = Self::create_pipeline(&mut device, swapchain_properties.extent, &subpass, descriptor_set_layouts)?; - - // Swapchain and associated resources - let target_chain = TargetChain::new(&mut device, &adapter, &mut surface, &renderpass, &mut cmd_pool, swapchain_properties, None).map_err(|e| error::CreationError::TargetChainCreationError (e) )?; - - Ok(RenderingContext { - instance: ManuallyDrop::new(instance), - surface: ManuallyDrop::new(surface), - - device: ManuallyDrop::new(device), - adapter, - queue_group, - - renderpass: ManuallyDrop::new(renderpass), - target_chain: ManuallyDrop::new(target_chain), - cmd_pool: ManuallyDrop::new(cmd_pool), - - pipeline_layout: ManuallyDrop::new(pipeline_layout), - pipeline: ManuallyDrop::new(pipeline), - - texture_store: ManuallyDrop::new(texture_store), - - vert_buffer: ManuallyDrop::new(vert_buffer), - index_buffer: ManuallyDrop::new(index_buffer), - - vs_module: ManuallyDrop::new(vs_module), - fs_module: ManuallyDrop::new(fs_module), - - camera - }) - } - - /// If this function fails the whole context is probably dead - /// The context must not be used while this is being called - pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> { - self.device.wait_idle().unwrap(); - - let properties = SwapchainProperties::find_best(&self.adapter, &self.surface).map_err(|_| error::CreationError::BadSurface)?; - - // Camera settings (aspect ratio) - self.camera.update_aspect_ratio( - properties.extent.width as f32 / - properties.extent.height as f32); - - use core::ptr::read; - - // Graphics pipeline - self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); - - self.device - .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - - self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); - self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); - - let (pipeline_layout, pipeline, vs_module, fs_module) = { - let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); - descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref()); - - let subpass = hal::pass::Subpass { - index: 0, - main_pass: &(*self.renderpass) - }; - - Self::create_pipeline(&mut self.device, properties.extent, &subpass, descriptor_set_layouts)? - }; - - self.pipeline_layout = ManuallyDrop::new(pipeline_layout); - self.pipeline = ManuallyDrop::new(pipeline); - - self.vs_module = ManuallyDrop::new(vs_module); - self.fs_module = ManuallyDrop::new(fs_module); - - let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)).deactivate_with_recyling(&mut self.device, &mut self.cmd_pool); - self.target_chain = ManuallyDrop::new(TargetChain::new(&mut self.device, &self.adapter, &mut self.surface, &self.renderpass, &mut self.cmd_pool, properties, Some(old_swapchain)) - .map_err(|e| error::CreationError::TargetChainCreationError (e) )?); - - Ok(()) - } - - #[allow(clippy::type_complexity)] - fn create_pipeline<T>(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass<back::Backend>, set_layouts: T) -> Result< - ( - PipelineLayout, - GraphicsPipeline, - ShaderModule, - ShaderModule - ), error::CreationError> where T: IntoIterator, T::Item: Borrow<DescriptorSetLayout> { - use hal::pso::*; - use hal::format::Format; - - // Shader modules - let (vs_module, fs_module) = { - let mut compiler = shaderc::Compiler::new().ok_or(error::CreationError::NoShaderC)?; - - let vertex_compile_artifact = compiler - .compile_into_spirv(VERTEX_SOURCE, shaderc::ShaderKind::Vertex, "vertex.vert", ENTRY_NAME, None) - .map_err(|e| error::CreationError::ShaderCError (e))?; - - let fragment_compile_artifact = compiler - .compile_into_spirv(FRAGMENT_SOURCE, shaderc::ShaderKind::Fragment, "fragment.frag", ENTRY_NAME, None) - .map_err(|e| error::CreationError::ShaderCError (e))?; - - // Make into shader module - unsafe { - (device - .create_shader_module(vertex_compile_artifact.as_binary()) - .map_err(|e| error::CreationError::ShaderModuleFailed (e))?, - device - .create_shader_module(fragment_compile_artifact.as_binary()) - .map_err(|e| error::CreationError::ShaderModuleFailed (e))?) - } - }; - - // Shader entry points (ShaderStage) - let (vs_entry, fs_entry) = ( - EntryPoint::<back::Backend> { - entry: ENTRY_NAME, - module: &vs_module, - specialization: Specialization::default() - }, - EntryPoint::<back::Backend> { - entry: ENTRY_NAME, - module: &fs_module, - specialization: Specialization::default() - } - ); - - // Shader set - let shaders = GraphicsShaderSet { - vertex: vs_entry, - fragment: Some(fs_entry), - hull: None, - domain: None, - geometry: None - }; - - // Vertex buffers - let vertex_buffers: Vec<VertexBufferDesc> = vec![VertexBufferDesc { - binding: 0, - stride: (size_of::<f32>() * 6) as u32, - rate: VertexInputRate::Vertex, - }]; - - let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0, - size_of::<f32>() * 3; Rgb32Sfloat, - size_of::<u32>(); R32Sint, - size_of::<f32>() * 2; Rg32Sfloat - ); - - // Rasterizer - let rasterizer = Rasterizer { - polygon_mode: PolygonMode::Fill, - cull_face: Face::BACK, - front_face: FrontFace::CounterClockwise, - depth_clamping: false, - depth_bias: None, - conservative: true, - line_width: hal::pso::State::Static(1.0) - }; - - // Depth stencil - let depth_stencil = DepthStencilDesc { - depth: Some(DepthTest { - fun: Comparison::Less, - write: true - }), - depth_bounds: false, - stencil: None, - }; - - // Pipeline layout - let layout = unsafe { - device.create_pipeline_layout( - set_layouts, - // vp matrix, 4x4 f32 - &[(ShaderStageFlags::VERTEX, 0..64)] - ) - }.map_err(|_| error::CreationError::OutOfMemoryError)?; - - // Colour blending - let blender = { - let blend_state = BlendState { - color: BlendOp::Add { - src: Factor::One, - dst: Factor::Zero, - }, - alpha: BlendOp::Add { - src: Factor::One, - dst: Factor::Zero, - }, - }; - - BlendDesc { - logic_op: Some(LogicOp::Copy), - targets: vec![ColorBlendDesc { mask: ColorMask::ALL, blend: Some(blend_state) }], - } - }; - - // Baked states - let baked_states = BakedStates { - viewport: Some(Viewport { - rect: extent.rect(), - depth: (0.0..1.0) - }), - scissor: Some(extent.rect()), - blend_color: None, - depth_bounds: None, - }; - - // Input assembler - let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList); - - // Pipeline description - let pipeline_desc = GraphicsPipelineDesc { - shaders, - rasterizer, - vertex_buffers, - blender, - depth_stencil, - multisampling: None, - baked_states, - layout: &layout, - subpass: *subpass, - flags: PipelineCreationFlags::empty(), - parent: BasePipeline::None, - input_assembler, - attributes - }; - - // Pipeline - let pipeline = unsafe { - device.create_graphics_pipeline(&pipeline_desc, None) - }.map_err(|e| error::CreationError::PipelineError (e))?; - - Ok((layout, pipeline, vs_module, fs_module)) - } - - /// Draw all vertices in the buffer - pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>(&mut self, file: &M,faces: &Vec<u32>) -> Result<(), &'static str> { - // Prepare command buffer - let cmd_buffer = self.target_chain.prep_next_target( - &mut self.device, - self.vert_buffer.get_buffer(), - self.index_buffer.get_buffer(), - &self.renderpass, - &self.pipeline, - &self.pipeline_layout, - &mut self.camera - )?; - - // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. - let mut current_chunk = file.get_face(0).texture_idx as usize / 8; - let mut chunk_start = 0; - - let mut curr_vert_idx: usize = 0; - let mut curr_idx_idx: usize = 0; - - for face in faces.into_iter().map(|idx| file.get_face(*idx)) { - if current_chunk != face.texture_idx as usize / 8 { - // Last index was last of group, so draw it all. - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); - unsafe { - - cmd_buffer.bind_graphics_descriptor_sets( - &self.pipeline_layout, - 0, - descriptor_sets, - &[] - ); - cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1); - } - - // Next group of same-chunked faces starts here. - chunk_start = curr_idx_idx; - current_chunk = face.texture_idx as usize / 8; - } - - if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh { - // 2 layers of indirection - let base = face.vertices_idx.start; - - for idx in face.meshverts_idx.clone().step_by(3) { - let start_idx: u16 = curr_vert_idx.try_into().unwrap(); - - for idx2 in idx..idx+3 { - let vert = &file.resolve_meshvert(idx2 as u32, base); - let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]); - - let uvp = UVPoint (vert.position, face.texture_idx.try_into().unwrap(), uv); - self.vert_buffer[curr_vert_idx] = uvp; - - curr_vert_idx += 1; - } - - - self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2); - - curr_idx_idx += 1; - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() { - println!("out of vertex buffer space!"); - break; - } - } - } else { - // TODO: Other types of faces - } - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() { - println!("out of vertex buffer space!"); - break; - } - } - - // Draw the final group of chunks - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); - unsafe { - cmd_buffer.bind_graphics_descriptor_sets( - &self.pipeline_layout, - 0, - descriptor_sets, - &[] - ); - cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1); - } - - // Update our buffers before we actually start drawing - self.vert_buffer.commit( - &self.device, - &mut self.queue_group.queues[0], - &mut self.cmd_pool - ); - - self.index_buffer.commit( - &self.device, - &mut self.queue_group.queues[0], - &mut self.cmd_pool - ); - - // Send commands off to GPU - self.target_chain.finish_and_submit_target(&mut self.queue_group.queues[0])?; - - Ok(()) - } - - /// Get current position of camera - pub fn camera_pos(&self) -> Vector3 { - self.camera.camera_pos() - } - - /// Move the camera by `delta` relative to its rotation - pub fn move_camera_relative(&mut self, delta: Vector3) { - self.camera.move_camera_relative(delta) - } - - /// Rotate the camera - /// `euler` should be euler angles in radians - pub fn rotate(&mut self, euler: Vector3) { - self.camera.rotate(euler) - } + /// Create a new RenderingContext for the given window. + pub fn new<T: HasTextures>(window: &Window, file: &T) -> Result<Self, error::CreationError> { + // Create surface + let (instance, mut surface, mut adapters) = unsafe { + use hal::Instance; + + let instance = back::Instance::create("stockton", 1) + .map_err(|_| error::CreationError::WindowError)?; + let surface = instance + .create_surface(window) + .map_err(|_| error::CreationError::WindowError)?; + let adapters = instance.enumerate_adapters(); + + (instance, surface, adapters) + }; + + // TODO: Properly figure out which adapter to use + let mut adapter = adapters.remove(0); + + // Device & Queue group + let (mut device, mut queue_group) = { + let family = adapter + .queue_families + .iter() + .find(|family| { + surface.supports_queue_family(family) && family.queue_type().supports_graphics() + }) + .unwrap(); + + let mut gpu = unsafe { + adapter + .physical_device + .open(&[(family, &[1.0])], hal::Features::empty()) + .unwrap() + }; + + (gpu.device, gpu.queue_groups.pop().unwrap()) + }; + + // Figure out what our swapchain will look like + let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface) + .map_err(|_| error::CreationError::BadSurface)?; + + debug!( + "Detected following swapchain properties: {:?}", + swapchain_properties + ); + + // Command pool + let mut cmd_pool = unsafe { + device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) + } + .map_err(|_| error::CreationError::OutOfMemoryError)?; + + // Renderpass + let renderpass = { + use hal::{ + image::{Access, Layout}, + memory::Dependencies, + pass::*, + pso::PipelineStage, + }; + + let img_attachment = Attachment { + format: Some(swapchain_properties.format), + samples: 1, + ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), + stencil_ops: AttachmentOps::new( + AttachmentLoadOp::Clear, + AttachmentStoreOp::DontCare, + ), + layouts: Layout::Undefined..Layout::Present, + }; + + let depth_attachment = Attachment { + format: Some(swapchain_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, + }; + + let subpass = SubpassDesc { + colors: &[(0, Layout::ColorAttachmentOptimal)], + depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)), + inputs: &[], + resolves: &[], + preserves: &[], + }; + + let in_dependency = SubpassDependency { + flags: Dependencies::empty(), + passes: None..Some(0), + stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT + ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT + | PipelineStage::EARLY_FRAGMENT_TESTS), + accesses: Access::empty() + ..(Access::COLOR_ATTACHMENT_READ + | Access::COLOR_ATTACHMENT_WRITE + | Access::DEPTH_STENCIL_ATTACHMENT_READ + | Access::DEPTH_STENCIL_ATTACHMENT_WRITE), + }; + + let out_dependency = SubpassDependency { + flags: Dependencies::empty(), + passes: Some(0)..None, + stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS + ..PipelineStage::COLOR_ATTACHMENT_OUTPUT, + accesses: (Access::COLOR_ATTACHMENT_READ + | Access::COLOR_ATTACHMENT_WRITE + | Access::DEPTH_STENCIL_ATTACHMENT_READ + | Access::DEPTH_STENCIL_ATTACHMENT_WRITE) + ..Access::empty(), + }; + + unsafe { + device.create_render_pass( + &[img_attachment, depth_attachment], + &[subpass], + &[in_dependency, out_dependency], + ) + } + .map_err(|_| error::CreationError::OutOfMemoryError)? + }; + + // Subpass + let subpass = hal::pass::Subpass { + index: 0, + main_pass: &renderpass, + }; + + // Camera + // TODO: Settings + let ratio = + swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32; + let camera = WorkingCamera::defaults(ratio); + + // Vertex and index buffers + let (vert_buffer, index_buffer) = { + use hal::buffer::Usage; + + let vert = StagedBuffer::new(&mut device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE)?; + let index = StagedBuffer::new(&mut device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE)?; + + (vert, index) + }; + + // Texture store + let texture_store = TextureStore::new( + &mut device, + &mut adapter, + &mut queue_group.queues[0], + &mut cmd_pool, + file, + )?; + + let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); + descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref()); + + // Graphics pipeline + let (pipeline_layout, pipeline, vs_module, fs_module) = Self::create_pipeline( + &mut device, + swapchain_properties.extent, + &subpass, + descriptor_set_layouts, + )?; + + // Swapchain and associated resources + let target_chain = TargetChain::new( + &mut device, + &adapter, + &mut surface, + &renderpass, + &mut cmd_pool, + swapchain_properties, + None, + ) + .map_err(error::CreationError::TargetChainCreationError)?; + + Ok(RenderingContext { + instance: ManuallyDrop::new(instance), + surface: ManuallyDrop::new(surface), + + device: ManuallyDrop::new(device), + adapter, + queue_group, + + renderpass: ManuallyDrop::new(renderpass), + target_chain: ManuallyDrop::new(target_chain), + cmd_pool: ManuallyDrop::new(cmd_pool), + + pipeline_layout: ManuallyDrop::new(pipeline_layout), + pipeline: ManuallyDrop::new(pipeline), + + texture_store: ManuallyDrop::new(texture_store), + + vert_buffer: ManuallyDrop::new(vert_buffer), + index_buffer: ManuallyDrop::new(index_buffer), + + vs_module: ManuallyDrop::new(vs_module), + fs_module: ManuallyDrop::new(fs_module), + + camera, + }) + } + + /// If this function fails the whole context is probably dead + /// # Safety + /// The context must not be used while this is being called + pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> { + self.device.wait_idle().unwrap(); + + let properties = SwapchainProperties::find_best(&self.adapter, &self.surface) + .map_err(|_| error::CreationError::BadSurface)?; + + // Camera settings (aspect ratio) + self.camera + .update_aspect_ratio(properties.extent.width as f32 / properties.extent.height as f32); + + use core::ptr::read; + + // Graphics pipeline + self.device + .destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); + + self.device + .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); + + self.device + .destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); + self.device + .destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); + + let (pipeline_layout, pipeline, vs_module, fs_module) = { + let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); + descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref()); + + let subpass = hal::pass::Subpass { + index: 0, + main_pass: &(*self.renderpass), + }; + + Self::create_pipeline( + &mut self.device, + properties.extent, + &subpass, + descriptor_set_layouts, + )? + }; + + self.pipeline_layout = ManuallyDrop::new(pipeline_layout); + self.pipeline = ManuallyDrop::new(pipeline); + + self.vs_module = ManuallyDrop::new(vs_module); + self.fs_module = ManuallyDrop::new(fs_module); + + let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)) + .deactivate_with_recyling(&mut self.device, &mut self.cmd_pool); + self.target_chain = ManuallyDrop::new( + TargetChain::new( + &mut self.device, + &self.adapter, + &mut self.surface, + &self.renderpass, + &mut self.cmd_pool, + properties, + Some(old_swapchain), + ) + .map_err(error::CreationError::TargetChainCreationError)?, + ); + + Ok(()) + } + + #[allow(clippy::type_complexity)] + fn create_pipeline<T>( + device: &mut Device, + extent: hal::image::Extent, + subpass: &hal::pass::Subpass<back::Backend>, + set_layouts: T, + ) -> Result<(PipelineLayout, GraphicsPipeline, ShaderModule, ShaderModule), error::CreationError> + where + T: IntoIterator, + T::Item: Borrow<DescriptorSetLayout>, + { + use hal::format::Format; + use hal::pso::*; + + // Shader modules + let (vs_module, fs_module) = { + let mut compiler = shaderc::Compiler::new().ok_or(error::CreationError::NoShaderC)?; + + let vertex_compile_artifact = compiler + .compile_into_spirv( + VERTEX_SOURCE, + shaderc::ShaderKind::Vertex, + "vertex.vert", + ENTRY_NAME, + None, + ) + .map_err(error::CreationError::ShaderCError)?; + + let fragment_compile_artifact = compiler + .compile_into_spirv( + FRAGMENT_SOURCE, + shaderc::ShaderKind::Fragment, + "fragment.frag", + ENTRY_NAME, + None, + ) + .map_err(error::CreationError::ShaderCError)?; + + // Make into shader module + unsafe { + ( + device + .create_shader_module(vertex_compile_artifact.as_binary()) + .map_err(error::CreationError::ShaderModuleFailed)?, + device + .create_shader_module(fragment_compile_artifact.as_binary()) + .map_err(error::CreationError::ShaderModuleFailed)?, + ) + } + }; + + // Shader entry points (ShaderStage) + let (vs_entry, fs_entry) = ( + EntryPoint::<back::Backend> { + entry: ENTRY_NAME, + module: &vs_module, + specialization: Specialization::default(), + }, + EntryPoint::<back::Backend> { + entry: ENTRY_NAME, + module: &fs_module, + specialization: Specialization::default(), + }, + ); + + // Shader set + let shaders = GraphicsShaderSet { + vertex: vs_entry, + fragment: Some(fs_entry), + hull: None, + domain: None, + geometry: None, + }; + + // Vertex buffers + let vertex_buffers: Vec<VertexBufferDesc> = vec![VertexBufferDesc { + binding: 0, + stride: (size_of::<f32>() * 6) as u32, + rate: VertexInputRate::Vertex, + }]; + + let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0, + size_of::<f32>() * 3; Rgb32Sfloat, + size_of::<u32>(); R32Sint, + size_of::<f32>() * 2; Rg32Sfloat + ); + + // Rasterizer + let rasterizer = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::BACK, + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: true, + line_width: hal::pso::State::Static(1.0), + }; + + // Depth stencil + let depth_stencil = DepthStencilDesc { + depth: Some(DepthTest { + fun: Comparison::Less, + write: true, + }), + depth_bounds: false, + stencil: None, + }; + + // Pipeline layout + let layout = unsafe { + device.create_pipeline_layout( + set_layouts, + // vp matrix, 4x4 f32 + &[(ShaderStageFlags::VERTEX, 0..64)], + ) + } + .map_err(|_| error::CreationError::OutOfMemoryError)?; + + // Colour blending + let blender = { + let blend_state = BlendState { + color: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + alpha: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + }; + + BlendDesc { + logic_op: Some(LogicOp::Copy), + targets: vec![ColorBlendDesc { + mask: ColorMask::ALL, + blend: Some(blend_state), + }], + } + }; + + // Baked states + let baked_states = BakedStates { + viewport: Some(Viewport { + rect: extent.rect(), + depth: (0.0..1.0), + }), + scissor: Some(extent.rect()), + blend_color: None, + depth_bounds: None, + }; + + // Input assembler + let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList); + + // Pipeline description + let pipeline_desc = GraphicsPipelineDesc { + shaders, + rasterizer, + vertex_buffers, + blender, + depth_stencil, + multisampling: None, + baked_states, + layout: &layout, + subpass: *subpass, + flags: PipelineCreationFlags::empty(), + parent: BasePipeline::None, + input_assembler, + attributes, + }; + + // Pipeline + let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } + .map_err(error::CreationError::PipelineError)?; + + Ok((layout, pipeline, vs_module, fs_module)) + } + + /// Draw all vertices in the buffer + pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>( + &mut self, + file: &M, + faces: &[u32], + ) -> Result<(), &'static str> { + // Prepare command buffer + let cmd_buffer = self.target_chain.prep_next_target( + &mut self.device, + self.vert_buffer.get_buffer(), + self.index_buffer.get_buffer(), + &self.renderpass, + &self.pipeline, + &self.pipeline_layout, + &mut self.camera, + )?; + + // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. + let mut current_chunk = file.get_face(0).texture_idx as usize / 8; + let mut chunk_start = 0; + + let mut curr_vert_idx: usize = 0; + let mut curr_idx_idx: usize = 0; + + for face in faces.iter().map(|idx| file.get_face(*idx)) { + if current_chunk != face.texture_idx as usize / 8 { + // Last index was last of group, so draw it all. + let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); + descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); + unsafe { + cmd_buffer.bind_graphics_descriptor_sets( + &self.pipeline_layout, + 0, + descriptor_sets, + &[], + ); + cmd_buffer.draw_indexed( + chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, + 0, + 0..1, + ); + } + + // Next group of same-chunked faces starts here. + chunk_start = curr_idx_idx; + current_chunk = face.texture_idx as usize / 8; + } + + if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh { + // 2 layers of indirection + let base = face.vertices_idx.start; + + for idx in face.meshverts_idx.clone().step_by(3) { + let start_idx: u16 = curr_vert_idx.try_into().unwrap(); + + for idx2 in idx..idx + 3 { + let vert = &file.resolve_meshvert(idx2 as u32, base); + let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]); + + let uvp = UVPoint(vert.position, face.texture_idx.try_into().unwrap(), uv); + self.vert_buffer[curr_vert_idx] = uvp; + + curr_vert_idx += 1; + } + + self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2); + + curr_idx_idx += 1; + + if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() + || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() + { + println!("out of vertex buffer space!"); + break; + } + } + } else { + // TODO: Other types of faces + } + + if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() + || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() + { + println!("out of vertex buffer space!"); + break; + } + } + + // Draw the final group of chunks + let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); + descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); + unsafe { + cmd_buffer.bind_graphics_descriptor_sets( + &self.pipeline_layout, + 0, + descriptor_sets, + &[], + ); + cmd_buffer.draw_indexed( + chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, + 0, + 0..1, + ); + } + + // Update our buffers before we actually start drawing + self.vert_buffer.commit( + &self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool, + ); + + self.index_buffer.commit( + &self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool, + ); + + // Send commands off to GPU + self.target_chain + .finish_and_submit_target(&mut self.queue_group.queues[0])?; + + Ok(()) + } + + /// Get current position of camera + pub fn camera_pos(&self) -> Vector3 { + self.camera.camera_pos() + } + + /// Move the camera by `delta` relative to its rotation + pub fn move_camera_relative(&mut self, delta: Vector3) { + self.camera.move_camera_relative(delta) + } + + /// Rotate the camera + /// `euler` should be euler angles in radians + pub fn rotate(&mut self, euler: Vector3) { + self.camera.rotate(euler) + } } impl<'a> core::ops::Drop for RenderingContext<'a> { - fn drop(&mut self) { - self.device.wait_idle().unwrap(); - - unsafe { - use core::ptr::read; - - ManuallyDrop::into_inner(read(&self.vert_buffer)).deactivate(&mut self.device); - ManuallyDrop::into_inner(read(&self.index_buffer)).deactivate(&mut self.device); - ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device); - - ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(&mut self.device, &mut self.cmd_pool); - - self.device.destroy_command_pool( - ManuallyDrop::into_inner(read(&self.cmd_pool)), - ); - self.device - .destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); - - self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); - self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); - - self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); - - self.device - .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - - self.instance - .destroy_surface(ManuallyDrop::into_inner(read(&self.surface))); - - ManuallyDrop::drop(&mut self.device); - } - } -}
\ No newline at end of file + fn drop(&mut self) { + self.device.wait_idle().unwrap(); + + unsafe { + use core::ptr::read; + + ManuallyDrop::into_inner(read(&self.vert_buffer)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.index_buffer)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device); + + ManuallyDrop::into_inner(read(&self.target_chain)) + .deactivate(&mut self.device, &mut self.cmd_pool); + + self.device + .destroy_command_pool(ManuallyDrop::into_inner(read(&self.cmd_pool))); + self.device + .destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); + + self.device + .destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); + self.device + .destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); + + self.device + .destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); + + self.device + .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); + + self.instance + .destroy_surface(ManuallyDrop::into_inner(read(&self.surface))); + + ManuallyDrop::drop(&mut self.device); + } + } +} diff --git a/stockton-render/src/draw/macros.rs b/stockton-render/src/draw/macros.rs index d191d5b..65566a2 100644 --- a/stockton-render/src/draw/macros.rs +++ b/stockton-render/src/draw/macros.rs @@ -19,10 +19,10 @@ /// ``` /// // 0 is the binding value /// let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0, -/// size_of::<f32>() * 3; Rgb32Sfloat -/// size_of::<f32>() * 2; Rg32Sfloat, -/// size_of::<u32>(); R32Sint -/// ); +/// size_of::<f32>() * 3; Rgb32Sfloat +/// size_of::<f32>() * 2; Rg32Sfloat, +/// size_of::<u32>(); R32Sint +/// ); /// ``` /// See the hal::pso::Format enum for possible types macro_rules! pipeline_vb_attributes { @@ -98,4 +98,4 @@ macro_rules! pipeline_vb_attributes { } }); }); -}
\ No newline at end of file +} diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index af86b46..1151192 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -19,10 +19,10 @@ pub mod target; #[macro_use] mod macros; -mod context; mod buffer; -mod texture; mod camera; +mod context; +mod texture; pub use self::context::RenderingContext; pub use self::context::UVPoint; diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index 3d0ecdf..5e0ade0 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -14,394 +14,498 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Resources needed for drawing on the screen, including sync objects +use super::{camera::WorkingCamera, texture::image::LoadedImage}; use crate::types::*; -use super::{ - camera::WorkingCamera, - texture::image::LoadedImage -}; -use core::{ - iter::once, - mem::ManuallyDrop -}; +use core::{iter::once, mem::ManuallyDrop}; use arrayvec::ArrayVec; use hal::{ - prelude::*, - queue::Submission, - pso::Viewport, - format::{ChannelType, Format, Swizzle}, - image::{Extent, ViewKind, Usage as ImgUsage}, - window::{CompositeAlphaMode, PresentMode, SwapchainConfig, Extent2D} + format::{ChannelType, Format, Swizzle}, + image::{Extent, Usage as ImgUsage, ViewKind}, + prelude::*, + pso::Viewport, + queue::Submission, + window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig}, }; /// Defines the colour range we use. const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: 0..1, - layers: 0..1, + aspects: hal::format::Aspects::COLOR, + levels: 0..1, + layers: 0..1, }; #[derive(Debug, Clone)] pub struct SwapchainProperties { - pub format: Format, - pub depth_format: Format, - pub present_mode: PresentMode, - pub composite_alpha_mode: CompositeAlphaMode, - pub viewport: Viewport, - pub extent: Extent + pub format: Format, + pub depth_format: Format, + pub present_mode: PresentMode, + pub composite_alpha_mode: CompositeAlphaMode, + pub viewport: Viewport, + pub extent: Extent, } impl SwapchainProperties { - pub fn find_best(adapter: &Adapter, surface: &Surface) -> Result<SwapchainProperties, ()> { - let caps = surface.capabilities(&adapter.physical_device); - let formats = surface.supported_formats(&adapter.physical_device); - - // Find which settings we'll actually use based on preset preferences - let format = formats.clone().map_or(Format::Rgba8Srgb, |formats| { - formats.iter() - .find(|format| format.base_format().1 == ChannelType::Srgb) - .map(|format| *format) - .unwrap_or(formats[0]) - }); - - let depth_format = *[Format::D32SfloatS8Uint, Format::D24UnormS8Uint, Format::D32Sfloat].iter().find(|format| { - use hal::format::ImageFeature; - - format.is_depth() && adapter.physical_device.format_properties(Some(**format)).optimal_tiling.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT) - }).ok_or_else(|| ())?; - - let present_mode = { - [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] - .iter() - .cloned() - .find(|pm| caps.present_modes.contains(*pm)) - .ok_or(())? - }; - let composite_alpha_mode = { - [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] - .iter() - .cloned() - .find(|ca| caps.composite_alpha_modes.contains(*ca)) - .ok_or(())? - }; - - let extent = caps.extents.end().to_extent(); // Size - let viewport = Viewport { - rect: extent.rect(), - depth: 0.0..1.0 - }; - - Ok(SwapchainProperties {format, depth_format, present_mode, composite_alpha_mode, extent, viewport}) - } + pub fn find_best(adapter: &Adapter, surface: &Surface) -> Result<SwapchainProperties, ()> { + let caps = surface.capabilities(&adapter.physical_device); + let formats = surface.supported_formats(&adapter.physical_device); + + // Find which settings we'll actually use based on preset preferences + let format = formats.map_or(Format::Rgba8Srgb, |formats| { + formats + .iter() + .find(|format| format.base_format().1 == ChannelType::Srgb) + .copied() + .unwrap_or(formats[0]) + }); + + let depth_format = *[ + Format::D32SfloatS8Uint, + Format::D24UnormS8Uint, + Format::D32Sfloat, + ] + .iter() + .find(|format| { + use hal::format::ImageFeature; + + format.is_depth() + && adapter + .physical_device + .format_properties(Some(**format)) + .optimal_tiling + .contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT) + }) + .ok_or_else(|| ())?; + + let present_mode = { + [ + PresentMode::MAILBOX, + PresentMode::FIFO, + PresentMode::RELAXED, + PresentMode::IMMEDIATE, + ] + .iter() + .cloned() + .find(|pm| caps.present_modes.contains(*pm)) + .ok_or(())? + }; + let composite_alpha_mode = { + [ + CompositeAlphaMode::OPAQUE, + CompositeAlphaMode::INHERIT, + CompositeAlphaMode::PREMULTIPLIED, + CompositeAlphaMode::POSTMULTIPLIED, + ] + .iter() + .cloned() + .find(|ca| caps.composite_alpha_modes.contains(*ca)) + .ok_or(())? + }; + + let extent = caps.extents.end().to_extent(); // Size + let viewport = Viewport { + rect: extent.rect(), + depth: 0.0..1.0, + }; + + Ok(SwapchainProperties { + format, + depth_format, + present_mode, + composite_alpha_mode, + extent, + viewport, + }) + } } pub struct TargetChain { - /// Swapchain we're targeting - pub swapchain: ManuallyDrop<Swapchain>, + /// Swapchain we're targeting + pub swapchain: ManuallyDrop<Swapchain>, - pub properties: SwapchainProperties, + pub properties: SwapchainProperties, - /// The depth buffer/image used for drawing - pub depth_buffer: ManuallyDrop<LoadedImage>, + /// The depth buffer/image used for drawing + pub depth_buffer: ManuallyDrop<LoadedImage>, - /// Resources tied to each target frame in the swapchain - pub targets: Box<[TargetResources]>, + /// Resources tied to each target frame in the swapchain + pub targets: Box<[TargetResources]>, - /// The last target drawn to - last_drawn: usize, + /// The last target drawn to + last_drawn: usize, - /// Last image index of the swapchain drawn to - last_image_index: u32, + /// Last image index of the swapchain drawn to + last_image_index: u32, } impl TargetChain { - pub fn new(device: &mut Device, adapter: &Adapter, surface: &mut Surface, renderpass: &RenderPass, cmd_pool: &mut CommandPool, properties: SwapchainProperties, old_swapchain: Option<Swapchain>) -> Result<TargetChain, TargetChainCreationError> { - let caps = surface.capabilities(&adapter.physical_device); - - // Number of frames to pre-render - let image_count = if properties.present_mode == PresentMode::MAILBOX { - ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) - } else { - ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) - }; - - // Swap config - let swap_config = SwapchainConfig { - present_mode: properties.present_mode, - composite_alpha_mode: properties.composite_alpha_mode, - format: properties.format, - extent: Extent2D { width: properties.extent.width, height: properties.extent.height }, - image_count, - image_layers: 1, - image_usage: ImgUsage::COLOR_ATTACHMENT, - }; - - // Swapchain - let (swapchain, mut backbuffer) = unsafe { - device.create_swapchain(surface, swap_config, old_swapchain) - .map_err(|_| TargetChainCreationError::Todo)? - }; - - let depth_buffer: LoadedImage = { - use hal::image::SubresourceRange; - use hal::format::Aspects; - - LoadedImage::new(device, adapter, properties.depth_format, ImgUsage::DEPTH_STENCIL_ATTACHMENT, SubresourceRange { - aspects: Aspects::DEPTH, - levels: 0..1, - layers: 0..1, - },properties.extent.width as usize, properties.extent.height as usize).map_err(|_| TargetChainCreationError::Todo) - }?; - - let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len()); - for image in backbuffer.drain(..) { - targets.push(TargetResources::new(device, cmd_pool, renderpass, image, &(*depth_buffer.image_view), properties.extent, properties.format) - .map_err(|_| TargetChainCreationError::Todo)?); - } - - Ok(TargetChain { - swapchain: ManuallyDrop::new(swapchain), - targets: targets.into_boxed_slice(), - depth_buffer: ManuallyDrop::new(depth_buffer), - properties, - last_drawn: (image_count - 1) as usize, // This means the next one to be used is index 0 - last_image_index: 0 - }) - } - - pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () { - use core::ptr::read; - unsafe { - ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); - - for i in 0..self.targets.len() { - read(&self.targets[i]).deactivate(device, cmd_pool); - } - - device.destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); - } - } - - pub fn deactivate_with_recyling(self, device: &mut Device, cmd_pool: &mut CommandPool) -> Swapchain { - use core::ptr::read; - unsafe { - ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); - - for i in 0..self.targets.len() { - read(&self.targets[i]).deactivate(device, cmd_pool); - } - } - - return unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) }; - } - - pub fn prep_next_target<'a>(&'a mut self, device: &mut Device, vert_buffer: &Buffer, index_buffer: &Buffer, renderpass: &RenderPass, pipeline: &GraphicsPipeline, pipeline_layout: &PipelineLayout, camera: &mut WorkingCamera) -> Result<&'a mut crate::types::CommandBuffer, &'static str> { - self.last_drawn = (self.last_drawn + 1) % self.targets.len(); - - let target = &mut self.targets[self.last_drawn]; - - // Get the image - let (image_index, _) = unsafe { - self.swapchain - .acquire_image(core::u64::MAX, Some(&target.get_image), None) - .map_err(|_| "FrameError::AcquireError")? - }; - - self.last_image_index = image_index; - - // Make sure whatever was last using this has finished - unsafe { - device - .wait_for_fence(&target.present_complete, core::u64::MAX) - .map_err(|_| "FrameError::SyncObjectError")?; - device - .reset_fence(&target.present_complete) - .map_err(|_| "FrameError::SyncObjectError")?; - }; - - // Record commands - unsafe { - use hal::buffer::{IndexBufferView, SubRange}; - use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor, ClearDepthStencil}; - use hal::pso::ShaderStageFlags; - - // Colour to clear window to - let clear_values = [ClearValue { - color: ClearColor { - float32: [0.0, 0.0, 0.0, 1.0] - } - }, ClearValue { - depth_stencil: ClearDepthStencil { - depth: 1.0, - stencil: 0 - } - }]; - - // Get references to our buffers - let (vbufs, ibuf) = { - let vbufref: &<back::Backend as hal::Backend>::Buffer = vert_buffer; - - let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); - let ibuf = index_buffer; - - (vbufs, ibuf) - }; - - target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY); - // Main render pass / pipeline - target.cmd_buffer.begin_render_pass( - renderpass, - &target.framebuffer, - self.properties.viewport.rect, - clear_values.iter(), - SubpassContents::Inline - ); - target.cmd_buffer.bind_graphics_pipeline(&pipeline); - - // VP Matrix - let vp = camera.get_matrix().as_slice(); - let vp = std::mem::transmute::<&[f32], &[u32]>(vp); - - target.cmd_buffer.push_graphics_constants( - &pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - vp); - - // Bind buffers - target.cmd_buffer.bind_vertex_buffers(0, vbufs); - target.cmd_buffer.bind_index_buffer(IndexBufferView { - buffer: ibuf, - range: SubRange::WHOLE, - index_type: hal::IndexType::U16 - }); - }; - - Ok(&mut target.cmd_buffer) - } - - pub fn finish_and_submit_target(&mut self, command_queue: &mut CommandQueue) -> Result<(), &'static str> { - let target = &mut self.targets[self.last_drawn]; - - unsafe { - target.cmd_buffer.end_render_pass(); - target.cmd_buffer.finish(); - } - - // Make submission object - let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer); - let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once((&target.get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)); - let signal_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete); - - let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete); - - let submission = Submission { - command_buffers, - wait_semaphores, - signal_semaphores, - }; - - // Submit it - unsafe { - command_queue.submit(submission, Some(&target.present_complete)); - self.swapchain - .present(command_queue, self.last_image_index as u32, present_wait_semaphores) - .map_err(|_| "FrameError::PresentError")?; - }; - - // TODO - Ok(()) - } + pub fn new( + device: &mut Device, + adapter: &Adapter, + surface: &mut Surface, + renderpass: &RenderPass, + cmd_pool: &mut CommandPool, + properties: SwapchainProperties, + old_swapchain: Option<Swapchain>, + ) -> Result<TargetChain, TargetChainCreationError> { + let caps = surface.capabilities(&adapter.physical_device); + + // Number of frames to pre-render + let image_count = if properties.present_mode == PresentMode::MAILBOX { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) + } else { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) + }; + + // Swap config + let swap_config = SwapchainConfig { + present_mode: properties.present_mode, + composite_alpha_mode: properties.composite_alpha_mode, + format: properties.format, + extent: Extent2D { + width: properties.extent.width, + height: properties.extent.height, + }, + image_count, + image_layers: 1, + image_usage: ImgUsage::COLOR_ATTACHMENT, + }; + + // Swapchain + let (swapchain, mut backbuffer) = unsafe { + device + .create_swapchain(surface, swap_config, old_swapchain) + .map_err(|_| TargetChainCreationError::Todo)? + }; + + let depth_buffer: LoadedImage = { + use hal::format::Aspects; + use hal::image::SubresourceRange; + + LoadedImage::new( + device, + adapter, + properties.depth_format, + ImgUsage::DEPTH_STENCIL_ATTACHMENT, + SubresourceRange { + aspects: Aspects::DEPTH, + levels: 0..1, + layers: 0..1, + }, + properties.extent.width as usize, + properties.extent.height as usize, + ) + .map_err(|_| TargetChainCreationError::Todo) + }?; + + let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len()); + for image in backbuffer.drain(..) { + targets.push( + TargetResources::new( + device, + cmd_pool, + renderpass, + image, + &(*depth_buffer.image_view), + properties.extent, + properties.format, + ) + .map_err(|_| TargetChainCreationError::Todo)?, + ); + } + + Ok(TargetChain { + swapchain: ManuallyDrop::new(swapchain), + targets: targets.into_boxed_slice(), + depth_buffer: ManuallyDrop::new(depth_buffer), + properties, + last_drawn: (image_count - 1) as usize, // This means the next one to be used is index 0 + last_image_index: 0, + }) + } + + pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) { + use core::ptr::read; + unsafe { + ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); + + for i in 0..self.targets.len() { + read(&self.targets[i]).deactivate(device, cmd_pool); + } + + device.destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); + } + } + + pub fn deactivate_with_recyling( + self, + device: &mut Device, + cmd_pool: &mut CommandPool, + ) -> Swapchain { + use core::ptr::read; + unsafe { + ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); + + for i in 0..self.targets.len() { + read(&self.targets[i]).deactivate(device, cmd_pool); + } + } + + unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) } + } + + pub fn prep_next_target<'a>( + &'a mut self, + device: &mut Device, + vert_buffer: &Buffer, + index_buffer: &Buffer, + renderpass: &RenderPass, + pipeline: &GraphicsPipeline, + pipeline_layout: &PipelineLayout, + camera: &mut WorkingCamera, + ) -> Result<&'a mut crate::types::CommandBuffer, &'static str> { + self.last_drawn = (self.last_drawn + 1) % self.targets.len(); + + let target = &mut self.targets[self.last_drawn]; + + // Get the image + let (image_index, _) = unsafe { + self.swapchain + .acquire_image(core::u64::MAX, Some(&target.get_image), None) + .map_err(|_| "FrameError::AcquireError")? + }; + + self.last_image_index = image_index; + + // Make sure whatever was last using this has finished + unsafe { + device + .wait_for_fence(&target.present_complete, core::u64::MAX) + .map_err(|_| "FrameError::SyncObjectError")?; + device + .reset_fence(&target.present_complete) + .map_err(|_| "FrameError::SyncObjectError")?; + }; + + // Record commands + unsafe { + use hal::buffer::{IndexBufferView, SubRange}; + use hal::command::{ + ClearColor, ClearDepthStencil, ClearValue, CommandBufferFlags, SubpassContents, + }; + use hal::pso::ShaderStageFlags; + + // Colour to clear window to + let clear_values = [ + ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }, + ClearValue { + depth_stencil: ClearDepthStencil { + depth: 1.0, + stencil: 0, + }, + }, + ]; + + // Get references to our buffers + let (vbufs, ibuf) = { + let vbufref: &<back::Backend as hal::Backend>::Buffer = vert_buffer; + + let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); + let ibuf = index_buffer; + + (vbufs, ibuf) + }; + + target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY); + // Main render pass / pipeline + target.cmd_buffer.begin_render_pass( + renderpass, + &target.framebuffer, + self.properties.viewport.rect, + clear_values.iter(), + SubpassContents::Inline, + ); + target.cmd_buffer.bind_graphics_pipeline(&pipeline); + + // VP Matrix + let vp = camera.get_matrix().as_slice(); + let vp = &*(vp as *const [f32] as *const [u32]); + + target.cmd_buffer.push_graphics_constants( + &pipeline_layout, + ShaderStageFlags::VERTEX, + 0, + vp, + ); + + // Bind buffers + target.cmd_buffer.bind_vertex_buffers(0, vbufs); + target.cmd_buffer.bind_index_buffer(IndexBufferView { + buffer: ibuf, + range: SubRange::WHOLE, + index_type: hal::IndexType::U16, + }); + }; + + Ok(&mut target.cmd_buffer) + } + + pub fn finish_and_submit_target( + &mut self, + command_queue: &mut CommandQueue, + ) -> Result<(), &'static str> { + let target = &mut self.targets[self.last_drawn]; + + unsafe { + target.cmd_buffer.end_render_pass(); + target.cmd_buffer.finish(); + } + + // Make submission object + let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer); + let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once(( + &target.get_image, + hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT, + )); + let signal_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete); + + let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete); + + let submission = Submission { + command_buffers, + wait_semaphores, + signal_semaphores, + }; + + // Submit it + unsafe { + command_queue.submit(submission, Some(&target.present_complete)); + self.swapchain + .present( + command_queue, + self.last_image_index as u32, + present_wait_semaphores, + ) + .map_err(|_| "FrameError::PresentError")?; + }; + + // TODO + Ok(()) + } } /// Resources for a single target frame, including sync objects pub struct TargetResources { - /// Command buffer to use when drawing - pub cmd_buffer: ManuallyDrop<CommandBuffer>, + /// Command buffer to use when drawing + pub cmd_buffer: ManuallyDrop<CommandBuffer>, - /// The image for this frame - pub image: ManuallyDrop<Image>, + /// The image for this frame + pub image: ManuallyDrop<Image>, - /// Imageviews for this frame - pub imageview: ManuallyDrop<ImageView>, + /// Imageviews for this frame + pub imageview: ManuallyDrop<ImageView>, - /// Framebuffer for this frame - pub framebuffer: ManuallyDrop<Framebuffer>, + /// Framebuffer for this frame + pub framebuffer: ManuallyDrop<Framebuffer>, - // Sync objects + // Sync objects + /// Triggered when the image is ready to draw to + pub get_image: ManuallyDrop<Semaphore>, - /// Triggered when the image is ready to draw to - pub get_image: ManuallyDrop<Semaphore>, + /// Triggered when rendering is done + pub render_complete: ManuallyDrop<Semaphore>, - /// Triggered when rendering is done - pub render_complete: ManuallyDrop<Semaphore>, - - /// Triggered when the image is on screen - pub present_complete: ManuallyDrop<Fence> + /// Triggered when the image is on screen + pub present_complete: ManuallyDrop<Fence>, } - impl TargetResources { - pub fn new(device: &mut Device, cmd_pool: &mut CommandPool, renderpass: &RenderPass, image: Image, depth_pass: &ImageView, extent: Extent, format: Format) -> Result<TargetResources, TargetResourcesCreationError> { - // Command Buffer - let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; - - // ImageView - let imageview = unsafe { device.create_image_view( - &image, - ViewKind::D2, - format, - Swizzle::NO, - COLOR_RANGE.clone(), - ).map_err(|e| TargetResourcesCreationError::ImageViewError (e))? }; - - // Framebuffer - let framebuffer = unsafe { device.create_framebuffer( - &renderpass, - once(&imageview).chain(once(depth_pass)), - extent - ).map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? }; - - // Sync objects - let get_image = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; - let render_complete = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; - let present_complete = device.create_fence(true).map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; - - Ok(TargetResources { - cmd_buffer: ManuallyDrop::new(cmd_buffer), - image: ManuallyDrop::new(image), - imageview: ManuallyDrop::new(imageview), - framebuffer: ManuallyDrop::new(framebuffer), - get_image: ManuallyDrop::new(get_image), - render_complete: ManuallyDrop::new(render_complete), - present_complete: ManuallyDrop::new(present_complete) - }) - } - - pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () { - use core::ptr::read; - unsafe { - cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer)))); - - device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer))); - device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview))); - - device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image))); - device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete))); - device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete))); - } - } + pub fn new( + device: &mut Device, + cmd_pool: &mut CommandPool, + renderpass: &RenderPass, + image: Image, + depth_pass: &ImageView, + extent: Extent, + format: Format, + ) -> Result<TargetResources, TargetResourcesCreationError> { + // Command Buffer + let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; + + // ImageView + let imageview = unsafe { + device + .create_image_view( + &image, + ViewKind::D2, + format, + Swizzle::NO, + COLOR_RANGE.clone(), + ) + .map_err(TargetResourcesCreationError::ImageViewError)? + }; + + // Framebuffer + let framebuffer = unsafe { + device + .create_framebuffer( + &renderpass, + once(&imageview).chain(once(depth_pass)), + extent, + ) + .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? + }; + + // Sync objects + let get_image = device + .create_semaphore() + .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + let render_complete = device + .create_semaphore() + .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + let present_complete = device + .create_fence(true) + .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; + + Ok(TargetResources { + cmd_buffer: ManuallyDrop::new(cmd_buffer), + image: ManuallyDrop::new(image), + imageview: ManuallyDrop::new(imageview), + framebuffer: ManuallyDrop::new(framebuffer), + get_image: ManuallyDrop::new(get_image), + render_complete: ManuallyDrop::new(render_complete), + present_complete: ManuallyDrop::new(present_complete), + }) + } + + pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) { + use core::ptr::read; + unsafe { + cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer)))); + + device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer))); + device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview))); + + device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image))); + device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete))); + device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete))); + } + } } #[derive(Debug)] pub enum TargetChainCreationError { - Todo + Todo, } #[derive(Debug)] pub enum TargetResourcesCreationError { - ImageViewError (hal::image::ViewCreationError), - FrameBufferNoMemory, - SyncObjectsNoMemory + ImageViewError(hal::image::ViewCreationError), + FrameBufferNoMemory, + SyncObjectsNoMemory, } diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs index 40eb21e..02f2dc5 100644 --- a/stockton-render/src/draw/texture/chunk.rs +++ b/stockton-render/src/draw/texture/chunk.rs @@ -16,22 +16,17 @@ //! A chunk of textures is an array of textures, the size of which is known at compile time. //! This reduces the number of times we need to re-bind our descriptor sets -use image::{Rgba, RgbaImage}; use hal::prelude::*; +use image::{Rgba, RgbaImage}; -use core::{ - mem::{replace} -}; -use std::ops::{Range, Deref}; +use core::mem::replace; +use std::ops::{Deref, Range}; -use crate::{ - types::*, - error -}; +use crate::{error, types::*}; -use log::debug; -use super::resolver::TextureResolver; use super::image::SampledImage; +use super::resolver::TextureResolver; +use log::debug; use stockton_levels::prelude::*; /// The size of a chunk. Needs to match up with the fragment shader @@ -39,125 +34,135 @@ pub const CHUNK_SIZE: usize = 8; /// An array of textures pub struct TextureChunk { - pub(crate) descriptor_set: DescriptorSet, - sampled_images: Vec<SampledImage>, + pub(crate) descriptor_set: DescriptorSet, + sampled_images: Vec<SampledImage>, } impl TextureChunk { - /// Create a new texture chunk and load in the textures specified by `range` from `file` using `resolver` - /// Can error if the descriptor pool is too small or if a texture isn't found - pub fn new<T: HasTextures, R: TextureResolver>(device: &mut Device, - adapter: &mut Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, - pool: &mut DescriptorPool, - layout: &DescriptorSetLayout, - file: &T, range: Range<u32>, - resolver: &mut R) -> Result<TextureChunk, error::CreationError> { - - // - let descriptor_set = unsafe { - pool.allocate_set(&layout).map_err(|e| { - println!("{:?}", e); - error::CreationError::OutOfMemoryError - })? - }; - - let mut store = TextureChunk { - descriptor_set: descriptor_set, - sampled_images: Vec::with_capacity(CHUNK_SIZE), - }; - - let mut local_idx = 0; - - debug!("Created descriptor set"); - for tex_idx in range { - debug!("Loading tex {}", local_idx + 1); - let tex = file.get_texture(tex_idx); - if let Some(img) = resolver.resolve(tex) { - store.put_texture(img, local_idx, - device, adapter, - command_queue, command_pool).unwrap(); - } else { - // Texture not found. For now, tear everything down. - store.deactivate(device); - - return Err(error::CreationError::BadDataError); - } - - local_idx += 1; - } - - // Pad out the end if needed - while local_idx < CHUNK_SIZE { - debug!("Putting a placeholder in slot {}", local_idx); - store.put_texture(RgbaImage::from_pixel(1, 1, Rgba ([0, 0, 0, 1])), local_idx, - device, adapter, - command_queue, command_pool).unwrap(); - - local_idx += 1; - } - - Ok(store) - } - - - pub fn put_texture(&mut self, image: RgbaImage, - idx: usize, - device: &mut Device, - adapter: &mut Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool) -> Result<(), &'static str>{ - - // Load the image - let texture = SampledImage::load_into_new( - image, - device, - adapter, - command_queue, - command_pool, - hal::format::Format::Rgba8Srgb, // TODO - hal::image::Usage::empty() - )?; - - // Write it to the descriptor set - unsafe { - use hal::pso::{DescriptorSetWrite, Descriptor}; - use hal::image::Layout; - - device.write_descriptor_sets(vec![ - DescriptorSetWrite { - set: &self.descriptor_set, - binding: 0, - array_offset: idx, - descriptors: Some(Descriptor::Image( - texture.image.image_view.deref(), - Layout::ShaderReadOnlyOptimal - )), - }, - DescriptorSetWrite { - set: &self.descriptor_set, - binding: 1, - array_offset: idx, - descriptors: Some(Descriptor::Sampler(texture.sampler.deref())), - }, - ]); - }; - - // Store it so we can safely deactivate it when we need to - // Deactivate the old image if we need to - if idx < self.sampled_images.len() { - replace(&mut self.sampled_images[idx], texture).deactivate(device); - } else { - self.sampled_images.push(texture); - } - - Ok(()) - } - - pub fn deactivate(mut self, device: &mut Device) -> () { - for img in self.sampled_images.drain(..) { - img.deactivate(device); - } - } -}
\ No newline at end of file + /// Create a new texture chunk and load in the textures specified by `range` from `file` using `resolver` + /// Can error if the descriptor pool is too small or if a texture isn't found + pub fn new<T: HasTextures, R: TextureResolver>( + device: &mut Device, + adapter: &mut Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + pool: &mut DescriptorPool, + layout: &DescriptorSetLayout, + file: &T, + range: Range<u32>, + resolver: &mut R, + ) -> Result<TextureChunk, error::CreationError> { + // + let descriptor_set = unsafe { + pool.allocate_set(&layout).map_err(|e| { + println!("{:?}", e); + error::CreationError::OutOfMemoryError + })? + }; + + let mut store = TextureChunk { + descriptor_set, + sampled_images: Vec::with_capacity(CHUNK_SIZE), + }; + + let mut local_idx = 0; + + debug!("Created descriptor set"); + for tex_idx in range { + debug!("Loading tex {}", local_idx + 1); + let tex = file.get_texture(tex_idx); + if let Some(img) = resolver.resolve(tex) { + store + .put_texture(img, local_idx, device, adapter, command_queue, command_pool) + .unwrap(); + } else { + // Texture not found. For now, tear everything down. + store.deactivate(device); + + return Err(error::CreationError::BadDataError); + } + + local_idx += 1; + } + + // Pad out the end if needed + while local_idx < CHUNK_SIZE { + debug!("Putting a placeholder in slot {}", local_idx); + store + .put_texture( + RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 1])), + local_idx, + device, + adapter, + command_queue, + command_pool, + ) + .unwrap(); + + local_idx += 1; + } + + Ok(store) + } + + pub fn put_texture( + &mut self, + image: RgbaImage, + idx: usize, + device: &mut Device, + adapter: &mut Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + ) -> Result<(), &'static str> { + // Load the image + let texture = SampledImage::load_into_new( + image, + device, + adapter, + command_queue, + command_pool, + hal::format::Format::Rgba8Srgb, // TODO + hal::image::Usage::empty(), + )?; + + // Write it to the descriptor set + unsafe { + use hal::image::Layout; + use hal::pso::{Descriptor, DescriptorSetWrite}; + + device.write_descriptor_sets(vec![ + DescriptorSetWrite { + set: &self.descriptor_set, + binding: 0, + array_offset: idx, + descriptors: Some(Descriptor::Image( + texture.image.image_view.deref(), + Layout::ShaderReadOnlyOptimal, + )), + }, + DescriptorSetWrite { + set: &self.descriptor_set, + binding: 1, + array_offset: idx, + descriptors: Some(Descriptor::Sampler(texture.sampler.deref())), + }, + ]); + }; + + // Store it so we can safely deactivate it when we need to + // Deactivate the old image if we need to + if idx < self.sampled_images.len() { + replace(&mut self.sampled_images[idx], texture).deactivate(device); + } else { + self.sampled_images.push(texture); + } + + Ok(()) + } + + pub fn deactivate(mut self, device: &mut Device) { + for img in self.sampled_images.drain(..) { + img.deactivate(device); + } + } +} diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs index 530628a..0b8c64e 100644 --- a/stockton-render/src/draw/texture/image.rs +++ b/stockton-render/src/draw/texture/image.rs @@ -14,23 +14,20 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. use core::{ - ptr::copy_nonoverlapping, - mem::{size_of, ManuallyDrop} + mem::{size_of, ManuallyDrop}, + ptr::copy_nonoverlapping, }; -use std::{ - iter::once, - convert::TryInto -}; -use image::RgbaImage; use hal::{ - MemoryTypeId, - buffer::Usage as BufUsage, - format::{Format, Swizzle, Aspects}, - image::{ViewKind, SubresourceRange, Usage as ImgUsage}, - queue::Submission, - memory::{Properties as MemProperties, Dependencies as MemDependencies, Segment}, - prelude::*, + buffer::Usage as BufUsage, + format::{Aspects, Format, Swizzle}, + image::{SubresourceRange, Usage as ImgUsage, ViewKind}, + memory::{Dependencies as MemDependencies, Properties as MemProperties, Segment}, + prelude::*, + queue::Submission, + MemoryTypeId, }; +use image::RgbaImage; +use std::{convert::TryInto, iter::once}; use crate::types::*; use draw::buffer::create_buffer; @@ -40,297 +37,367 @@ const PIXEL_SIZE: usize = size_of::<image::Rgba<u8>>(); /// Holds an image that's loaded into GPU memory and can be sampled from pub struct LoadedImage { - /// The GPU Image handle - image: ManuallyDrop<Image>, + /// The GPU Image handle + image: ManuallyDrop<Image>, - /// The full view of the image - pub image_view: ManuallyDrop<ImageView>, + /// The full view of the image + pub image_view: ManuallyDrop<ImageView>, - /// The memory backing the image - memory: ManuallyDrop<Memory> + /// The memory backing the image + memory: ManuallyDrop<Memory>, } -pub fn create_image_view(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<(Memory, Image), &'static str> { - // Round up the size to align properly - let initial_row_size = PIXEL_SIZE * width; - let limits = adapter.physical_device.limits(); - let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; - - let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; - debug_assert!(row_size as usize >= initial_row_size); - - // Make the image - let mut image_ref = unsafe { - use hal::image::{Kind, Tiling, ViewCapabilities}; - - device.create_image( - Kind::D2(width as u32, height as u32, 1, 1), - 1, - format, - Tiling::Optimal, - usage, - ViewCapabilities::empty() - ) - }.map_err(|_| "Couldn't create image")?; - - // Allocate memory - let memory = unsafe { - let requirements = device.get_image_requirements(&image_ref); - - let memory_type_id = adapter.physical_device - .memory_properties().memory_types - .iter().enumerate() - .find(|&(id, memory_type)| { - requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(MemProperties::DEVICE_LOCAL) - }) - .map(|(id, _)| MemoryTypeId(id)) - .ok_or("Couldn't find a memory type for image memory")?; - - let memory = device - .allocate_memory(memory_type_id, requirements.size) - .map_err(|_| "Couldn't allocate image memory")?; - - device.bind_image_memory(&memory, 0, &mut image_ref) - .map_err(|_| "Couldn't bind memory to image")?; - - Ok(memory) - }?; - - Ok((memory, image_ref)) - +pub fn create_image_view( + device: &mut Device, + adapter: &Adapter, + format: Format, + usage: ImgUsage, + width: usize, + height: usize, +) -> Result<(Memory, Image), &'static str> { + // Round up the size to align properly + let initial_row_size = PIXEL_SIZE * width; + let limits = adapter.physical_device.limits(); + let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; + + let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; + debug_assert!(row_size as usize >= initial_row_size); + + // Make the image + let mut image_ref = unsafe { + use hal::image::{Kind, Tiling, ViewCapabilities}; + + device.create_image( + Kind::D2(width as u32, height as u32, 1, 1), + 1, + format, + Tiling::Optimal, + usage, + ViewCapabilities::empty(), + ) + } + .map_err(|_| "Couldn't create image")?; + + // Allocate memory + let memory = unsafe { + let requirements = device.get_image_requirements(&image_ref); + + let memory_type_id = adapter + .physical_device + .memory_properties() + .memory_types + .iter() + .enumerate() + .find(|&(id, memory_type)| { + requirements.type_mask & (1 << id) != 0 + && memory_type.properties.contains(MemProperties::DEVICE_LOCAL) + }) + .map(|(id, _)| MemoryTypeId(id)) + .ok_or("Couldn't find a memory type for image memory")?; + + let memory = device + .allocate_memory(memory_type_id, requirements.size) + .map_err(|_| "Couldn't allocate image memory")?; + + device + .bind_image_memory(&memory, 0, &mut image_ref) + .map_err(|_| "Couldn't bind memory to image")?; + + Ok(memory) + }?; + + Ok((memory, image_ref)) } impl LoadedImage { - pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, resources: SubresourceRange, width: usize, height: usize) -> Result<LoadedImage, &'static str> { - let (memory, image_ref) = create_image_view(device, adapter, format, usage, width, height)?; - - // Create ImageView and sampler - let image_view = unsafe { device.create_image_view( - &image_ref, - ViewKind::D2, - format, - Swizzle::NO, - resources, - )}.map_err(|_| "Couldn't create the image view!")?; - - Ok(LoadedImage { - image: ManuallyDrop::new(image_ref), - image_view: ManuallyDrop::new(image_view), - memory: ManuallyDrop::new(memory) - }) - } - - /// Load the given image - pub fn load(&mut self, img: RgbaImage, device: &mut Device, adapter: &Adapter, command_queue: &mut CommandQueue, - command_pool: &mut CommandPool) -> Result<(), &'static str> { - let initial_row_size = PIXEL_SIZE * img.width() as usize; - let limits = adapter.physical_device.limits(); - let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; - - let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; - let total_size = (row_size * (img.height() as usize)) as u64; - debug_assert!(row_size as usize >= initial_row_size); - - // Make a staging buffer - let (staging_buffer, staging_memory) = create_buffer(device, adapter, BufUsage::TRANSFER_SRC, MemProperties::CPU_VISIBLE, total_size) - .map_err(|_| "Couldn't create staging buffer")?; - - // Copy everything into it - unsafe { - let mapped_memory: *mut u8 = device.map_memory(&staging_memory, Segment::ALL).map_err(|_| "Couldn't map buffer memory")?; - - for y in 0..img.height() as usize { - let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size]; - let dest_base: isize = (y * row_size).try_into().unwrap(); - - copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len()); - } - device.flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL))).map_err(|_| "Couldn't write buffer memory")?; - device.unmap_memory(&staging_memory); - } - - // Copy from staging to image memory - let buf = unsafe { - use hal::command::{CommandBufferFlags, BufferImageCopy}; - use hal::pso::PipelineStage; - use hal::memory::Barrier; - use hal::image::{Access, Layout, SubresourceLayers, Offset, Extent}; - - // Get a command buffer - let mut buf = command_pool.allocate_one(hal::command::Level::Primary); - buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - - // Setup the layout of our image for copying - let image_barrier = Barrier::Image { - states: (Access::empty(), Layout::Undefined) - ..( - Access::TRANSFER_WRITE, - Layout::TransferDstOptimal, - ), - target: &(*self.image), - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - }; - buf.pipeline_barrier( - PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, - MemDependencies::empty(), - &[image_barrier], - ); - - // Copy from buffer to image - buf.copy_buffer_to_image(&staging_buffer, &(*self.image), - Layout::TransferDstOptimal, &[ - BufferImageCopy { - buffer_offset: 0, - buffer_width: (row_size / PIXEL_SIZE) as u32, - buffer_height: img.height(), - image_layers: SubresourceLayers { - aspects: Aspects::COLOR, - level: 0, - layers: 0..1 - }, - image_offset: Offset { - x: 0, y: 0, z: 0 - }, - image_extent: Extent { - width: img.width(), - height: img.height(), - depth: 1 - } - } - ]); - - // Setup the layout of our image for shaders - let image_barrier = Barrier::Image { - states: ( - Access::TRANSFER_WRITE, - Layout::TransferDstOptimal, - )..( - Access::SHADER_READ, - Layout::ShaderReadOnlyOptimal, - ), - target: &(*self.image), - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - }; - - buf.pipeline_barrier( - PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER, - MemDependencies::empty(), - &[image_barrier], - ); - - buf.finish(); - - buf - }; - - // Submit our commands and wait for them to finish - unsafe { - let setup_finished = device.create_fence(false).unwrap(); - command_queue.submit::<_, _, Semaphore, _, _>(Submission { - command_buffers: &[&buf], - wait_semaphores: std::iter::empty::<_>(), - signal_semaphores: std::iter::empty::<_>() - }, Some(&setup_finished)); - - device - .wait_for_fence(&setup_finished, core::u64::MAX).unwrap(); - device.destroy_fence(setup_finished); - }; - - // Clean up temp resources - unsafe { - command_pool.free(once(buf)); - - device.free_memory(staging_memory); - device.destroy_buffer(staging_buffer); - } - - Ok(()) - } - - /// Load the given image into a new buffer - pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<LoadedImage, &'static str> { - let mut loaded_image = Self::new(device, adapter, format, usage | ImgUsage::TRANSFER_DST, SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, img.width() as usize, img.height() as usize)?; - loaded_image.load(img, device, adapter, command_queue, command_pool)?; - - Ok(loaded_image) - } - - /// Properly frees/destroys all the objects in this struct - /// Dropping without doing this is a bad idea - pub fn deactivate(self, device: &Device) -> () { - unsafe { - use core::ptr::read; - - device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view))); - device.destroy_image(ManuallyDrop::into_inner(read(&self.image))); - device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); - } - } + pub fn new( + device: &mut Device, + adapter: &Adapter, + format: Format, + usage: ImgUsage, + resources: SubresourceRange, + width: usize, + height: usize, + ) -> Result<LoadedImage, &'static str> { + let (memory, image_ref) = create_image_view(device, adapter, format, usage, width, height)?; + + // Create ImageView and sampler + let image_view = unsafe { + device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources) + } + .map_err(|_| "Couldn't create the image view!")?; + + Ok(LoadedImage { + image: ManuallyDrop::new(image_ref), + image_view: ManuallyDrop::new(image_view), + memory: ManuallyDrop::new(memory), + }) + } + + /// Load the given image + pub fn load( + &mut self, + img: RgbaImage, + device: &mut Device, + adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + ) -> Result<(), &'static str> { + let initial_row_size = PIXEL_SIZE * img.width() as usize; + let limits = adapter.physical_device.limits(); + let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; + + let row_size = + ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; + let total_size = (row_size * (img.height() as usize)) as u64; + debug_assert!(row_size as usize >= initial_row_size); + + // Make a staging buffer + let (staging_buffer, staging_memory) = create_buffer( + device, + adapter, + BufUsage::TRANSFER_SRC, + MemProperties::CPU_VISIBLE, + total_size, + ) + .map_err(|_| "Couldn't create staging buffer")?; + + // Copy everything into it + unsafe { + let mapped_memory: *mut u8 = device + .map_memory(&staging_memory, Segment::ALL) + .map_err(|_| "Couldn't map buffer memory")?; + + for y in 0..img.height() as usize { + let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size]; + let dest_base: isize = (y * row_size).try_into().unwrap(); + + copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len()); + } + device + .flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL))) + .map_err(|_| "Couldn't write buffer memory")?; + device.unmap_memory(&staging_memory); + } + + // Copy from staging to image memory + let buf = unsafe { + use hal::command::{BufferImageCopy, CommandBufferFlags}; + use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers}; + use hal::memory::Barrier; + use hal::pso::PipelineStage; + + // Get a command buffer + let mut buf = command_pool.allocate_one(hal::command::Level::Primary); + buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); + + // Setup the layout of our image for copying + let image_barrier = Barrier::Image { + states: (Access::empty(), Layout::Undefined) + ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), + target: &(*self.image), + families: None, + range: SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + }; + buf.pipeline_barrier( + PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, + MemDependencies::empty(), + &[image_barrier], + ); + + // Copy from buffer to image + buf.copy_buffer_to_image( + &staging_buffer, + &(*self.image), + Layout::TransferDstOptimal, + &[BufferImageCopy { + buffer_offset: 0, + buffer_width: (row_size / PIXEL_SIZE) as u32, + buffer_height: img.height(), + image_layers: SubresourceLayers { + aspects: Aspects::COLOR, + level: 0, + layers: 0..1, + }, + image_offset: Offset { x: 0, y: 0, z: 0 }, + image_extent: Extent { + width: img.width(), + height: img.height(), + depth: 1, + }, + }], + ); + + // Setup the layout of our image for shaders + let image_barrier = Barrier::Image { + states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) + ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal), + target: &(*self.image), + families: None, + range: SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + }; + + buf.pipeline_barrier( + PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER, + MemDependencies::empty(), + &[image_barrier], + ); + + buf.finish(); + + buf + }; + + // Submit our commands and wait for them to finish + unsafe { + let setup_finished = device.create_fence(false).unwrap(); + command_queue.submit::<_, _, Semaphore, _, _>( + Submission { + command_buffers: &[&buf], + wait_semaphores: std::iter::empty::<_>(), + signal_semaphores: std::iter::empty::<_>(), + }, + Some(&setup_finished), + ); + + device + .wait_for_fence(&setup_finished, core::u64::MAX) + .unwrap(); + device.destroy_fence(setup_finished); + }; + + // Clean up temp resources + unsafe { + command_pool.free(once(buf)); + + device.free_memory(staging_memory); + device.destroy_buffer(staging_buffer); + } + + Ok(()) + } + + /// Load the given image into a new buffer + pub fn load_into_new( + img: RgbaImage, + device: &mut Device, + adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + format: Format, + usage: ImgUsage, + ) -> Result<LoadedImage, &'static str> { + let mut loaded_image = Self::new( + device, + adapter, + format, + usage | ImgUsage::TRANSFER_DST, + SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + img.width() as usize, + img.height() as usize, + )?; + loaded_image.load(img, device, adapter, command_queue, command_pool)?; + + Ok(loaded_image) + } + + /// Properly frees/destroys all the objects in this struct + /// Dropping without doing this is a bad idea + pub fn deactivate(self, device: &Device) { + unsafe { + use core::ptr::read; + + device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view))); + device.destroy_image(ManuallyDrop::into_inner(read(&self.image))); + device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); + } + } } - pub struct SampledImage { - pub image: ManuallyDrop<LoadedImage>, - pub sampler: ManuallyDrop<Sampler> + pub image: ManuallyDrop<LoadedImage>, + pub sampler: ManuallyDrop<Sampler>, } impl SampledImage { - pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<SampledImage, &'static str> { - let image = LoadedImage::new(device, adapter, format, usage | ImgUsage::SAMPLED, SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, width, height)?; - - let sampler = unsafe { - use hal::image::{SamplerDesc, Filter, WrapMode}; - - device.create_sampler(&SamplerDesc::new( - Filter::Nearest, - WrapMode::Tile, - )) - }.map_err(|_| "Couldn't create the sampler!")?; - - Ok(SampledImage { - image: ManuallyDrop::new(image), - sampler: ManuallyDrop::new(sampler) - }) - } - - pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<SampledImage, &'static str> { - let mut sampled_image = SampledImage::new(device, adapter, format, usage | ImgUsage::TRANSFER_DST, img.width() as usize, img.height() as usize)?; - sampled_image.image.load(img, device, adapter, command_queue, command_pool)?; - - Ok(sampled_image) - - } - - pub fn deactivate(self, device: &mut Device) -> () { - unsafe { - use core::ptr::read; - - device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler))); - - ManuallyDrop::into_inner(read(&self.image)).deactivate(device); - } - } + pub fn new( + device: &mut Device, + adapter: &Adapter, + format: Format, + usage: ImgUsage, + width: usize, + height: usize, + ) -> Result<SampledImage, &'static str> { + let image = LoadedImage::new( + device, + adapter, + format, + usage | ImgUsage::SAMPLED, + SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + width, + height, + )?; + + let sampler = unsafe { + use hal::image::{Filter, SamplerDesc, WrapMode}; + + device.create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile)) + } + .map_err(|_| "Couldn't create the sampler!")?; + + Ok(SampledImage { + image: ManuallyDrop::new(image), + sampler: ManuallyDrop::new(sampler), + }) + } + + pub fn load_into_new( + img: RgbaImage, + device: &mut Device, + adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + format: Format, + usage: ImgUsage, + ) -> Result<SampledImage, &'static str> { + let mut sampled_image = SampledImage::new( + device, + adapter, + format, + usage | ImgUsage::TRANSFER_DST, + img.width() as usize, + img.height() as usize, + )?; + sampled_image + .image + .load(img, device, adapter, command_queue, command_pool)?; + + Ok(sampled_image) + } + + pub fn deactivate(self, device: &mut Device) { + unsafe { + use core::ptr::read; + + device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler))); + + ManuallyDrop::into_inner(read(&self.image)).deactivate(device); + } + } } diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index 1ac0961..863e1a8 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -14,17 +14,15 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Deals with loading textures into GPU memory -use std::path::Path; -use draw::texture::resolver::BasicFSResolver; -use draw::texture::chunk::CHUNK_SIZE; -use core::mem::{ManuallyDrop}; use super::chunk::TextureChunk; +use core::mem::ManuallyDrop; +use draw::texture::chunk::CHUNK_SIZE; +use draw::texture::resolver::BasicFSResolver; +use std::path::Path; use log::debug; -use hal::{ - prelude::*, -}; +use hal::prelude::*; use stockton_levels::prelude::*; @@ -36,137 +34,151 @@ use crate::types::*; /// The descriptor set layout should have the same count of textures as this does. /// All descriptors will be properly initialised images. pub struct TextureStore { - descriptor_pool: ManuallyDrop<DescriptorPool>, - pub(crate) descriptor_set_layout: ManuallyDrop<DescriptorSetLayout>, - chunks: Box<[TextureChunk]> + descriptor_pool: ManuallyDrop<DescriptorPool>, + pub(crate) descriptor_set_layout: ManuallyDrop<DescriptorSetLayout>, + chunks: Box<[TextureChunk]>, } impl TextureStore { - /// Create a new texture store for the given file, loading all textures from it. - pub fn new<T: HasTextures>(device: &mut Device, - adapter: &mut Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, file: &T) -> Result<TextureStore, error::CreationError> { - // Figure out how many textures in this file / how many chunks needed - let size = file.textures_iter().count(); - let num_chunks = { - let mut x = size / CHUNK_SIZE; - if size % CHUNK_SIZE != 0 { - x += 1; - } - x - }; - let rounded_size = num_chunks * CHUNK_SIZE; - - // Descriptor pool, where we get our sets from - let mut descriptor_pool = unsafe { - use hal::pso::{DescriptorRangeDesc, DescriptorType, DescriptorPoolCreateFlags, ImageDescriptorType}; - - device.create_descriptor_pool( - num_chunks, - &[ - DescriptorRangeDesc { - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false - } - }, - count: rounded_size - }, - DescriptorRangeDesc { - ty: DescriptorType::Sampler, - count: rounded_size - } - ], - DescriptorPoolCreateFlags::empty() - ).map_err(|e| { - println!("{:?}", e); - error::CreationError::OutOfMemoryError - })? - }; - - // Layout of our descriptor sets - let mut descriptor_set_layout = unsafe { - use hal::pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags, ImageDescriptorType}; - - device.create_descriptor_set_layout( - &[ - DescriptorSetLayoutBinding { - binding: 0, - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false - } - }, - count: CHUNK_SIZE, - stage_flags: ShaderStageFlags::FRAGMENT, - immutable_samplers: false - }, - DescriptorSetLayoutBinding { - binding: 1, - ty: DescriptorType::Sampler, - count: CHUNK_SIZE, - stage_flags: ShaderStageFlags::FRAGMENT, - immutable_samplers: false - } - ], - &[], - ) - }.map_err(|_| error::CreationError::OutOfMemoryError)?; - - // TODO: Proper way to set up resolver - let mut resolver = BasicFSResolver::new(Path::new(".")); - - // Create texture chunks - debug!("Starting to load textures..."); - let mut chunks = Vec::with_capacity(num_chunks); - for i in 0..num_chunks { - let range = { - let mut r = (i * CHUNK_SIZE) as u32..((i + 1) * CHUNK_SIZE) as u32; - if r.end > size as u32 { - r.end = size as u32; - } - r - }; - debug!("Chunk {} / {} covering {:?}", i + 1, num_chunks, range); - - chunks.push( - TextureChunk::new( - device, adapter, command_queue, - command_pool, &mut descriptor_pool, - &mut descriptor_set_layout, file, - range, &mut resolver - )? - ); - } - - debug!("All textures loaded."); - - Ok(TextureStore { - descriptor_pool: ManuallyDrop::new(descriptor_pool), - descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout), - chunks: chunks.into_boxed_slice() - }) - } - - /// Call this before dropping - pub fn deactivate(mut self, device: &mut Device) -> () { - unsafe { - use core::ptr::read; - - for chunk in self.chunks.into_vec().drain(..) { - chunk.deactivate(device) - } - - self.descriptor_pool.reset(); - device - .destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(&self.descriptor_set_layout))); - device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool))); - } - } - - /// Get the descriptor set for a given chunk - pub fn get_chunk_descriptor_set<'a>(&'a self, idx: usize) -> &'a DescriptorSet { - &self.chunks[idx].descriptor_set - } + /// Create a new texture store for the given file, loading all textures from it. + pub fn new<T: HasTextures>( + device: &mut Device, + adapter: &mut Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + file: &T, + ) -> Result<TextureStore, error::CreationError> { + // Figure out how many textures in this file / how many chunks needed + let size = file.textures_iter().count(); + let num_chunks = { + let mut x = size / CHUNK_SIZE; + if size % CHUNK_SIZE != 0 { + x += 1; + } + x + }; + let rounded_size = num_chunks * CHUNK_SIZE; + + // Descriptor pool, where we get our sets from + let mut descriptor_pool = unsafe { + use hal::pso::{ + DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, ImageDescriptorType, + }; + + device + .create_descriptor_pool( + num_chunks, + &[ + DescriptorRangeDesc { + ty: DescriptorType::Image { + ty: ImageDescriptorType::Sampled { + with_sampler: false, + }, + }, + count: rounded_size, + }, + DescriptorRangeDesc { + ty: DescriptorType::Sampler, + count: rounded_size, + }, + ], + DescriptorPoolCreateFlags::empty(), + ) + .map_err(|e| { + println!("{:?}", e); + error::CreationError::OutOfMemoryError + })? + }; + + // Layout of our descriptor sets + let descriptor_set_layout = unsafe { + use hal::pso::{ + DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags, + }; + + device.create_descriptor_set_layout( + &[ + DescriptorSetLayoutBinding { + binding: 0, + ty: DescriptorType::Image { + ty: ImageDescriptorType::Sampled { + with_sampler: false, + }, + }, + count: CHUNK_SIZE, + stage_flags: ShaderStageFlags::FRAGMENT, + immutable_samplers: false, + }, + DescriptorSetLayoutBinding { + binding: 1, + ty: DescriptorType::Sampler, + count: CHUNK_SIZE, + stage_flags: ShaderStageFlags::FRAGMENT, + immutable_samplers: false, + }, + ], + &[], + ) + } + .map_err(|_| error::CreationError::OutOfMemoryError)?; + + // TODO: Proper way to set up resolver + let mut resolver = BasicFSResolver::new(Path::new(".")); + + // Create texture chunks + debug!("Starting to load textures..."); + let mut chunks = Vec::with_capacity(num_chunks); + for i in 0..num_chunks { + let range = { + let mut r = (i * CHUNK_SIZE) as u32..((i + 1) * CHUNK_SIZE) as u32; + if r.end > size as u32 { + r.end = size as u32; + } + r + }; + debug!("Chunk {} / {} covering {:?}", i + 1, num_chunks, range); + + chunks.push(TextureChunk::new( + device, + adapter, + command_queue, + command_pool, + &mut descriptor_pool, + &descriptor_set_layout, + file, + range, + &mut resolver, + )?); + } + + debug!("All textures loaded."); + + Ok(TextureStore { + descriptor_pool: ManuallyDrop::new(descriptor_pool), + descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout), + chunks: chunks.into_boxed_slice(), + }) + } + + /// Call this before dropping + pub fn deactivate(mut self, device: &mut Device) { + unsafe { + use core::ptr::read; + + for chunk in self.chunks.into_vec().drain(..) { + chunk.deactivate(device) + } + + self.descriptor_pool.reset(); + device.destroy_descriptor_set_layout(ManuallyDrop::into_inner(read( + &self.descriptor_set_layout, + ))); + device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool))); + } + } + + /// Get the descriptor set for a given chunk + pub fn get_chunk_descriptor_set(&self, idx: usize) -> &DescriptorSet { + &self.chunks[idx].descriptor_set + } } diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs index 05dfe38..756fe79 100644 --- a/stockton-render/src/draw/texture/mod.rs +++ b/stockton-render/src/draw/texture/mod.rs @@ -15,10 +15,10 @@ //! Everything related to loading textures into GPU memory -mod resolver; -pub mod image; mod chunk; +pub mod image; pub mod loader; +mod resolver; +pub use self::image::{LoadedImage, SampledImage}; pub use self::loader::TextureStore; -pub use self::image::{LoadedImage, SampledImage};
\ No newline at end of file diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs index 5867171..5fab16a 100644 --- a/stockton-render/src/draw/texture/resolver.rs +++ b/stockton-render/src/draw/texture/resolver.rs @@ -17,44 +17,39 @@ use stockton_levels::traits::textures::Texture; -use image::{ - RgbaImage, - io::Reader -}; +use image::{io::Reader, RgbaImage}; use std::path::Path; /// An object that can be used to resolve a texture from a BSP File pub trait TextureResolver { - /// Get the given texture, or None if it's corrupt/not there. - fn resolve(&mut self, texture: &Texture) -> Option<RgbaImage>; + /// Get the given texture, or None if it's corrupt/not there. + fn resolve(&mut self, texture: &Texture) -> Option<RgbaImage>; } /// A basic filesystem resolver which expects no file extension and guesses the image format pub struct BasicFSResolver<'a> { - path: &'a Path + path: &'a Path, } impl<'a> BasicFSResolver<'a> { - pub fn new(path: &'a Path) -> BasicFSResolver<'a> { - BasicFSResolver { - path - } - } + pub fn new(path: &'a Path) -> BasicFSResolver<'a> { + BasicFSResolver { path } + } } impl<'a> TextureResolver for BasicFSResolver<'a> { - fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> { - 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_rgba()); - } - } - } - - None - } -}
\ No newline at end of file + fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> { + 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_rgba()); + } + } + } + + None + } +} diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index 8922423..28d3282 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -18,35 +18,30 @@ use super::draw::target::TargetChainCreationError; /// An error encountered creating a rendering context. -/// Falls into 3 main types: -/// - Hardware - No suitable card usually -/// - Sanity - Things that probably aren't true, likely indicating a deeper issue. -/// These aren't guaranteed sanity issues, but they are weird issues. -/// - Runtime - Things caused by runtime conditions, usually resource constraints. Could also be caused by corrupt files #[derive(Debug)] pub enum CreationError { - TargetChainCreationError (TargetChainCreationError), - WindowError, - BadSurface, - - DeviceError (hal::device::CreationError), - - OutOfMemoryError, - - SyncObjectError, - - NoShaderC, - ShaderCError (shaderc::Error), - ShaderModuleFailed (hal::device::ShaderError), - RenderPassError, - PipelineError (hal::pso::CreationError), - BufferError (hal::buffer::CreationError), - BufferNoMemory, - - SwapchainError (hal::window::CreationError), - ImageViewError (hal::image::ViewCreationError), - - BadDataError + TargetChainCreationError(TargetChainCreationError), + WindowError, + BadSurface, + + DeviceError(hal::device::CreationError), + + OutOfMemoryError, + + SyncObjectError, + + NoShaderC, + ShaderCError(shaderc::Error), + ShaderModuleFailed(hal::device::ShaderError), + RenderPassError, + PipelineError(hal::pso::CreationError), + BufferError(hal::buffer::CreationError), + BufferNoMemory, + + SwapchainError(hal::window::CreationError), + ImageViewError(hal::image::ViewCreationError), + + BadDataError, } /// An error encountered when rendering. @@ -54,7 +49,7 @@ pub enum CreationError { /// You'll likely need to exit or create a new context. #[derive(Debug, Clone)] pub enum FrameError { - AcquireError (hal::window::AcquireError), - SyncObjectError, - PresentError -}
\ No newline at end of file + AcquireError(hal::window::AcquireError), + SyncObjectError, + PresentError, +} diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs index 0db4b76..7e7ea5b 100644 --- a/stockton-render/src/lib.rs +++ b/stockton-render/src/lib.rs @@ -21,58 +21,57 @@ extern crate gfx_hal as hal; extern crate shaderc; extern crate winit; -extern crate nalgebra_glm as na; extern crate image; extern crate log; +extern crate nalgebra_glm as na; -extern crate stockton_types; extern crate stockton_levels; +extern crate stockton_types; extern crate arrayvec; +mod culling; pub mod draw; mod error; mod types; -mod culling; -use stockton_types::World; use stockton_levels::prelude::*; +use stockton_types::World; -use error::{CreationError, FrameError}; -use draw::RenderingContext; use culling::get_visible_faces; +use draw::RenderingContext; +use error::{CreationError, FrameError}; /// Renders a world to a window when you tell it to. pub struct Renderer<'a, T: MinBSPFeatures<VulkanSystem>> { - world: World<T>, - pub context: RenderingContext<'a> + world: World<T>, + pub context: RenderingContext<'a>, } - impl<'a, T: MinBSPFeatures<VulkanSystem>> Renderer<'a, T> { - /// Create a new Renderer. - /// This initialises all the vulkan context, etc needed. - pub fn new(world: World<T>, window: &winit::window::Window) -> Result<Self, CreationError> { - let context = RenderingContext::new(window, &world.map)?; - - Ok(Renderer { - world, context - }) - } - - /// Render a single frame of the world - pub fn render_frame(&mut self) -> Result<(), FrameError>{ - // Get visible faces - let faces = get_visible_faces(self.context.camera_pos(), &self.world.map); - - // Then draw them - if let Err(_) = self.context.draw_vertices(&self.world.map, &faces) { - unsafe {self.context.handle_surface_change().unwrap()}; - - // If it fails twice, then error - self.context.draw_vertices(&self.world.map, &faces).map_err(|_| FrameError::PresentError)?; - } - - Ok(()) - } + /// Create a new Renderer. + /// This initialises all the vulkan context, etc needed. + pub fn new(world: World<T>, window: &winit::window::Window) -> Result<Self, CreationError> { + let context = RenderingContext::new(window, &world.map)?; + + Ok(Renderer { world, context }) + } + + /// Render a single frame of the world + pub fn render_frame(&mut self) -> Result<(), FrameError> { + // Get visible faces + let faces = get_visible_faces(self.context.camera_pos(), &self.world.map); + + // Then draw them + if self.context.draw_vertices(&self.world.map, &faces).is_err() { + unsafe { self.context.handle_surface_change().unwrap() }; + + // If it fails twice, then error + self.context + .draw_vertices(&self.world.map, &faces) + .map_err(|_| FrameError::PresentError)?; + } + + Ok(()) + } } diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs index a0f242c..a5aefd8 100644 --- a/stockton-render/src/types.rs +++ b/stockton-render/src/types.rs @@ -38,4 +38,4 @@ pub type Framebuffer = <back::Backend as hal::Backend>::Framebuffer; pub type RenderPass = <back::Backend as hal::Backend>::RenderPass; pub type Adapter = hal::adapter::Adapter<back::Backend>; -pub type QueueGroup = hal::queue::QueueGroup<back::Backend>;
\ No newline at end of file +pub type QueueGroup = hal::queue::QueueGroup<back::Backend>; diff --git a/stockton-types/src/lib.rs b/stockton-types/src/lib.rs index bf690f1..ee01019 100644 --- a/stockton-types/src/lib.rs +++ b/stockton-types/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -14,8 +14,8 @@ // with this program. If not, see <http://www.gnu.org/licenses/>. //! Common types for all stockton crates. -extern crate stockton_levels; extern crate nalgebra_glm as na; +extern crate stockton_levels; pub mod world; pub use world::World; @@ -32,4 +32,4 @@ pub type Vector2i = na::IVec2; pub type Vector3i = na::IVec3; /// Alias for convenience -pub type Matrix4 = na::Mat4x4;
\ No newline at end of file +pub type Matrix4 = na::Mat4x4; diff --git a/stockton-types/src/world.rs b/stockton-types/src/world.rs index 69a7ad6..2bb0273 100644 --- a/stockton-types/src/world.rs +++ b/stockton-types/src/world.rs @@ -1,4 +1,4 @@ -// Copyright (C) Oscar Shrimpton 2019 +// Copyright (C) Oscar Shrimpton 2019 // 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 @@ -19,15 +19,13 @@ use stockton_levels::prelude::*; /// A loaded world. pub struct World<T: MinBSPFeatures<VulkanSystem>> { - pub map: T, + pub map: T, } impl<T: MinBSPFeatures<VulkanSystem>> World<T> { - /// Create a new world from a level. - /// The level can be any format, as long as it has the required features of a bsp. - pub fn new(map: T) -> World<T> { - World { - map - } - } -}
\ No newline at end of file + /// Create a new world from a level. + /// The level can be any format, as long as it has the required features of a bsp. + pub fn new(map: T) -> World<T> { + World { map } + } +} |