Almost implement instance merging for sprites (validation error and segfault)

This commit is contained in:
Imbris 2021-01-30 22:50:03 -05:00
parent 0ba1fe6a7d
commit 3a6a96b70b
14 changed files with 718 additions and 429 deletions

View File

@ -22,9 +22,9 @@ layout(location = 2) flat in float f_light;
layout(location = 3) in vec2 f_uv_pos; layout(location = 3) in vec2 f_uv_pos;
layout(location = 4) in vec2 f_inst_light; layout(location = 4) in vec2 f_inst_light;
layout(set = 4, binding = 0) layout(set = 3, binding = 0)
uniform texture2D t_col_light; uniform texture2D t_col_light;
layout(set = 4, binding = 1) layout(set = 3, binding = 1)
uniform sampler s_col_light; uniform sampler s_col_light;
layout(location = 0) out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;

View File

@ -16,29 +16,22 @@
#include <srgb.glsl> #include <srgb.glsl>
#include <sky.glsl> #include <sky.glsl>
layout(location = 0) in vec3 v_pos; layout(location = 0) in vec4 inst_mat0;
layout(location = 1) in uint v_atlas_pos; layout(location = 1) in vec4 inst_mat1;
layout(location = 2) in uint v_norm_ao; layout(location = 2) in vec4 inst_mat2;
layout(location = 3) in uint inst_pos_ori; layout(location = 3) in vec4 inst_mat3;
layout(location = 4) in vec4 inst_mat0; // TODO: is there a better way to pack the various vertex attributes?
layout(location = 5) in vec4 inst_mat1; // TODO: ori is unused
layout(location = 6) in vec4 inst_mat2; layout(location = 4) in uint inst_pos_ori;
layout(location = 7) in vec4 inst_mat3; layout(location = 5) in uint inst_vert_page; // NOTE: this could fit in less bits
layout(location = 8) in vec4 inst_light; // TODO: do we need this many bits for light and glow?
layout(location = 9) in float inst_wind_sway; layout(location = 6) in float inst_light;
layout(location = 7) in float inst_glow;
layout(location = 8) in float model_wind_sway; // NOTE: this only varies per model
layout(location = 9) in float model_z_scale; // NOTE: this only varies per model
struct SpriteLocals { layout(set = 0, binding = 12) uniform utexture2D t_sprite_verts;
mat4 mat; layout(set = 0, binding = 13) uniform sampler s_sprite_verts;
vec4 wind_sway;
vec4 offs;
};
layout(std140, set = 3, binding = 0)
uniform u_locals {
mat4 mat;
vec4 wind_sway;
vec4 offs;
};
layout (std140, set = 2, binding = 0) layout (std140, set = 2, binding = 0)
uniform u_terrain_locals { uniform u_terrain_locals {
@ -47,6 +40,7 @@ uniform u_terrain_locals {
ivec4 atlas_offs; ivec4 atlas_offs;
}; };
// TODO: consider grouping into vec4's
layout(location = 0) out vec3 f_pos; layout(location = 0) out vec3 f_pos;
layout(location = 1) flat out vec3 f_norm; layout(location = 1) flat out vec3 f_norm;
layout(location = 2) flat out float f_light; layout(location = 2) flat out float f_light;
@ -57,38 +51,63 @@ const float SCALE = 1.0 / 11.0;
const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2; const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2;
const int EXTRA_NEG_Z = 32768; const int EXTRA_NEG_Z = 32768;
const int VERT_EXTRA_NEG_Z = 128;
const int VERT_PAGE_SIZE = 256;
void main() { void main() {
// Matrix to transform this sprite instance from model space to chunk space
mat4 inst_mat; mat4 inst_mat;
inst_mat[0] = inst_mat0; inst_mat[0] = inst_mat0;
inst_mat[1] = inst_mat1; inst_mat[1] = inst_mat1;
inst_mat[2] = inst_mat2; inst_mat[2] = inst_mat2;
inst_mat[3] = inst_mat3; inst_mat[3] = inst_mat3;
vec3 inst_offs = model_offs - focus_off.xyz;
f_inst_light = inst_light.xy; // Worldpos of the chunk that this sprite is in
vec3 chunk_offs = model_offs - focus_off.xyz;
vec3 v_pos_ = wind_sway.xyz * v_pos; f_inst_light = vec2(inst_light, inst_glow);
f_pos = (inst_mat * vec4(v_pos_, 1.0)).xyz * SCALE + inst_offs; // Index of the vertex data in the 1D vertex texture
int vertex_index = int(gl_VertexIndex % VERT_PAGE_SIZE + inst_vert_page);
const int WIDTH = 16384; // TODO: temp
ivec2 tex_coords = ivec2(vertex_index % WIDTH, vertex_index / WIDTH);
uvec2 pos_atlas_pos_norm_ao = texelFetch(usampler2D(t_sprite_verts, s_sprite_verts), tex_coords, 0).xy;
uint v_pos_norm = pos_atlas_pos_norm_ao.x;
uint v_atlas_pos = pos_atlas_pos_norm_ao.y;
// Expand the model vertex position bits into float values
vec3 v_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 8, 16)) & uvec3(0xFu, 0xFu, 0x0FFFu)) - ivec3(0, 0, VERT_EXTRA_NEG_Z));
// Transform into chunk space and scale
f_pos = (inst_mat * vec4(v_pos, 1.0)).xyz;
// Transform info world space
f_pos += chunk_offs;
// Terrain 'pop-in' effect // Terrain 'pop-in' effect
f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0));
f_pos += wind_sway.w * vec3( // Wind sway effect
f_pos += model_wind_sway * vec3(
sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25),
0.0 0.0
//) * pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR; // NOTE: could potentially replace `v_pos.z * model_z_scale` with a calculation using `inst_chunk_pos` from below
) * v_pos_.z * SCALE_FACTOR; //) * pow(abs(v_pos.z * model_z_scale), 1.3) * SCALE_FACTOR;
) * v_pos.z * model_z_scale * SCALE_FACTOR;
vec3 norm = (inst_mat[(v_norm_ao >> 1u) & 3u].xyz); // Determine normal
f_norm = mix(-norm, norm, v_norm_ao & 1u); vec3 norm = (inst_mat[(v_pos_norm >> 30u) & 3u].xyz);
f_norm = mix(-norm, norm, v_pos_norm >> 29u & 1u);
f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));/* + 0.5*/; // Expand atlas tex coords to floats
// NOTE: Could defer to fragment shader if we are vert heavy
f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));;
// Position of the sprite block in the chunk
// Used solely for highlighting the selected sprite
vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
// Select glowing // Select glowing
vec3 sprite_pos = floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE) + inst_offs; vec3 sprite_pos = inst_chunk_pos + chunk_offs;
f_light = (select_pos.w > 0 && select_pos.xyz == sprite_pos) ? 5.0 : 1.0; f_light = (select_pos.w > 0 && select_pos.xyz == sprite_pos) ? 5.0 : 1.0;
gl_Position = gl_Position =

