aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src/level.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-render/src/level.rs')
-rw-r--r--stockton-render/src/level.rs478
1 files changed, 478 insertions, 0 deletions
diff --git a/stockton-render/src/level.rs b/stockton-render/src/level.rs
new file mode 100644
index 0000000..6c74211
--- /dev/null
+++ b/stockton-render/src/level.rs
@@ -0,0 +1,478 @@
+//! Minimal code for drawing any level, based on traits from stockton-levels
+
+use stockton_levels::{
+ features::MinRenderFeatures,
+ parts::{data::Geometry, IsFace},
+};
+use stockton_skeleton::{
+ buffers::{
+ DedicatedLoadedImage, DrawBuffers, ModifiableBuffer, INITIAL_INDEX_SIZE, INITIAL_VERT_SIZE,
+ },
+ builders::{
+ CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc, VertexBufferSpec,
+ VertexPrimitiveAssemblerSpec,
+ },
+ context::RenderingContext,
+ draw_passes::{util::TargetSpecificResources, DrawPass, IntoDrawPass},
+ error::{EnvironmentError, LevelError, LockPoisoned},
+ queue_negotiator::QueueNegotiator,
+ texture::{resolver::TextureResolver, TexLoadQueue, TextureLoadConfig, TextureRepo},
+ types::*,
+};
+use stockton_types::{
+ components::{CameraSettings, CameraVPMatrix, Transform},
+ *,
+};
+
+use std::{
+ array::IntoIter,
+ convert::TryInto,
+ iter::{empty, once},
+ marker::PhantomData,
+ sync::{Arc, RwLock},
+};
+
+use anyhow::{Context, Result};
+use hal::{
+ buffer::SubRange,
+ command::{ClearColor, ClearDepthStencil, ClearValue, RenderAttachmentInfo, SubpassContents},
+ format::{Aspects, Format},
+ image::{
+ Filter, FramebufferAttachment, Layout, SubresourceRange, Usage, ViewCapabilities, WrapMode,
+ },
+ pass::{Attachment, AttachmentLoadOp, AttachmentOps, AttachmentStoreOp},
+ pso::{
+ BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, Comparison, DepthStencilDesc,
+ DepthTest, Face, Factor, FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive,
+ Rasterizer, ShaderStageFlags, State, VertexInputRate,
+ },
+};
+use legion::{Entity, IntoQuery};
+use shaderc::ShaderKind;
+
+/// The Vertexes that go to the shader
+#[derive(Debug, Clone, Copy)]
+struct UvPoint(pub Vector3, pub i32, pub Vector2);
+
+/// Draw a level
+pub struct LevelDrawPass<'a, M> {
+ pipeline: CompletePipeline,
+ repo: TextureRepo,
+ active_camera: Entity,
+ draw_buffers: DrawBuffers<'a, UvPoint>,
+
+ framebuffers: TargetSpecificResources<FramebufferT>,
+ depth_buffers: TargetSpecificResources<DedicatedLoadedImage>,
+
+ _d: PhantomData<M>,
+}
+
+impl<'a, M> DrawPass for LevelDrawPass<'a, M>
+where
+ M: for<'b> MinRenderFeatures<'b> + 'static,
+{
+ fn queue_draw(
+ &mut self,
+ session: &Session,
+ img_view: &ImageViewT,
+ cmd_buffer: &mut CommandBufferT,
+ ) -> anyhow::Result<()> {
+ // We might have loaded more textures
+ self.repo.process_responses();
+
+ // Make sure we update the vertex buffers after they're written to, but before they're read from.
+ self.draw_buffers
+ .vertex_buffer
+ .record_commit_cmds(cmd_buffer)?;
+ self.draw_buffers
+ .index_buffer
+ .record_commit_cmds(cmd_buffer)?;
+
+ // Get level & camera
+ let mut query = <(&Transform, &CameraSettings, &CameraVPMatrix)>::query();
+ let (camera_transform, camera_settings, camera_vp) = query
+ .get(&session.world, self.active_camera)
+ .context("Couldn't find camera components")?;
+ let map_lock: Arc<RwLock<M>> = session.resources.get::<Arc<RwLock<M>>>().unwrap().clone();
+ let map = map_lock.read().map_err(|_| LockPoisoned::Map)?;
+
+ // Get framebuffer and depth buffer
+ let fb = self.framebuffers.get_next();
+ let db = self.depth_buffers.get_next();
+
+ unsafe {
+ cmd_buffer.begin_render_pass(
+ &self.pipeline.renderpass,
+ fb,
+ self.pipeline.render_area,
+ vec![
+ RenderAttachmentInfo {
+ image_view: img_view,
+ clear_value: ClearValue {
+ color: ClearColor {
+ float32: [0.0, 0.0, 0.0, 1.0],
+ },
+ },
+ },
+ RenderAttachmentInfo {
+ image_view: &*db.image_view,
+ clear_value: ClearValue {
+ depth_stencil: ClearDepthStencil {
+ depth: 1.0,
+ stencil: 0,
+ },
+ },
+ },
+ ]
+ .into_iter(),
+ SubpassContents::Inline,
+ );
+ cmd_buffer.bind_graphics_pipeline(&self.pipeline.pipeline);
+
+ // VP Matrix
+ let vp = &*(camera_vp.vp_matrix.data.as_slice() as *const [f32] as *const [u32]);
+
+ cmd_buffer.push_graphics_constants(
+ &self.pipeline.pipeline_layout,
+ ShaderStageFlags::VERTEX,
+ 0,
+ vp,
+ );
+
+ // Bind buffers
+ cmd_buffer.bind_vertex_buffers(
+ 0,
+ once((
+ self.draw_buffers.vertex_buffer.get_buffer(),
+ SubRange {
+ offset: 0,
+ size: None,
+ },
+ )),
+ );
+ cmd_buffer.bind_index_buffer(
+ self.draw_buffers.index_buffer.get_buffer(),
+ SubRange {
+ offset: 0,
+ size: None,
+ },
+ hal::IndexType::U16,
+ );
+ }
+
+ // Get visible faces
+ let mut faces = map.get_visible(camera_transform, camera_settings);
+
+ // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once.
+ let face = faces.next();
+ if let Some(face) = face {
+ let mut face = map.get_face(face).ok_or(LevelError::BadReference)?;
+ let mut current_chunk = face.texture_idx(&map) as usize / 8;
+ let mut chunk_start = 0;
+
+ let mut curr_vert_idx: usize = 0;
+ let mut curr_idx_idx: usize = 0;
+ loop {
+ if current_chunk != face.texture_idx(&map) as usize / 8 {
+ // Last index was last of group, so draw it all if textures are loaded.
+ draw_or_queue(
+ current_chunk,
+ &mut self.repo,
+ cmd_buffer,
+ &*self.pipeline.pipeline_layout,
+ chunk_start as u32,
+ curr_idx_idx as u32,
+ )?;
+
+ // Next group of same-chunked faces starts here.
+ chunk_start = curr_idx_idx;
+ current_chunk = face.texture_idx(&map) as usize / 8;
+ }
+
+ match face.geometry(&map) {
+ Geometry::Vertices(v1, v2, v3) => {
+ for v in &[v1, v2, v3] {
+ let uvp =
+ UvPoint(v.position, face.texture_idx(&map).try_into()?, v.tex);
+
+ self.draw_buffers.vertex_buffer[curr_vert_idx] = uvp;
+ curr_vert_idx += 1;
+ }
+ self.draw_buffers.index_buffer[curr_idx_idx] = (
+ curr_vert_idx as u16 - 3,
+ curr_vert_idx as u16 - 2,
+ curr_vert_idx as u16 - 1,
+ );
+ curr_idx_idx += 1;
+ }
+ }
+
+ if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()?
+ || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()?
+ {
+ println!("out of vertex buffer space!");
+ break;
+ }
+
+ match faces.next() {
+ Some(x) => face = map.get_face(x).ok_or(LevelError::BadReference)?,
+ None => break,
+ };
+ }
+
+ // Draw the final group of chunks
+ draw_or_queue(
+ current_chunk,
+ &mut self.repo,
+ cmd_buffer,
+ &*self.pipeline.pipeline_layout,
+ chunk_start as u32,
+ curr_idx_idx as u32,
+ )?;
+ }
+
+ unsafe {
+ cmd_buffer.end_render_pass();
+ }
+
+ Ok(())
+ }
+
+ fn deactivate(self, context: &mut RenderingContext) -> Result<()> {
+ unsafe {
+ let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ self.pipeline.deactivate(&mut device);
+ self.draw_buffers.deactivate(&mut device);
+ for fb in self.framebuffers.dissolve() {
+ device.destroy_framebuffer(fb);
+ }
+ for db in self.depth_buffers.dissolve() {
+ db.deactivate(&mut device);
+ }
+ }
+ self.repo.deactivate(context.device());
+
+ Ok(())
+ }
+
+ fn handle_surface_change(
+ &mut self,
+ _session: &Session,
+ _context: &mut RenderingContext,
+ ) -> Result<()> {
+ todo!()
+ }
+}
+
+pub struct LevelDrawPassConfig<R> {
+ pub active_camera: Entity,
+ pub tex_resolver: R,
+}
+
+impl<'a, M, R> IntoDrawPass<LevelDrawPass<'a, M>> for LevelDrawPassConfig<R>
+where
+ M: for<'b> MinRenderFeatures<'b> + 'static,
+ R: TextureResolver + Send + Sync + 'static,
+{
+ fn init(
+ self,
+ _session: &mut Session,
+ context: &mut RenderingContext,
+ ) -> Result<LevelDrawPass<'a, M>> {
+ let spec = PipelineSpecBuilder::default()
+ .rasterizer(Rasterizer {
+ polygon_mode: PolygonMode::Fill,
+ cull_face: Face::BACK,
+ front_face: FrontFace::CounterClockwise,
+ depth_clamping: false,
+ depth_bias: None,
+ conservative: true,
+ line_width: State::Static(1.0),
+ })
+ .depth_stencil(DepthStencilDesc {
+ depth: Some(DepthTest {
+ fun: Comparison::Less,
+ write: true,
+ }),
+ depth_bounds: false,
+ stencil: None,
+ })
+ .blender(BlendDesc {
+ logic_op: Some(LogicOp::Copy),
+ targets: vec![ColorBlendDesc {
+ mask: ColorMask::ALL,
+ blend: Some(BlendState {
+ color: BlendOp::Add {
+ src: Factor::One,
+ dst: Factor::Zero,
+ },
+ alpha: BlendOp::Add {
+ src: Factor::One,
+ dst: Factor::Zero,
+ },
+ }),
+ }],
+ })
+ .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers(
+ InputAssemblerDesc::new(Primitive::TriangleList),
+ vec![VertexBufferSpec {
+ attributes: vec![Format::Rgb32Sfloat, Format::R32Sint, Format::Rg32Sfloat],
+ rate: VertexInputRate::Vertex,
+ }],
+ ))
+ .shader_vertex(ShaderDesc {
+ source: include_str!("./data/3d.vert").to_string(),
+ entry: "main".to_string(),
+ kind: ShaderKind::Vertex,
+ })
+ .shader_fragment(ShaderDesc {
+ source: include_str!("./data/3d.frag").to_string(),
+ entry: "main".to_string(),
+ kind: ShaderKind::Fragment,
+ })
+ .push_constants(vec![(ShaderStageFlags::VERTEX, 0..64)])
+ .renderpass(RenderpassSpec {
+ colors: vec![Attachment {
+ format: Some(context.target_chain().properties().format),
+ samples: 1,
+ ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store),
+ stencil_ops: AttachmentOps::new(
+ AttachmentLoadOp::Clear,
+ AttachmentStoreOp::DontCare,
+ ),
+ layouts: Layout::ColorAttachmentOptimal..Layout::ColorAttachmentOptimal,
+ }],
+ depth: Some(Attachment {
+ format: Some(context.target_chain().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,
+ }),
+ inputs: vec![],
+ resolves: vec![],
+ preserves: vec![],
+ })
+ .build()
+ .context("Error building pipeline")?;
+
+ let repo = TextureRepo::new(
+ context.device().clone(),
+ context
+ .queue_negotiator_mut()
+ .family::<TexLoadQueue>()
+ .ok_or(EnvironmentError::NoSuitableFamilies)
+ .context("Error finding texture queue")?,
+ context
+ .queue_negotiator_mut()
+ .get_queue::<TexLoadQueue>()
+ .ok_or(EnvironmentError::NoQueues)
+ .context("Error finding texture queue")?,
+ context.adapter(),
+ TextureLoadConfig {
+ resolver: self.tex_resolver,
+ filter: Filter::Linear,
+ wrap_mode: WrapMode::Tile,
+ },
+ )
+ .context("Error creating texture repo")?;
+
+ let (draw_buffers, pipeline, framebuffers, depth_buffers) = {
+ let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let draw_buffers = DrawBuffers::new(&mut device, context.adapter())
+ .context("Error creating draw buffers")?;
+ let pipeline = spec
+ .build(
+ &mut device,
+ context.target_chain().properties().extent,
+ context.target_chain().properties(),
+ once(&*repo.get_ds_layout()?),
+ )
+ .context("Error building pipeline")?;
+
+ let fat = context.target_chain().properties().framebuffer_attachment();
+ let dat = FramebufferAttachment {
+ usage: Usage::DEPTH_STENCIL_ATTACHMENT,
+ format: context.target_chain().properties().depth_format,
+ view_caps: ViewCapabilities::empty(),
+ };
+ let framebuffers = TargetSpecificResources::new(
+ || unsafe {
+ Ok(device.create_framebuffer(
+ &pipeline.renderpass,
+ IntoIter::new([fat.clone(), dat.clone()]),
+ context.target_chain().properties().extent,
+ )?)
+ },
+ context.target_chain().properties().image_count as usize,
+ )?;
+ let depth_buffers = TargetSpecificResources::new(
+ || {
+ DedicatedLoadedImage::new(
+ &mut device,
+ context.adapter(),
+ context.target_chain().properties().depth_format,
+ Usage::DEPTH_STENCIL_ATTACHMENT,
+ SubresourceRange {
+ aspects: Aspects::DEPTH,
+ level_start: 0,
+ level_count: Some(1),
+ layer_start: 0,
+ layer_count: Some(1),
+ },
+ context.target_chain().properties().extent.width as usize,
+ context.target_chain().properties().extent.height as usize,
+ )
+ .context("Error creating depth buffer")
+ },
+ context.target_chain().properties().image_count as usize,
+ )?;
+
+ (draw_buffers, pipeline, framebuffers, depth_buffers)
+ };
+
+ Ok(LevelDrawPass {
+ pipeline,
+ repo,
+ draw_buffers,
+ active_camera: self.active_camera,
+ _d: PhantomData,
+ framebuffers,
+ depth_buffers,
+ })
+ }
+
+ fn find_aux_queues<'c>(
+ adapter: &'c Adapter,
+ queue_negotiator: &mut QueueNegotiator,
+ ) -> Result<Vec<(&'c QueueFamilyT, Vec<f32>)>> {
+ queue_negotiator.find(adapter, &TexLoadQueue)?;
+
+ Ok(vec![queue_negotiator
+ .family_spec::<TexLoadQueue>(&adapter.queue_families, 1)
+ .ok_or(EnvironmentError::NoSuitableFamilies)?])
+ }
+}
+
+fn draw_or_queue(
+ current_chunk: usize,
+ tex_repo: &mut TextureRepo,
+ cmd_buffer: &mut CommandBufferT,
+ pipeline_layout: &PipelineLayoutT,
+ chunk_start: u32,
+ curr_idx_idx: u32,
+) -> Result<()> {
+ if let Some(ds) = tex_repo.attempt_get_descriptor_set(current_chunk) {
+ unsafe {
+ cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, once(ds), empty());
+ cmd_buffer.draw_indexed(chunk_start * 3..(curr_idx_idx * 3) + 1, 0, 0..1);
+ }
+ } else {
+ tex_repo.queue_load(current_chunk)?
+ }
+
+ Ok(())
+}