diff --git a/assets/voxygen/shaders/debug-frag.glsl b/assets/voxygen/shaders/debug-frag.glsl new file mode 100644 index 0000000000..c681b64bfc --- /dev/null +++ b/assets/voxygen/shaders/debug-frag.glsl @@ -0,0 +1,19 @@ +#version 420 core + +#include + +layout (location = 0) +in vec4 f_color; + +layout (std140, set = 1, binding = 0) +uniform u_locals { + vec4 w_pos; + vec4 w_color; +}; + +layout (location = 0) +out vec4 tgt_color; + +void main() { + tgt_color = f_color; +} diff --git a/assets/voxygen/shaders/debug-vert.glsl b/assets/voxygen/shaders/debug-vert.glsl new file mode 100644 index 0000000000..97d774a642 --- /dev/null +++ b/assets/voxygen/shaders/debug-vert.glsl @@ -0,0 +1,20 @@ +#version 420 core + +#include + +layout (location = 0) +in vec3 v_pos; + +layout (std140, set = 1, binding = 0) +uniform u_locals { + vec4 w_pos; + vec4 w_color; +}; + +layout (location = 0) +out vec4 f_color; + +void main() { + f_color = w_color; + gl_Position = all_mat * vec4((v_pos + w_pos.xyz) - focus_off.xyz, 1); +} diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 0d23f9022b..e82625f42b 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -21,6 +21,7 @@ pub use self::{ model::{DynamicModel, Model, SubModel}, pipelines::{ clouds::Locals as CloudsLocals, + debug::{DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex}, figure::{ BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel, Locals as FigureLocals, diff --git a/voxygen/src/render/pipelines/debug.rs b/voxygen/src/render/pipelines/debug.rs new file mode 100644 index 0000000000..3c8dd51032 --- /dev/null +++ b/voxygen/src/render/pipelines/debug.rs @@ -0,0 +1,201 @@ +use super::{ + super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait}, +}; +use bytemuck::{Pod, Zeroable}; +use std::mem; +use vek::*; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +pub struct Vertex { + pub pos: [f32; 3], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: Self::STRIDE, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }], + } + } +} + +impl VertexTrait for Vertex { + //const QUADS_INDEX: Option = + // Some(wgpu::IndexFormat::Uint32); + const QUADS_INDEX: Option = None; + const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +pub struct Locals { + /// pos is [f32; 4] instead of [f32; 3] so that Locals's size is a multiple + /// of 8 bytes (which is required by gfx), the last component is ignored + /// by the shader + pub pos: [f32; 4], + pub color: [f32; 4], +} + +pub type BoundLocals = Bound>; + +/*gfx_defines! { + vertex Vertex { + pos: [f32; 3] = "v_pos", + } + + constant Locals { + // pos is [f32; 4] instead of [f32; 3] so that Locals's size is a multiple of 8 bytes + // (which is required by gfx), the last component is ignored by the shader + pos: [f32; 4] = "w_pos", + color: [f32; 4] = "w_color", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + tgt_color: gfx::BlendTarget = ("tgt_color", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA), + //tgt_depth: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + tgt_depth: gfx::DepthTarget = gfx::preset::depth::PASS_TEST, + } +}*/ + +impl From> for Vertex { + fn from(pos: Vec3) -> Vertex { + Vertex { + pos: [pos.x, pos.y, pos.z], + } + } +} + +pub struct DebugPipeline { + pub pipeline: wgpu::RenderPipeline, +} + +impl DebugPipeline { + pub fn new( + device: &wgpu::Device, + vs_module: &wgpu::ShaderModule, + fs_module: &wgpu::ShaderModule, + global_layouts: &GlobalsLayouts, + layout: &DebugLayout, + aa_mode: AaMode, + ) -> Self { + common_base::span!(_guard, "DebugPipeline::new"); + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Debug pipeline layout"), + push_constant_ranges: &[], + bind_group_layouts: &[&global_layouts.globals, &layout.locals], + }); + + let samples = match aa_mode { + AaMode::None | AaMode::Fxaa => 1, + // TODO: Ensure sampling in the shader is exactly between the 4 texels + AaMode::MsaaX4 => 4, + AaMode::MsaaX8 => 8, + AaMode::MsaaX16 => 16, + }; + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Debug pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: vs_module, + entry_point: "main", + buffers: &[Vertex::desc()], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + clamp_depth: false, + polygon_mode: wgpu::PolygonMode::Fill, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::GreaterEqual, + stencil: wgpu::StencilState { + front: wgpu::StencilFaceState::IGNORE, + back: wgpu::StencilFaceState::IGNORE, + read_mask: !0, + write_mask: !0, + }, + bias: wgpu::DepthBiasState { + constant: 0, + slope_scale: 0.0, + clamp: 0.0, + }, + }), + multisample: wgpu::MultisampleState { + count: samples, + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: fs_module, + entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: wgpu::TextureFormat::Rgba16Float, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + }); + + Self { + pipeline: render_pipeline, + } + } +} + +pub struct DebugLayout { + pub locals: wgpu::BindGroupLayout, +} + +impl DebugLayout { + pub fn new(device: &wgpu::Device) -> Self { + Self { + locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }), + } + } + + pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts) -> BoundLocals { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &self.locals, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: locals.buf().as_entire_binding(), + }], + }); + + BoundLocals { + bind_group, + with: locals, + } + } +} diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 51e70de035..bb55a288a0 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1,5 +1,6 @@ pub mod blit; pub mod clouds; +pub mod debug; pub mod figure; pub mod fluid; pub mod lod_terrain; diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 728fbb2ac6..381842043d 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -21,7 +21,7 @@ use super::{ mesh::Mesh, model::{DynamicModel, Model}, pipelines::{ - blit, clouds, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup, + blit, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup, }, texture::Texture, @@ -48,6 +48,7 @@ struct Layouts { global: GlobalsLayouts, clouds: clouds::CloudsLayout, + debug: debug::DebugLayout, figure: figure::FigureLayout, postprocess: postprocess::PostProcessLayout, shadow: shadow::ShadowLayout, @@ -266,6 +267,7 @@ impl Renderer { let global = GlobalsLayouts::new(&device); let clouds = clouds::CloudsLayout::new(&device); + let debug = debug::DebugLayout::new(&device); let figure = figure::FigureLayout::new(&device); let postprocess = postprocess::PostProcessLayout::new(&device); let shadow = shadow::ShadowLayout::new(&device); @@ -278,6 +280,7 @@ impl Renderer { global, clouds, + debug, figure, postprocess, shadow, diff --git a/voxygen/src/render/renderer/binding.rs b/voxygen/src/render/renderer/binding.rs index 3f1dd6bb7a..69f109171e 100644 --- a/voxygen/src/render/renderer/binding.rs +++ b/voxygen/src/render/renderer/binding.rs @@ -2,7 +2,7 @@ use super::{ super::{ buffer::Buffer, pipelines::{ - figure, lod_terrain, shadow, sprite, terrain, ui, ColLights, GlobalModel, + debug, figure, lod_terrain, shadow, sprite, terrain, ui, ColLights, GlobalModel, GlobalsBindGroup, }, texture::Texture, @@ -36,6 +36,11 @@ impl Renderer { ) } + pub fn create_debug_bound_locals(&mut self, vals: &[debug::Locals]) -> debug::BoundLocals { + let locals = self.create_consts(vals); + self.layouts.debug.bind_locals(&self.device, locals) + } + pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals { let locals = self.create_consts(vals); self.layouts.ui.bind_locals(&self.device, locals) diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index cfac9f1353..497025e44c 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -4,8 +4,8 @@ use super::{ instances::Instances, model::{DynamicModel, Model, SubModel}, pipelines::{ - blit, clouds, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, terrain, - ui, ColLights, GlobalsBindGroup, + blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, + terrain, ui, ColLights, GlobalsBindGroup, }, }, Renderer, ShadowMap, ShadowMapRenderer, @@ -534,6 +534,18 @@ impl<'pass> FirstPassDrawer<'pass> { render_pass.draw(0..model.len() as u32, 0..1); } + pub fn draw_debug(&mut self) -> DebugDrawer<'_, 'pass> { + let mut render_pass = self.render_pass.scope("debug", self.borrow.device); + + render_pass.set_pipeline(&self.pipelines.debug.pipeline); + set_quad_index_buffer::(&mut render_pass, &self.borrow); + + DebugDrawer { + render_pass, + globals: self.globals, + } + } + pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model) { let mut render_pass = self.render_pass.scope("lod_terrain", self.borrow.device); @@ -601,6 +613,25 @@ impl<'pass> FirstPassDrawer<'pass> { } } +pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> { + render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, + globals: &'pass GlobalsBindGroup, +} + +impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> { + pub fn draw<'data: 'pass>( + &mut self, + model: &'data Model, + locals: &'data debug::BoundLocals, + ) { + self.render_pass + .set_bind_group(0, &self.globals.bind_group, &[]); + self.render_pass.set_bind_group(1, &locals.bind_group, &[]); + self.render_pass.set_vertex_buffer(0, model.buf().slice(..)); + self.render_pass.draw(0..model.len() as u32, 0..1); + } +} + pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> { render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, } diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs index 2fb98e97f4..c1d50cfdb6 100644 --- a/voxygen/src/render/renderer/pipeline_creation.rs +++ b/voxygen/src/render/renderer/pipeline_creation.rs @@ -1,7 +1,7 @@ use super::{ super::{ pipelines::{ - blit, clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, + blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, ui, }, AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode, @@ -14,6 +14,7 @@ use std::sync::Arc; /// All the pipelines pub struct Pipelines { + pub debug: debug::DebugPipeline, pub figure: figure::FigurePipeline, pub fluid: fluid::FluidPipeline, pub lod_terrain: lod_terrain::LodTerrainPipeline, @@ -32,6 +33,7 @@ pub struct Pipelines { /// Pipelines that are needed to render 3D stuff in-game /// Use to decouple interface pipeline creation when initializing the renderer pub struct IngamePipelines { + debug: debug::DebugPipeline, figure: figure::FigurePipeline, fluid: fluid::FluidPipeline, lod_terrain: lod_terrain::LodTerrainPipeline, @@ -66,6 +68,7 @@ pub struct InterfacePipelines { impl Pipelines { pub fn consolidate(interface: InterfacePipelines, ingame: IngamePipelines) -> Self { Self { + debug: ingame.debug, figure: ingame.figure, fluid: ingame.fluid, lod_terrain: ingame.lod_terrain, @@ -86,6 +89,8 @@ impl Pipelines { struct ShaderModules { skybox_vert: wgpu::ShaderModule, skybox_frag: wgpu::ShaderModule, + debug_vert: wgpu::ShaderModule, + debug_frag: wgpu::ShaderModule, figure_vert: wgpu::ShaderModule, figure_frag: wgpu::ShaderModule, terrain_vert: wgpu::ShaderModule, @@ -232,6 +237,8 @@ impl ShaderModules { Ok(Self { skybox_vert: create_shader("skybox-vert", ShaderKind::Vertex)?, skybox_frag: create_shader("skybox-frag", ShaderKind::Fragment)?, + debug_vert: create_shader("debug-vert", ShaderKind::Vertex)?, + debug_frag: create_shader("debug-frag", ShaderKind::Fragment)?, figure_vert: create_shader("figure-vert", ShaderKind::Vertex)?, figure_frag: create_shader("figure-frag", ShaderKind::Fragment)?, terrain_vert: create_shader("terrain-vert", ShaderKind::Vertex)?, @@ -352,7 +359,7 @@ fn create_interface_pipelines( fn create_ingame_and_shadow_pipelines( needs: PipelineNeeds, pool: &rayon::ThreadPool, - tasks: [Task; 12], + tasks: [Task; 13], ) -> IngameAndShadowPipelines { prof_span!(_guard, "create_ingame_and_shadow_pipelines"); @@ -365,6 +372,7 @@ fn create_ingame_and_shadow_pipelines( } = needs; let [ + debug_task, skybox_task, figure_task, terrain_task, @@ -384,6 +392,22 @@ fn create_ingame_and_shadow_pipelines( // TODO: pass in format of target color buffer + // Pipeline for rendering debug shapes + let create_debug = || { + debug_task.run( + || { + debug::DebugPipeline::new( + device, + &shaders.debug_vert, + &shaders.debug_frag, + &layouts.global, + &layouts.debug, + mode.aa, + ) + }, + "debug pipeline creation", + ) + }; // Pipeline for rendering skyboxes let create_skybox = || { skybox_task.run( @@ -596,7 +620,7 @@ fn create_ingame_and_shadow_pipelines( ) }; - let j1 = || pool.join(create_skybox, create_figure); + let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure)); let j2 = || pool.join(create_terrain, create_fluid); let j3 = || pool.join(create_sprite, create_particle); let j4 = || pool.join(create_lod_terrain, create_clouds); @@ -610,7 +634,10 @@ fn create_ingame_and_shadow_pipelines( // Ignore this let ( - (((skybox, figure), (terrain, fluid)), ((sprite, particle), (lod_terrain, clouds))), + ( + ((debug, (skybox, figure)), (terrain, fluid)), + ((sprite, particle), (lod_terrain, clouds)), + ), ((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)), ) = pool.join( || pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)), @@ -619,6 +646,7 @@ fn create_ingame_and_shadow_pipelines( IngameAndShadowPipelines { ingame: IngamePipelines { + debug, figure, fluid, lod_terrain, diff --git a/voxygen/src/render/renderer/shaders.rs b/voxygen/src/render/renderer/shaders.rs index 3f1b0bb2b5..eb3a148a89 100644 --- a/voxygen/src/render/renderer/shaders.rs +++ b/voxygen/src/render/renderer/shaders.rs @@ -50,6 +50,8 @@ impl assets::Compound for Shaders { "point-light-shadows-vert", "skybox-vert", "skybox-frag", + "debug-vert", + "debug-frag", "figure-frag", "terrain-vert", "terrain-frag", diff --git a/voxygen/src/scene/debug.rs b/voxygen/src/scene/debug.rs new file mode 100644 index 0000000000..507d40b62b --- /dev/null +++ b/voxygen/src/scene/debug.rs @@ -0,0 +1,133 @@ +use crate::render::{ + Bound, Consts, DebugLocals, DebugVertex, FirstPassDrawer, Mesh, + Model, Quad, Renderer, Tri, +}; +use hashbrown::{HashMap, HashSet}; +use vek::*; + +#[derive(Debug)] +pub enum DebugShape { + Line([Vec3; 2]), + Cylinder { radius: f32, height: f32 }, +} + +impl DebugShape { + pub fn mesh(&self) -> Mesh { + use core::f32::consts::PI; + use DebugShape::*; + let mut mesh = Mesh::new(); + let tri = |x: Vec3, y: Vec3, z: Vec3| { + Tri::::new(x.into(), y.into(), z.into()) + }; + let quad = |x: Vec3, y: Vec3, z: Vec3, w: Vec3| { + Quad::::new(x.into(), y.into(), z.into(), w.into()) + }; + match self { + Line([a, b]) => { + let h = Vec3::new(0.0, 1.0, 0.0); + mesh.push_quad(quad(*a, a + h, b + h, *b)); + }, + Cylinder { radius, height } => { + const SUBDIVISIONS: usize = 16; + for i in 0..SUBDIVISIONS { + let angle = |j: usize| (j as f32 / SUBDIVISIONS as f32) * 2.0 * PI; + let a = Vec3::zero(); + let b = Vec3::new(radius * angle(i).cos(), radius * angle(i).sin(), 0.0); + let c = Vec3::new( + radius * angle(i + 1).cos(), + radius * angle(i + 1).sin(), + 0.0, + ); + let h = Vec3::new(0.0, 0.0, *height); + mesh.push_tri(tri(a, b, c)); + mesh.push_quad(quad(b, c, c + h, b + h)); + mesh.push_tri(tri(a + h, b + h, c + h)); + } + }, + } + mesh + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct DebugShapeId(usize); + +pub struct Debug { + next_shape_id: DebugShapeId, + pending_shapes: HashMap, + pending_locals: HashMap, + pending_deletes: HashSet, + models: HashMap>, + //locals: HashMap>, + locals: HashMap>>, +} + +impl Debug { + pub fn new() -> Debug { + Debug { + next_shape_id: DebugShapeId(0), + pending_shapes: HashMap::new(), + pending_locals: HashMap::new(), + pending_deletes: HashSet::new(), + models: HashMap::new(), + locals: HashMap::new(), + } + } + + pub fn add_shape(&mut self, shape: DebugShape) -> DebugShapeId { + let id = DebugShapeId(self.next_shape_id.0); + self.next_shape_id.0 += 1; + self.pending_shapes.insert(id, shape); + id + } + + pub fn set_pos_and_color(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4]) { + self.pending_locals.insert(id, (pos, color)); + } + + pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); } + + pub fn maintain(&mut self, renderer: &mut Renderer) { + for (id, shape) in self.pending_shapes.drain() { + self.models + .insert(id, renderer.create_model(&shape.mesh()).unwrap()); + /*self.locals.insert( + id, + renderer.create_consts(&[DebugLocals { + pos: [0.0; 4], + color: [1.0, 0.0, 0.0, 1.0], + }]), + );*/ + } + for (id, (pos, color)) in self.pending_locals.drain() { + // TODO: what are the efficiency ramifications of creating the constants each + // time instead of caching them and binding them? UI seems to + // recreate them each time they change? + /*if let Some(locals) = self.locals.get_mut(&id) { + let new_locals = [DebugLocals { pos, color }]; + renderer.update_consts(locals, &new_locals); + renderer.create_debug_bound_locals(new_locals); + }*/ + let new_locals = [DebugLocals { pos, color }]; + self.locals + .insert(id, renderer.create_debug_bound_locals(&new_locals)); + } + for id in self.pending_deletes.drain() { + self.models.remove(&id); + self.locals.remove(&id); + } + } + + pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>) { + let mut debug_drawer = drawer.draw_debug(); + for (id, model) in self.models.iter() { + if let Some(locals) = self.locals.get(id) { + debug_drawer.draw(model, locals); + } + } + } +} + +impl Default for Debug { + fn default() -> Debug { Debug::new() } +} diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 0748379f5c..67cdea354b 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1,4 +1,5 @@ pub mod camera; +pub mod debug; pub mod figure; pub mod lod; pub mod math; @@ -8,6 +9,7 @@ pub mod terrain; pub use self::{ camera::{Camera, CameraMode}, + debug::{Debug, DebugShape, DebugShapeId}, figure::FigureMgr, lod::Lod, particle::ParticleMgr, @@ -79,6 +81,7 @@ pub struct Scene { skybox: Skybox, terrain: Terrain, + pub debug: Debug, pub lod: Lod, loaded_distance: f32, /// x coordinate is sea level (minimum height for any land chunk), and y @@ -289,6 +292,7 @@ impl Scene { model: renderer.create_model(&create_skybox_mesh()).unwrap(), }, terrain, + debug: Debug::new(), lod, loaded_distance: 0.0, map_bounds: Vec2::new( @@ -653,6 +657,9 @@ impl Scene { // Maintain LoD. self.lod.maintain(renderer); + // Maintain debug shapes + self.debug.maintain(renderer); + // Maintain the terrain. let (_visible_bounds, visible_light_volume, visible_psr_bounds) = self.terrain.maintain( renderer, @@ -1109,6 +1116,9 @@ impl Scene { // Render particle effects. self.particle_mgr .render(&mut first_pass.draw_particles(), scene_data); + + // Render debug shapes + self.debug.render(&mut first_pass); } } } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 9a5a8a8591..9d8bfa6b1c 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -39,7 +39,7 @@ use crate::{ key_state::KeyState, menu::char_selection::CharSelectionState, render::Renderer, - scene::{camera, terrain::Interaction, CameraMode, Scene, SceneData}, + scene::{camera, terrain::Interaction, CameraMode, DebugShape, DebugShapeId, Scene, SceneData}, settings::Settings, window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, @@ -73,6 +73,7 @@ pub struct SessionState { selected_entity: Option<(specs::Entity, std::time::Instant)>, interactable: Option, saved_zoom_dist: Option, + player_hitbox: DebugShapeId, } /// Represents an active game session (i.e., the one being played). @@ -100,6 +101,10 @@ impl SessionState { let hud = Hud::new(global_state, &client.borrow()); let walk_forward_dir = scene.camera().forward_xy(); let walk_right_dir = scene.camera().right_xy(); + let player_hitbox = scene.debug.add_shape(DebugShape::Cylinder { + radius: 0.4, + height: 1.75, + }); Self { scene, @@ -120,6 +125,7 @@ impl SessionState { selected_entity: None, interactable: None, saved_zoom_dist: None, + player_hitbox, } } @@ -139,6 +145,17 @@ impl SessionState { span!(_guard, "tick", "Session::tick"); let mut client = self.client.borrow_mut(); + if let Some(player_pos) = client + .state() + .ecs() + .read_component::() + .get(client.entity()) + { + let pos = [player_pos.0.x, player_pos.0.y, player_pos.0.z, 0.0]; + self.scene + .debug + .set_pos_and_color(self.player_hitbox, pos, [1.0, 0.0, 0.0, 0.5]); + } for event in client.tick(self.inputs.clone(), dt, crate::ecs::sys::add_local_systems)? { match event { client::Event::Chat(m) => {