View File

@ -28,6 +28,9 @@ impl<V: Vertex> Mesh<V> {
/// Get a mutable slice referencing the vertices of this mesh. /// Get a mutable slice referencing the vertices of this mesh.
pub fn vertices_mut(&mut self) -> &mut [V] { &mut self.verts } pub fn vertices_mut(&mut self) -> &mut [V] { &mut self.verts }
/// Get a mutable vec referencing the vertices of this mesh.
pub fn vertices_mut_vec(&mut self) -> &mut Vec<V> { &mut self.verts }
/// Push a new vertex onto the end of this mesh. /// Push a new vertex onto the end of this mesh.
pub fn push(&mut self, vert: V) { self.verts.push(vert); } pub fn push(&mut self, vert: V) { self.verts.push(vert); }

View File

@ -30,7 +30,11 @@ pub use self::{
postprocess::Locals as PostProcessLocals, postprocess::Locals as PostProcessLocals,
shadow::{Locals as ShadowLocals, PointLightMatrix}, shadow::{Locals as ShadowLocals, PointLightMatrix},
skybox::{create_mesh as create_skybox_mesh, Vertex as SkyboxVertex}, skybox::{create_mesh as create_skybox_mesh, Vertex as SkyboxVertex},
sprite::{Instance as SpriteInstance, Locals as SpriteLocals, Vertex as SpriteVertex}, sprite::{
create_verts_texture as create_sprite_verts_texture, Instance as SpriteInstance,
SpriteGlobalsBindGroup, Vertex as SpriteVertex,
VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE,
},
terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex}, terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex},
ui::{ ui::{
create_quad as create_ui_quad, create_quad as create_ui_quad,
@ -42,9 +46,9 @@ pub use self::{
}, },
renderer::{ renderer::{
drawer::{ drawer::{
ChunkSpriteDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer,
ParticleDrawer, PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer,
TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, UiDrawer, TerrainShadowDrawer, ThirdPassDrawer, UiDrawer,
}, },
ColLightInfo, Renderer, ColLightInfo, Renderer,
}, },

View File

@ -104,7 +104,7 @@ impl LodData {
array_layer_count: None, array_layer_count: None,
}; };
renderer.create_texture_with_data_raw( renderer.create_texture_with_data_raw::<4>(
&texture_info, &texture_info,
&view_info, &view_info,
&sampler_info, &sampler_info,

View File

@ -253,16 +253,14 @@ pub struct GlobalsLayouts {
} }
pub struct ColLights<Locals> { pub struct ColLights<Locals> {
pub bind_group: wgpu::BindGroup, pub(super) bind_group: wgpu::BindGroup,
pub texture: Texture, pub texture: Texture,
phantom: std::marker::PhantomData<Locals>, phantom: std::marker::PhantomData<Locals>,
} }
impl GlobalsLayouts { impl GlobalsLayouts {
pub fn new(device: &wgpu::Device) -> Self { pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { vec![
label: Some("Globals layout"),
entries: &[
// Global uniform // Global uniform
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -388,7 +386,13 @@ impl GlobalsLayouts {
}, },
count: None, count: None,
}, },
], ]
}
pub fn new(device: &wgpu::Device) -> Self {
let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Globals layout"),
entries: &Self::base_globals_layout(),
}); });
let col_light = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let col_light = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -470,17 +474,13 @@ impl GlobalsLayouts {
} }
} }
pub fn bind( // Note: this allocation serves the purpose of not having to duplicate code
&self, pub fn bind_base_globals<'a>(
device: &wgpu::Device, global_model: &'a GlobalModel,
global_model: &GlobalModel, lod_data: &'a lod_terrain::LodData,
lod_data: &lod_terrain::LodData, noise: &'a Texture,
noise: &Texture, ) -> Vec<wgpu::BindGroupEntry<'a>> {
) -> GlobalsBindGroup { vec![
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.globals,
entries: &[
// Global uniform // Global uniform
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
@ -537,7 +537,20 @@ impl GlobalsLayouts {
binding: 11, binding: 11,
resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler), resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
}, },
], ]
}
pub fn bind(
&self,
device: &wgpu::Device,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
noise: &Texture,
) -> GlobalsBindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.globals,
entries: &Self::bind_base_globals(global_model, lod_data, noise),
}); });
GlobalsBindGroup { bind_group } GlobalsBindGroup { bind_group }

View File

