mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement a Debug pipeline for hitboxes and pathfinding lines.
- Implements mesh generation for cylinders and lines. - Implements an Id-allocator so that clients can mutate positions efficiently. - Is split into pipeline and scene modules. - Contains simple shaders that just pass through a position and color.
This commit is contained in:
parent
254c2fb868
commit
364890653f
19
assets/voxygen/shaders/debug-frag.glsl
Normal file
19
assets/voxygen/shaders/debug-frag.glsl
Normal file
@ -0,0 +1,19 @@
|
||||
#version 420 core
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
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;
|
||||
}
|
20
assets/voxygen/shaders/debug-vert.glsl
Normal file
20
assets/voxygen/shaders/debug-vert.glsl
Normal file
@ -0,0 +1,20 @@
|
||||
#version 420 core
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
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);
|
||||
}
|
@ -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,
|
||||
|
201
voxygen/src/render/pipelines/debug.rs
Normal file
201
voxygen/src/render/pipelines/debug.rs
Normal file
@ -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<wgpu::IndexFormat> =
|
||||
// Some(wgpu::IndexFormat::Uint32);
|
||||
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
|
||||
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() 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<Consts<Locals>>;
|
||||
|
||||
/*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<Vertex> = (),
|
||||
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
|
||||
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA),
|
||||
//tgt_depth: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_TEST,
|
||||
tgt_depth: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::PASS_TEST,
|
||||
}
|
||||
}*/
|
||||
|
||||
impl From<Vec3<f32>> for Vertex {
|
||||
fn from(pos: Vec3<f32>) -> 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<Locals>) -> 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod blit;
|
||||
pub mod clouds;
|
||||
pub mod debug;
|
||||
pub mod figure;
|
||||
pub mod fluid;
|
||||
pub mod lod_terrain;
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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::<debug::Vertex>(&mut render_pass, &self.borrow);
|
||||
|
||||
DebugDrawer {
|
||||
render_pass,
|
||||
globals: self.globals,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model<lod_terrain::Vertex>) {
|
||||
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<debug::Vertex>,
|
||||
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>>,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
133
voxygen/src/scene/debug.rs
Normal file
133
voxygen/src/scene/debug.rs
Normal file
@ -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<f32>; 2]),
|
||||
Cylinder { radius: f32, height: f32 },
|
||||
}
|
||||
|
||||
impl DebugShape {
|
||||
pub fn mesh(&self) -> Mesh<DebugVertex> {
|
||||
use core::f32::consts::PI;
|
||||
use DebugShape::*;
|
||||
let mut mesh = Mesh::new();
|
||||
let tri = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>| {
|
||||
Tri::<DebugVertex>::new(x.into(), y.into(), z.into())
|
||||
};
|
||||
let quad = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>, w: Vec3<f32>| {
|
||||
Quad::<DebugVertex>::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<DebugShapeId, DebugShape>,
|
||||
pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4])>,
|
||||
pending_deletes: HashSet<DebugShapeId>,
|
||||
models: HashMap<DebugShapeId, Model<DebugVertex>>,
|
||||
//locals: HashMap<DebugShapeId, Consts<DebugLocals>>,
|
||||
locals: HashMap<DebugShapeId, Bound<Consts<DebugLocals>>>,
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
@ -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<TerrainChunk>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Interactable>,
|
||||
saved_zoom_dist: Option<f32>,
|
||||
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::<Pos>()
|
||||
.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) => {
|
||||
|
Loading…
Reference in New Issue
Block a user