@ -116,7 +116,7 @@ pub fn create_col_lights(
array_layer_count: None, array_layer_count: None,
}; };
renderer.create_texture_with_data_raw( renderer.create_texture_with_data_raw::<4>(
&texture_info, &texture_info,
&view_info, &view_info,
&sampler_info, &sampler_info,

View File

@ -1,45 +1,51 @@
use super::super::{AaMode, Bound, Consts, GlobalsLayouts, TerrainLayout, Vertex as VertexTrait}; use super::{
super::{
AaMode, Bound, Consts, GlobalsLayouts, Mesh, Renderer, TerrainLayout, Texture,
Vertex as VertexTrait,
},
lod_terrain, GlobalModel,
};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use core::fmt; use core::fmt;
use std::mem; use std::mem;
use vek::*; use vek::*;
pub const VERT_PAGE_SIZE: u32 = 256;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex { pub struct Vertex {
pos: [f32; 3], pos_norm: u32,
// Because we try to restrict terrain sprite data to a 128×128 block // Because we try to restrict terrain sprite data to a 128×128 block
// we need an offset into the texture atlas. // we need an offset into the texture atlas.
atlas_pos: u32, atlas_pos: u32,
// ____BBBBBBBBGGGGGGGGRRRRRRRR /* ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col", * col: u32 = "v_col",
// ...AANNN * .....NNN
// A = AO * A = AO
// N = Normal * N = Normal
norm_ao: u32, *norm: u32, */
} }
impl fmt::Display for Vertex { // TODO: fix?
/*impl fmt::Display for Vertex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vertex") f.debug_struct("Vertex")
.field("pos", &Vec3::<f32>::from(self.pos)) .field("pos_norm", &Vec3::<f32>::from(self.pos))
.field( .field(
"atlas_pos", "atlas_pos",
&Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF), &Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF),
) )
.field("norm_ao", &self.norm_ao)
.finish() .finish()
} }
} }*/
impl Vertex { impl Vertex {
// NOTE: Limit to 16 (x) × 16 (y) × 32 (z). // NOTE: Limit to 16 (x) × 16 (y) × 32 (z).
#[allow(clippy::collapsible_if)] #[allow(clippy::collapsible_if)]
pub fn new( pub fn new(atlas_pos: Vec2<u16>, pos: Vec3<f32>, norm: Vec3<f32>) -> Self {
atlas_pos: Vec2<u16>, const VERT_EXTRA_NEG_Z: i32 = 128; // NOTE: change if number of bits changes below, also we might not need this if meshing always produces positives values for sprites (I have no idea)
pos: Vec3<f32>,
norm: Vec3<f32>, /* , col: Rgb<f32>, ao: f32 */
) -> Self {
let norm_bits = if norm.x != 0.0 { let norm_bits = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 } if norm.x < 0.0 { 0 } else { 1 }
} else if norm.y != 0.0 { } else if norm.y != 0.0 {
@ -54,13 +60,15 @@ impl Vertex {
// | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12 // | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
// | if meta { 1 } else { 0 } << 28 // | if meta { 1 } else { 0 } << 28
// | (norm_bits & 0x7) << 29, // | (norm_bits & 0x7) << 29,
pos: pos.into_array(), pos_norm: ((pos.x as u32) & 0x00FF) // NOTE: temp hack, this doesn't need 8 bits
| ((pos.y as u32) & 0x00FF) << 8
| (((pos.z as i32 + VERT_EXTRA_NEG_Z).max(0).min(1 << 12) as u32) & 0x0FFF) << 16
| (norm_bits & 0x7) << 29,
atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16, atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16,
norm_ao: norm_bits,
} }
} }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { /*fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 3] = const ATTRIBUTES: [wgpu::VertexAttribute; 3] =
wgpu::vertex_attr_array![0 => Float3, 1 => Uint, 2 => Uint]; wgpu::vertex_attr_array![0 => Float3, 1 => Uint, 2 => Uint];
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
@ -68,53 +76,130 @@ impl Vertex {
step_mode: wgpu::InputStepMode::Vertex, step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES, attributes: &ATTRIBUTES,
} }
}*/
} }
impl Default for Vertex {
fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(), Vec3::zero()) }
} }
impl VertexTrait for Vertex { impl VertexTrait for Vertex {
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress; const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
} }
pub fn create_verts_texture(renderer: &mut Renderer, mut mesh: Mesh<Vertex>) -> Texture {
let mut verts = mesh.vertices_mut_vec();
let format = wgpu::TextureFormat::Rg32Uint;
// TODO: temp
const WIDTH: u32 = 16384;
let height = verts.len() as u32 / WIDTH;
// Fill in verts to full texture size
verts.resize_with(height as usize * WIDTH as usize, Vertex::default);
let texture_info = wgpu::TextureDescriptor {
label: Some("Sprite verts"),
size: wgpu::Extent3d {
width: WIDTH,
height,
depth: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::Repeat,
address_mode_v: wgpu::AddressMode::Repeat,
address_mode_w: wgpu::AddressMode::Repeat,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
};
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
renderer.create_texture_with_data_raw::<8>(
&texture_info,
&view_info,
&sampler_info,
bytemuck::cast_slice(verts),
)
}
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance { pub struct Instance {
pos_ori: u32,
inst_mat0: [f32; 4], inst_mat0: [f32; 4],
inst_mat1: [f32; 4], inst_mat1: [f32; 4],
inst_mat2: [f32; 4], inst_mat2: [f32; 4],
inst_mat3: [f32; 4], inst_mat3: [f32; 4],
inst_light: [f32; 4], pos_ori: u32,
inst_wind_sway: f32, inst_vert_page: u32,
inst_light: f32,
inst_glow: f32,
model_wind_sway: f32,
model_z_scale: f32,
} }
impl Instance { impl Instance {
pub fn new( pub fn new(
mat: Mat4<f32>, mat: Mat4<f32>,
wind_sway: f32, wind_sway: f32,
z_scale: f32,
pos: Vec3<i32>, pos: Vec3<i32>,
ori_bits: u8, ori_bits: u8,
light: f32, light: f32,
glow: f32, glow: f32,
vert_page: u32,
) -> Self { ) -> Self {
const EXTRA_NEG_Z: i32 = 32768; const EXTRA_NEG_Z: i32 = 32768;
let mat_arr = mat.into_col_arrays(); let mat_arr = mat.into_col_arrays();
Self { Self {
pos_ori: ((pos.x as u32) & 0x003F)
| ((pos.y as u32) & 0x003F) << 6
| (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12
| (u32::from(ori_bits) & 0x7) << 29,
inst_mat0: mat_arr[0], inst_mat0: mat_arr[0],
inst_mat1: mat_arr[1], inst_mat1: mat_arr[1],
inst_mat2: mat_arr[2], inst_mat2: mat_arr[2],
inst_mat3: mat_arr[3], inst_mat3: mat_arr[3],
inst_light: [light, glow, 1.0, 1.0], pos_ori: ((pos.x as u32) & 0x003F)
inst_wind_sway: wind_sway, | ((pos.y as u32) & 0x003F) << 6
| (((pos.z + EXTRA_NEG_Z).max(0).min(1 << 16) as u32) & 0xFFFF) << 12
| (u32::from(ori_bits) & 0x7) << 29,
inst_vert_page: vert_page,
inst_light: light,
inst_glow: glow,
model_wind_sway: wind_sway,
model_z_scale: z_scale,
} }
} }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 7] = wgpu::vertex_attr_array![3 => Uint, 4 => Float4, 5 => Float4, 6 => Float4,7 => Float4, 8 => Float4, 9 => Float]; const ATTRIBUTES: [wgpu::VertexAttribute; 10] = wgpu::vertex_attr_array![
0 => Float4,
1 => Float4,
2 => Float4,
3 => Float4,
4 => Uint,
5 => Uint,
6 => Float,
7 => Float,
8 => Float,
9 => Float,
];
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress, array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Instance, step_mode: wgpu::InputStepMode::Instance,
@ -124,10 +209,12 @@ impl Instance {
} }
impl Default for Instance { impl Default for Instance {
fn default() -> Self { Self::new(Mat4::identity(), 0.0, Vec3::zero(), 0, 1.0, 0.0) } fn default() -> Self { Self::new(Mat4::identity(), 0.0, 0.0, Vec3::zero(), 0, 1.0, 0.0, 0) }
} }
#[repr(C)] // TODO: ColLightsWrapper instead?
pub struct Locals;
/*#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals { pub struct Locals {
// Each matrix performs rotatation, translation, and scaling, relative to the sprite // Each matrix performs rotatation, translation, and scaling, relative to the sprite
@ -138,8 +225,6 @@ pub struct Locals {
offs: [f32; 4], offs: [f32; 4],
} }
pub type BoundLocals = Bound<Consts<Locals>>;
impl Default for Locals { impl Default for Locals {
fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) } fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) }
} }
@ -152,16 +237,53 @@ impl Locals {
offs: [offs.x, offs.y, offs.z, 0.0], offs: [offs.x, offs.y, offs.z, 0.0],
} }
} }
}*/
pub struct SpriteGlobalsBindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
pub(in super::super) sprite_verts: Texture,
} }
//pub type BoundLocals = Bound<Consts<Locals>>;
pub struct SpriteLayout { pub struct SpriteLayout {
pub locals: wgpu::BindGroupLayout, pub globals: wgpu::BindGroupLayout,
//pub locals: wgpu::BindGroupLayout,
} }
impl SpriteLayout { impl SpriteLayout {
pub fn new(device: &wgpu::Device) -> Self { pub fn new(device: &wgpu::Device) -> Self {
let mut entries = GlobalsLayouts::base_globals_layout();
debug_assert_eq!(12, entries.len()); // To remember to adjust the bindings below
entries.extend_from_slice(&[
// sprite verts (t_sprite_verts)
wgpu::BindGroupLayoutEntry {
binding: 12,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Uint,
view_dimension: wgpu::TextureViewDimension::D1,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 13,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Sampler {
filtering: false,
comparison: false,
},
count: None,
},
]);
Self { Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { globals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &entries,
}),
/*locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None, label: None,
entries: &[ entries: &[
// locals // locals
@ -184,11 +306,57 @@ impl SpriteLayout {
} }
}, },
], ],
}), }),*/
} }
} }
pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals { fn bind_globals_inner(
&self,
device: &wgpu::Device,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
noise: &Texture,
sprite_verts: &Texture,
) -> wgpu::BindGroup {
let mut entries = GlobalsLayouts::bind_base_globals(global_model, lod_data, noise);
entries.extend_from_slice(&[
// sprite verts (t_sprite_verts)
wgpu::BindGroupEntry {
binding: 12,
resource: wgpu::BindingResource::TextureView(&sprite_verts.view),
},
wgpu::BindGroupEntry {
binding: 13,
resource: wgpu::BindingResource::Sampler(&sprite_verts.sampler),
},
]);
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.globals,
entries: &entries,
})
}
pub fn bind_globals(
&self,
device: &wgpu::Device,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
noise: &Texture,
sprite_verts: Texture,
) -> SpriteGlobalsBindGroup {
let bind_group =
self.bind_globals_inner(device, global_model, lod_data, noise, &sprite_verts);
SpriteGlobalsBindGroup {
bind_group,
sprite_verts,
}
}
/*pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None, label: None,
layout: &self.locals, layout: &self.locals,
@ -202,7 +370,7 @@ impl SpriteLayout {
bind_group, bind_group,
with: locals, with: locals,
} }
} }*/
} }
pub struct SpritePipeline { pub struct SpritePipeline {
@ -226,10 +394,11 @@ impl SpritePipeline {
label: Some("Sprite pipeline layout"), label: Some("Sprite pipeline layout"),
push_constant_ranges: &[], push_constant_ranges: &[],
bind_group_layouts: &[ bind_group_layouts: &[
&global_layout.globals, &layout.globals,
&global_layout.shadow_textures, &global_layout.shadow_textures,
&terrain_layout.locals, &terrain_layout.locals,
&layout.locals, //&layout.locals,
// Note: mergable with globals
&global_layout.col_light, &global_layout.col_light,
], ],
}); });
@ -248,7 +417,7 @@ impl SpritePipeline {
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module: vs_module, module: vs_module,
entry_point: "main", entry_point: "main",
buffers: &[], buffers: &[Instance::desc()],
}, },
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,

View File

@ -320,7 +320,6 @@ impl Renderer {
.ok_or(RenderError::CouldNotFindAdapter)?; .ok_or(RenderError::CouldNotFindAdapter)?;
let limits = wgpu::Limits { let limits = wgpu::Limits {
max_bind_groups: 5,
max_push_constant_size: 64, max_push_constant_size: 64,
..Default::default() ..Default::default()
}; };
@ -1182,7 +1181,7 @@ impl Renderer {
} }
/// Create a new immutable texture from the provided image. /// Create a new immutable texture from the provided image.
pub fn create_texture_with_data_raw( pub fn create_texture_with_data_raw<const BYTES_PER_PIXEL: u32>(
&mut self, &mut self,
texture_info: &wgpu::TextureDescriptor, texture_info: &wgpu::TextureDescriptor,
view_info: &wgpu::TextureViewDescriptor, view_info: &wgpu::TextureViewDescriptor,
@ -1191,7 +1190,7 @@ impl Renderer {
) -> Texture { ) -> Texture {
let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info); let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info);
tex.update( tex.update::<BYTES_PER_PIXEL>(
&self.device, &self.device,
&self.queue, &self.queue,
[0; 2], [0; 2],
@ -1257,7 +1256,7 @@ impl Renderer {
// texture.update(&mut self.encoder, offset, size, data) // texture.update(&mut self.encoder, offset, size, data)
data: &[[u8; 4]], data: &[[u8; 4]],
) { ) {
texture.update( texture.update::<4>(
&self.device, &self.device,
&self.queue, &self.queue,
offset, offset,

View File

@ -20,6 +20,21 @@ impl Renderer {
.bind(&self.device, global_model, lod_data, &self.noise_tex) .bind(&self.device, global_model, lod_data, &self.noise_tex)
} }
pub fn bind_sprite_globals(
&self,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
sprite_verts: Texture,
) -> sprite::SpriteGlobalsBindGroup {
self.layouts.sprite.bind_globals(
&self.device,
global_model,
lod_data,
&self.noise_tex,
sprite_verts,
)
}
pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals { pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals {
let locals = self.create_consts(vals); let locals = self.create_consts(vals);
self.layouts.ui.bind_locals(&self.device, locals) self.layouts.ui.bind_locals(&self.device, locals)
@ -54,10 +69,10 @@ impl Renderer {
self.layouts.shadow.bind_locals(&self.device, locals) self.layouts.shadow.bind_locals(&self.device, locals)
} }
pub fn create_sprite_bound_locals(&mut self, locals: &[sprite::Locals]) -> sprite::BoundLocals { //pub fn create_sprite_bound_locals(&mut self, locals: &[sprite::Locals]) ->
let locals = self.create_consts(locals); // sprite::BoundLocals { let locals = self.create_consts(locals);
self.layouts.sprite.bind_locals(&self.device, locals) // self.layouts.sprite.bind_locals(&self.device, locals)
} //}
pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights<figure::Locals> { pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights<figure::Locals> {
self.layouts.global.bind_col_light(&self.device, col_light) self.layouts.global.bind_col_light(&self.device, col_light)

View File

@ -102,6 +102,7 @@ impl<'a> Drawer<'a> {
FirstPassDrawer { FirstPassDrawer {
render_pass, render_pass,
renderer: &self.renderer, renderer: &self.renderer,
globals: self.globals,
} }
} }
@ -365,6 +366,7 @@ impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> {
pub struct FirstPassDrawer<'pass> { pub struct FirstPassDrawer<'pass> {
pub(super) render_pass: wgpu::RenderPass<'pass>, pub(super) render_pass: wgpu::RenderPass<'pass>,
pub renderer: &'pass Renderer, pub renderer: &'pass Renderer,
globals: &'pass GlobalsBindGroup,
} }
impl<'pass> FirstPassDrawer<'pass> { impl<'pass> FirstPassDrawer<'pass> {
@ -416,15 +418,18 @@ impl<'pass> FirstPassDrawer<'pass> {
pub fn draw_sprites<'data: 'pass>( pub fn draw_sprites<'data: 'pass>(
&mut self, &mut self,
globals: &'data sprite::SpriteGlobalsBindGroup,
col_lights: &'data ColLights<sprite::Locals>, col_lights: &'data ColLights<sprite::Locals>,
) -> SpriteDrawer<'_, 'pass> { ) -> SpriteDrawer<'_, 'pass> {
self.render_pass self.render_pass
.set_pipeline(&self.renderer.sprite_pipeline.pipeline); .set_pipeline(&self.renderer.sprite_pipeline.pipeline);
self.render_pass.set_bind_group(0, &globals.bind_group, &[]);
self.render_pass self.render_pass
.set_bind_group(4, &col_lights.bind_group, &[]); .set_bind_group(3, &col_lights.bind_group, &[]);
SpriteDrawer { SpriteDrawer {
render_pass: &mut self.render_pass, render_pass: &mut self.render_pass,
globals: self.globals,
} }
} }
@ -501,38 +506,34 @@ impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> {
pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> { pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: &'pass_ref mut wgpu::RenderPass<'pass>, render_pass: &'pass_ref mut wgpu::RenderPass<'pass>,
globals: &'pass GlobalsBindGroup,
} }
impl<'pass_ref, 'pass: 'pass_ref> SpriteDrawer<'pass_ref, 'pass> { impl<'pass_ref, 'pass: 'pass_ref> SpriteDrawer<'pass_ref, 'pass> {
pub fn in_chunk<'data: 'pass>(
&mut self,
terrain_locals: &'data terrain::BoundLocals,
) -> ChunkSpriteDrawer<'_, 'pass> {
self.render_pass
.set_bind_group(2, &terrain_locals.bind_group, &[]);
ChunkSpriteDrawer {
render_pass: &mut self.render_pass,
}
}
}
pub struct ChunkSpriteDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: &'pass_ref mut wgpu::RenderPass<'pass>,
}
impl<'pass_ref, 'pass: 'pass_ref> ChunkSpriteDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>( pub fn draw<'data: 'pass>(
&mut self, &mut self,
model: &'data Model<sprite::Vertex>, terrain_locals: &'data terrain::BoundLocals,
//model: &'data Model<sprite::Vertex>,
//locals: &'data sprite::BoundLocals,
instances: &'data Instances<sprite::Instance>, instances: &'data Instances<sprite::Instance>,
locals: &'data sprite::BoundLocals,
) { ) {
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass self.render_pass
.set_vertex_buffer(1, instances.buf().slice(..)); .set_bind_group(2, &terrain_locals.bind_group, &[]);
self.render_pass.set_bind_group(3, &locals.bind_group, &[]); //self.render_pass.set_bind_group(3, &locals.bind_group, &[]);
//self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass self.render_pass
.draw(0..model.len() as u32, 0..instances.count() as u32); .set_vertex_buffer(0, instances.buf().slice(..));
self.render_pass
.draw(0..sprite::VERT_PAGE_SIZE - 4, 0..instances.count() as u32);
}
}
impl<'pass_ref, 'pass: 'pass_ref> Drop for SpriteDrawer<'pass_ref, 'pass> {
fn drop(&mut self) {
// Reset to regular globals
self.render_pass
.set_bind_group(0, &self.globals.bind_group, &[]);
} }
} }

View File

@ -170,7 +170,9 @@ impl Texture {
/// Update a texture with the given data (used for updating the glyph cache /// Update a texture with the given data (used for updating the glyph cache
/// texture). /// texture).
pub fn update( /// TODO: using generic here seems a bit hacky, consider storing this info
/// in the texture type or pass in wgpu::TextureFormat
pub fn update<const BYTES_PER_PIXEL: u32>(
&self, &self,
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
@ -178,9 +180,10 @@ impl Texture {
size: [u32; 2], size: [u32; 2],
data: &[u8], data: &[u8],
) { ) {
// Note: we only accept 4 bytes per pixel debug_assert_eq!(
// (enforce this in API?) data.len(),
debug_assert_eq!(data.len(), size[0] as usize * size[1] as usize * 4); size[0] as usize * size[1] as usize * BYTES_PER_PIXEL as usize
);
// TODO: Only works for 2D images // TODO: Only works for 2D images
queue.write_texture( queue.write_texture(
wgpu::TextureCopyViewBase { wgpu::TextureCopyViewBase {
@ -195,7 +198,7 @@ impl Texture {
data, data,
wgpu::TextureDataLayout { wgpu::TextureDataLayout {
offset: 0, offset: 0,
bytes_per_row: size[0] * 4, bytes_per_row: size[0] * BYTES_PER_PIXEL,
rows_per_image: size[1], rows_per_image: size[1],
}, },
wgpu::Extent3d { wgpu::Extent3d {

View File

@ -271,6 +271,8 @@ impl Scene {
let globals_bind_group = renderer.bind_globals(&data, lod.get_data()); let globals_bind_group = renderer.bind_globals(&data, lod.get_data());
let terrain = Terrain::new(renderer, &data, lod.get_data());
Self { Self {
data, data,
globals_bind_group, globals_bind_group,
@ -281,7 +283,7 @@ impl Scene {
skybox: Skybox { skybox: Skybox {
model: renderer.create_model(&create_skybox_mesh()).unwrap(), model: renderer.create_model(&create_skybox_mesh()).unwrap(),
}, },
terrain: Terrain::new(renderer), terrain,
lod, lod,
loaded_distance: 0.0, loaded_distance: 0.0,
map_bounds: Vec2::new( map_bounds: Vec2::new(

View File

@ -5,10 +5,11 @@ pub use self::watcher::BlocksOfInterest;
use crate::{ use crate::{
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_sprite, terrain::generate_mesh}, mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_sprite, terrain::generate_mesh},
render::{ render::{
create_sprite_verts_texture,
pipelines::{self, ColLights}, pipelines::{self, ColLights},
ColLightInfo, Consts, Drawer, FirstPassDrawer, FluidVertex, FluidWaves, GlobalModel, ColLightInfo, Consts, Drawer, FirstPassDrawer, FluidVertex, FluidWaves, GlobalModel,
Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteInstance, SpriteLocals, Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteGlobalsBindGroup,
SpriteVertex, TerrainLocals, TerrainShadowDrawer, TerrainVertex, Texture, SpriteInstance, SpriteVertex, TerrainLocals, TerrainShadowDrawer, TerrainVertex, Texture,
}, },
}; };
@ -39,6 +40,8 @@ use treeculler::{BVol, Frustum, AABB};
use vek::*; use vek::*;
const SPRITE_SCALE: Vec3<f32> = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0); const SPRITE_SCALE: Vec3<f32> = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0);
const SPRITE_LOD_LEVELS: usize = 5;
const SPRITE_VERT_PAGE_SIZE: usize = 256;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
struct Visibility { struct Visibility {
@ -67,7 +70,7 @@ pub struct TerrainChunkData {
col_lights: guillotiere::AllocId, col_lights: guillotiere::AllocId,
light_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, light_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
glow_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, glow_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
sprite_instances: HashMap<(SpriteKind, usize), Instances<SpriteInstance>>, sprite_instances: [Instances<SpriteInstance>; SPRITE_LOD_LEVELS],
locals: pipelines::terrain::BoundLocals, locals: pipelines::terrain::BoundLocals,
pub blocks_of_interest: BlocksOfInterest, pub blocks_of_interest: BlocksOfInterest,
@ -95,7 +98,7 @@ struct MeshWorkerResponse {
col_lights_info: ColLightInfo, col_lights_info: ColLightInfo,
light_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, light_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
glow_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, glow_map: Box<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
sprite_instances: HashMap<(SpriteKind, usize), Vec<SpriteInstance>>, sprite_instances: [Vec<SpriteInstance>; SPRITE_LOD_LEVELS],
started_tick: u64, started_tick: u64,
blocks_of_interest: BlocksOfInterest, blocks_of_interest: BlocksOfInterest,
} }
@ -150,7 +153,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
max_texture_size: u16, max_texture_size: u16,
chunk: Arc<TerrainChunk>, chunk: Arc<TerrainChunk>,
range: Aabb<i32>, range: Aabb<i32>,
sprite_data: &HashMap<(SpriteKind, usize), Vec<SpriteData>>, sprite_data: &HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
sprite_config: &SpriteSpec, sprite_config: &SpriteSpec,
) -> MeshWorkerResponse { ) -> MeshWorkerResponse {
span!(_guard, "mesh_worker"); span!(_guard, "mesh_worker");
@ -173,7 +176,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
// Extract sprite locations from volume // Extract sprite locations from volume
sprite_instances: { sprite_instances: {
span!(_guard, "extract sprite_instances"); span!(_guard, "extract sprite_instances");
let mut instances = HashMap::new(); let mut instances = [Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new()];
for x in 0..V::RECT_SIZE.x as i32 { for x in 0..V::RECT_SIZE.x as i32 {
for y in 0..V::RECT_SIZE.y as i32 { for y in 0..V::RECT_SIZE.y as i32 {
@ -201,23 +204,43 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
let key = (sprite, variation); let key = (sprite, variation);
// NOTE: Safe because we called sprite_config_for already. // NOTE: Safe because we called sprite_config_for already.
// NOTE: Safe because 0 ≤ ori < 8 // NOTE: Safe because 0 ≤ ori < 8
let sprite_data = &sprite_data[&key][0]; let sprite_data_lod_0 = &sprite_data[&key][0];
let instance = SpriteInstance::new( let mat = Mat4::identity()
Mat4::identity() // NOTE: offset doesn't change with lod level
.translated_3d(sprite_data.offset) // TODO: pull out of per lod level info or remove lod levels
// for sprites entirely
.translated_3d(sprite_data_lod_0.offset)
// Lod scaling etc is baked during meshing
.scaled_3d(SPRITE_SCALE)
.rotated_z(f32::consts::PI * 0.25 * ori as f32) .rotated_z(f32::consts::PI * 0.25 * ori as f32)
.translated_3d( .translated_3d(
(rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)) (rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0))
/ SPRITE_SCALE, );
), let light = light_map(wpos);
let glow = glow_map(wpos);
for lod_level in 0..SPRITE_LOD_LEVELS {
let sprite_data = &sprite_data[&key][lod_level];
// Add an instance for each page in the sprite model
for page in sprite_data.vert_pages.clone() {
// TODO: could be more efficient to create once and clone while
// modifying vert_page
let instance = SpriteInstance::new(
mat,
cfg.wind_sway, cfg.wind_sway,
sprite_data.scale.z,
rel_pos, rel_pos,
ori, ori,
light_map(wpos), light,
glow_map(wpos), glow,
page,
); );
instances[lod_level].push(instance);
}
}
instances.entry(key).or_insert(Vec::new()).push(instance); //instances.entry(key).or_insert(Vec::new()).
// push(instance);
} }
} }
} }
@ -232,11 +255,18 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
} }
} }
// TODO: may be unecessary
struct ChunkSpriteData {
// Instances
model: Instances<SpriteInstance>,
}
struct SpriteData { struct SpriteData {
/* mat: Mat4<f32>, */ // Sprite vert page ranges that need to be drawn
locals: pipelines::sprite::BoundLocals, vert_pages: core::ops::Range<u32>,
model: Model<SpriteVertex>, // Scale
/* scale: Vec3<f32>, */ scale: Vec3<f32>,
// Offset
offset: Vec3<f32>, offset: Vec3<f32>,
} }
@ -271,8 +301,10 @@ pub struct Terrain<V: RectRasterableVol = TerrainChunk> {
mesh_todos_active: Arc<AtomicU64>, mesh_todos_active: Arc<AtomicU64>,
// GPU data // GPU data
sprite_data: Arc<HashMap<(SpriteKind, usize), Vec<SpriteData>>>, // Maps sprite kind + variant to data detailing how to render it
sprite_data: Arc<HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>>,
col_lights: ColLights<pipelines::terrain::Locals>, col_lights: ColLights<pipelines::terrain::Locals>,
sprite_globals: SpriteGlobalsBindGroup,
sprite_col_lights: ColLights<pipelines::sprite::Locals>, sprite_col_lights: ColLights<pipelines::sprite::Locals>,
waves: FluidWaves, waves: FluidWaves,
@ -285,7 +317,7 @@ impl TerrainChunkData {
impl<V: RectRasterableVol> Terrain<V> { impl<V: RectRasterableVol> Terrain<V> {
#[allow(clippy::float_cmp)] // TODO: Pending review in #587 #[allow(clippy::float_cmp)] // TODO: Pending review in #587
pub fn new(renderer: &mut Renderer) -> Self { pub fn new(renderer: &mut Renderer, global_model: &GlobalModel, lod_data: &LodData) -> Self {
// Load all the sprite config data. // Load all the sprite config data.
let sprite_config = let sprite_config =
Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned(); Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned();
@ -300,7 +332,8 @@ impl<V: RectRasterableVol> Terrain<V> {
let max_texture_size = renderer.max_texture_size(); let max_texture_size = renderer.max_texture_size();
let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32); let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
let mut greedy = GreedyMesh::new(max_size); let mut greedy = GreedyMesh::new(max_size);
let mut locals_buffer = [SpriteLocals::default(); 8]; //let mut locals_buffer = [SpriteLocals::default(); 8];
let mut opaque_mesh = Mesh::new();
let sprite_config_ = &sprite_config; let sprite_config_ = &sprite_config;
// NOTE: Tracks the start vertex of the next model to be meshed. // NOTE: Tracks the start vertex of the next model to be meshed.
let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter() let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter()
@ -342,38 +375,48 @@ impl<V: RectRasterableVol> Terrain<V> {
scale scale
} }
}); });
let sprite_mat: Mat4<f32> =
Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE); //let sprite_mat: Mat4<f32> =
move |greedy: &mut GreedyMesh, renderer: &mut Renderer| { // Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE);
( move |greedy: &mut GreedyMesh, opaque_mesh: &mut Mesh<SpriteVertex>, /* , renderer: &mut Renderer */| {
(kind, variation), ((kind, variation), {
scaled let mut iter = scaled.iter().map(|&lod_scale_orig| {
.iter()
.map(|&lod_scale_orig| {
let lod_scale = model_scale let lod_scale = model_scale
* if lod_scale_orig == 1.0 { * if lod_scale_orig == 1.0 {
Vec3::broadcast(1.0) Vec3::broadcast(1.0)
} else { } else {
lod_axes * lod_scale_orig lod_axes * lod_scale_orig
+ lod_axes + lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
}; };
// Get starting page count of opaque mesh
let start_page_num =
opaque_mesh.vertices().len() / SPRITE_VERT_PAGE_SIZE;
// Mesh generation exclusively acts using side effects; it // Mesh generation exclusively acts using side effects; it
// has no // has no interesting return value, but updates the mesh.
// interesting return value, but updates the mesh.
let mut opaque_mesh = Mesh::new();
generate_mesh_base_vol_sprite( generate_mesh_base_vol_sprite(
Segment::from(&model.read().0).scaled_by(lod_scale), Segment::from(&model.read().0).scaled_by(lod_scale),
(greedy, &mut opaque_mesh, false), //(
// greedy,
// opaque_mesh,
// wind_sway >= 0.4 && lod_scale_orig == 1.0,
//),
(greedy, opaque_mesh, false),
); );
let model = renderer.create_model(&opaque_mesh).expect( // Get the number of pages after the model was meshed
"Failed to upload sprite model data to the GPU!", let end_page_num =
(opaque_mesh.vertices().len() + SPRITE_VERT_PAGE_SIZE - 1)
/ SPRITE_VERT_PAGE_SIZE;
// Fill the current last page up with degenerate verts
opaque_mesh.vertices_mut_vec().resize_with(
end_page_num * SPRITE_VERT_PAGE_SIZE,
SpriteVertex::default,
); );
let sprite_scale = Vec3::one() / lod_scale; let sprite_scale = Vec3::one() / lod_scale;
let sprite_mat: Mat4<f32> = //let sprite_mat: Mat4<f32> =
sprite_mat * Mat4::scaling_3d(sprite_scale); // sprite_mat * Mat4::scaling_3d(sprite_scale);
locals_buffer.iter_mut().enumerate().for_each( /*locals_buffer.iter_mut().enumerate().for_each(
|(ori, locals)| { |(ori, locals)| {
let sprite_mat = sprite_mat let sprite_mat = sprite_mat
.rotated_z(f32::consts::PI * 0.25 * ori as f32); .rotated_z(f32::consts::PI * 0.25 * ori as f32);
@ -384,24 +427,37 @@ impl<V: RectRasterableVol> Terrain<V> {
wind_sway, wind_sway,
); );
}, },
); );*/
SpriteData { SpriteData {
/* vertex_range */ model, vert_pages: start_page_num as u32..end_page_num as u32,
scale: sprite_scale,
offset, offset,
locals: renderer /*locals: renderer
.create_sprite_bound_locals(&locals_buffer), * .create_sprite_bound_locals(&locals_buffer), */
} }
});
[
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
]
}) })
.collect::<Vec<_>>(),
)
} }
}, },
) )
}) })
.map(|mut f| f(&mut greedy, renderer)) .map(|mut f| f(&mut greedy, &mut opaque_mesh, /* , renderer */))
.collect(); .collect();
// Write sprite model to a 1D texture
let sprite_verts_texture = create_sprite_verts_texture(renderer, opaque_mesh);
let sprite_globals =
renderer.bind_sprite_globals(global_model, lod_data, sprite_verts_texture);
let sprite_col_lights = pipelines::shadow::create_col_lights(renderer, greedy.finalize()); let sprite_col_lights = pipelines::shadow::create_col_lights(renderer, greedy.finalize());
Self { Self {
@ -426,6 +482,7 @@ impl<V: RectRasterableVol> Terrain<V> {
renderer.fluid_bind_waves(waves_tex) renderer.fluid_bind_waves(waves_tex)
}, },
sprite_globals,
col_lights, col_lights,
phantom: PhantomData, phantom: PhantomData,
} }
@ -811,18 +868,20 @@ impl<V: RectRasterableVol> Terrain<V> {
col_lights: allocation.id, col_lights: allocation.id,
light_map: response.light_map, light_map: response.light_map,
glow_map: response.glow_map, glow_map: response.glow_map,
sprite_instances: response sprite_instances: {
.sprite_instances let mut iter = response.sprite_instances.iter().map(|instances| {
.into_iter() renderer
.map(|(kind, instances)| { .create_instances(instances)
( .expect("Failed to upload chunk sprite instances to the GPU!")
kind, });
renderer.create_instances(&instances).expect( [
"Failed to upload chunk sprite instances to the GPU!", iter.next().unwrap(),
), iter.next().unwrap(),
) iter.next().unwrap(),
}) iter.next().unwrap(),
.collect(), iter.next().unwrap(),
]
},
locals: renderer.create_terrain_bound_locals(&[TerrainLocals { locals: renderer.create_terrain_bound_locals(&[TerrainLocals {
model_offs: Vec3::from( model_offs: Vec3::from(
response.pos.map2(VolGrid2d::<V>::chunk_size(), |e, sz| { response.pos.map2(VolGrid2d::<V>::chunk_size(), |e, sz| {
@ -1147,16 +1206,22 @@ impl<V: RectRasterableVol> Terrain<V> {
span!(guard, "Terrain sprites"); span!(guard, "Terrain sprites");
let chunk_size = V::RECT_SIZE.map(|e| e as f32); let chunk_size = V::RECT_SIZE.map(|e| e as f32);
let chunk_mag = (chunk_size * (f32::consts::SQRT_2 * 0.5)).magnitude_squared(); let chunk_mag = (chunk_size * (f32::consts::SQRT_2 * 0.5)).magnitude_squared();
let mut sprite_drawer = drawer.draw_sprites(&self.sprite_col_lights);
chunk_iter
.clone()
.filter(|(_, c)| c.visible.is_visible())
.for_each(|(pos, chunk)| {
let sprite_low_detail_distance = sprite_render_distance * 0.75; let sprite_low_detail_distance = sprite_render_distance * 0.75;
let sprite_mid_detail_distance = sprite_render_distance * 0.5; let sprite_mid_detail_distance = sprite_render_distance * 0.5;
let sprite_hid_detail_distance = sprite_render_distance * 0.35; let sprite_hid_detail_distance = sprite_render_distance * 0.35;
let sprite_high_detail_distance = sprite_render_distance * 0.15; let sprite_high_detail_distance = sprite_render_distance * 0.15;
let mut sprite_drawer = drawer.draw_sprites(&self.sprite_globals, &self.sprite_col_lights);
chunk_iter
.clone()
.filter(|(_, c)| c.visible.is_visible())
.for_each(|(pos, chunk)| {
// Skip chunk if it has no sprites
if chunk.sprite_instances[0].count() == 0 {
return;
}
let chunk_center = pos.map2(chunk_size, |e, sz| (e as f32 + 0.5) * sz); let chunk_center = pos.map2(chunk_size, |e, sz| (e as f32 + 0.5) * sz);
let focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); let focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center);
let dist_sqrd = let dist_sqrd =
@ -1173,31 +1238,27 @@ impl<V: RectRasterableVol> Terrain<V> {
chunk_center + chunk_size.x * 0.5 - chunk_size.y * 0.5, chunk_center + chunk_size.x * 0.5 - chunk_size.y * 0.5,
)); ));
if focus_dist_sqrd < sprite_render_distance.powi(2) { if focus_dist_sqrd < sprite_render_distance.powi(2) {
// TODO: skip if sprite_instances is empty let lod_level = /*let SpriteData { model, locals, .. } = if kind
let mut chunk_sprite_drawer = sprite_drawer.in_chunk(&chunk.locals);
for (kind, instances) in (&chunk.sprite_instances).into_iter() {
let SpriteData { model, locals, .. } = if kind
.0 .0
.elim_case_pure(&self.sprite_config.0) .elim_case_pure(&self.sprite_config.0)
.as_ref() .as_ref()
.map(|config| config.wind_sway >= 0.4) .map(|config| config.wind_sway >= 0.4)
.unwrap_or(false) .unwrap_or(false)
&& dist_sqrd <= chunk_mag &&*/ if dist_sqrd <= chunk_mag
|| dist_sqrd < sprite_high_detail_distance.powi(2) || dist_sqrd < sprite_high_detail_distance.powi(2)
{ {
&self.sprite_data[&kind][0] 0
} else if dist_sqrd < sprite_hid_detail_distance.powi(2) { } else if dist_sqrd < sprite_hid_detail_distance.powi(2) {
&self.sprite_data[&kind][1] 1
} else if dist_sqrd < sprite_mid_detail_distance.powi(2) { } else if dist_sqrd < sprite_mid_detail_distance.powi(2) {
&self.sprite_data[&kind][2] 2
} else if dist_sqrd < sprite_low_detail_distance.powi(2) { } else if dist_sqrd < sprite_low_detail_distance.powi(2) {
&self.sprite_data[&kind][3] 3
} else { } else {
&self.sprite_data[&kind][4] 4
}; };
chunk_sprite_drawer.draw(model, instances, locals); sprite_drawer.draw(&chunk.locals, &chunk.sprite_instances[lod_level]);
}
} }
}); });
drop(sprite_drawer); drop(sprite_drawer);