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 = 4) in vec2 f_inst_light;
layout(set = 4, binding = 0)
layout(set = 3, binding = 0)
uniform texture2D t_col_light;
layout(set = 4, binding = 1)
layout(set = 3, binding = 1)
uniform sampler s_col_light;
layout(location = 0) out vec4 tgt_color;

View File

@ -16,29 +16,22 @@
#include <srgb.glsl>
#include <sky.glsl>
layout(location = 0) in vec3 v_pos;
layout(location = 1) in uint v_atlas_pos;
layout(location = 2) in uint v_norm_ao;
layout(location = 3) in uint inst_pos_ori;
layout(location = 4) in vec4 inst_mat0;
layout(location = 5) in vec4 inst_mat1;
layout(location = 6) in vec4 inst_mat2;
layout(location = 7) in vec4 inst_mat3;
layout(location = 8) in vec4 inst_light;
layout(location = 9) in float inst_wind_sway;
layout(location = 0) in vec4 inst_mat0;
layout(location = 1) in vec4 inst_mat1;
layout(location = 2) in vec4 inst_mat2;
layout(location = 3) in vec4 inst_mat3;
// TODO: is there a better way to pack the various vertex attributes?
// TODO: ori is unused
layout(location = 4) in uint inst_pos_ori;
layout(location = 5) in uint inst_vert_page; // NOTE: this could fit in less bits
// TODO: do we need this many bits for light and glow?
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 {
mat4 mat;
vec4 wind_sway;
vec4 offs;
};
layout(std140, set = 3, binding = 0)
uniform u_locals {
mat4 mat;
vec4 wind_sway;
vec4 offs;
};
layout(set = 0, binding = 12) uniform utexture2D t_sprite_verts;
layout(set = 0, binding = 13) uniform sampler s_sprite_verts;
layout (std140, set = 2, binding = 0)
uniform u_terrain_locals {
@ -47,6 +40,7 @@ uniform u_terrain_locals {
ivec4 atlas_offs;
};
// TODO: consider grouping into vec4's
layout(location = 0) out vec3 f_pos;
layout(location = 1) flat out vec3 f_norm;
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 int EXTRA_NEG_Z = 32768;
const int VERT_EXTRA_NEG_Z = 128;
const int VERT_PAGE_SIZE = 256;
void main() {
// Matrix to transform this sprite instance from model space to chunk space
mat4 inst_mat;
inst_mat[0] = inst_mat0;
inst_mat[1] = inst_mat1;
inst_mat[2] = inst_mat2;
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
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.x * 0.1) * sin(tick.x * 0.25),
0.0
//) * pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR;
) * v_pos_.z * SCALE_FACTOR;
// NOTE: could potentially replace `v_pos.z * model_z_scale` with a calculation using `inst_chunk_pos` from below
//) * 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);
f_norm = mix(-norm, norm, v_norm_ao & 1u);
// Determine normal
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
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;
gl_Position =

View File

@ -28,6 +28,9 @@ impl<V: Vertex> Mesh<V> {
/// Get a mutable slice referencing the vertices of this mesh.
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.
pub fn push(&mut self, vert: V) { self.verts.push(vert); }

View File

@ -30,7 +30,11 @@ pub use self::{
postprocess::Locals as PostProcessLocals,
shadow::{Locals as ShadowLocals, PointLightMatrix},
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},
ui::{
create_quad as create_ui_quad,
@ -42,9 +46,9 @@ pub use self::{
},
renderer::{
drawer::{
ChunkSpriteDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer,
ParticleDrawer, PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer,
TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, UiDrawer,
Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer,
PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer,
TerrainShadowDrawer, ThirdPassDrawer, UiDrawer,
},
ColLightInfo, Renderer,
},

View File

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

View File

@ -253,142 +253,146 @@ pub struct GlobalsLayouts {
}
pub struct ColLights<Locals> {
pub bind_group: wgpu::BindGroup,
pub(super) bind_group: wgpu::BindGroup,
pub texture: Texture,
phantom: std::marker::PhantomData<Locals>,
}
impl GlobalsLayouts {
pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
vec![
// Global uniform
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,
},
// Noise tex
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Light uniform
wgpu::BindGroupLayoutEntry {
binding: 3,
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,
},
// Shadow uniform
wgpu::BindGroupLayoutEntry {
binding: 4,
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,
},
// Alt texture
wgpu::BindGroupLayoutEntry {
binding: 5,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 6,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Horizon texture
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 8,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// light shadows (ie shadows from a light?)
wgpu::BindGroupLayoutEntry {
binding: 9,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
// TODO: is this relevant?
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// lod map (t_map)
wgpu::BindGroupLayoutEntry {
binding: 10,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 11,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
]
}
pub fn new(device: &wgpu::Device) -> Self {
let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Globals layout"),
entries: &[
// Global uniform
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,
},
// Noise tex
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Light uniform
wgpu::BindGroupLayoutEntry {
binding: 3,
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,
},
// Shadow uniform
wgpu::BindGroupLayoutEntry {
binding: 4,
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,
},
// Alt texture
wgpu::BindGroupLayoutEntry {
binding: 5,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 6,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Horizon texture
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 8,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// light shadows (ie shadows from a light?)
wgpu::BindGroupLayoutEntry {
binding: 9,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
// TODO: is this relevant?
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// lod map (t_map)
wgpu::BindGroupLayoutEntry {
binding: 10,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 11,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
],
entries: &Self::base_globals_layout(),
});
let col_light = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -470,6 +474,72 @@ impl GlobalsLayouts {
}
}
// Note: this allocation serves the purpose of not having to duplicate code
pub fn bind_base_globals<'a>(
global_model: &'a GlobalModel,
lod_data: &'a lod_terrain::LodData,
noise: &'a Texture,
) -> Vec<wgpu::BindGroupEntry<'a>> {
vec![
// Global uniform
wgpu::BindGroupEntry {
binding: 0,
resource: global_model.globals.buf().as_entire_binding(),
},
// Noise tex
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&noise.view),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(&noise.sampler),
},
// Light uniform
wgpu::BindGroupEntry {
binding: 3,
resource: global_model.lights.buf().as_entire_binding(),
},
// Shadow uniform
wgpu::BindGroupEntry {
binding: 4,
resource: global_model.shadows.buf().as_entire_binding(),
},
// Alt texture
wgpu::BindGroupEntry {
binding: 5,
resource: wgpu::BindingResource::TextureView(&lod_data.alt.view),
},
wgpu::BindGroupEntry {
binding: 6,
resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler),
},
// Horizon texture
wgpu::BindGroupEntry {
binding: 7,
resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view),
},
wgpu::BindGroupEntry {
binding: 8,
resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler),
},
// light shadows
wgpu::BindGroupEntry {
binding: 9,
resource: global_model.shadow_mats.buf().as_entire_binding(),
},
// lod map (t_map)
wgpu::BindGroupEntry {
binding: 10,
resource: wgpu::BindingResource::TextureView(&lod_data.map.view),
},
wgpu::BindGroupEntry {
binding: 11,
resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
},
]
}
pub fn bind(
&self,
device: &wgpu::Device,
@ -480,64 +550,7 @@ impl GlobalsLayouts {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.globals,
entries: &[
// Global uniform
wgpu::BindGroupEntry {
binding: 0,
resource: global_model.globals.buf().as_entire_binding(),
},
// Noise tex
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&noise.view),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(&noise.sampler),
},
// Light uniform
wgpu::BindGroupEntry {
binding: 3,
resource: global_model.lights.buf().as_entire_binding(),
},
// Shadow uniform
wgpu::BindGroupEntry {
binding: 4,
resource: global_model.shadows.buf().as_entire_binding(),
},
// Alt texture
wgpu::BindGroupEntry {
binding: 5,
resource: wgpu::BindingResource::TextureView(&lod_data.alt.view),
},
wgpu::BindGroupEntry {
binding: 6,
resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler),
},
// Horizon texture
wgpu::BindGroupEntry {
binding: 7,
resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view),
},
wgpu::BindGroupEntry {
binding: 8,
resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler),
},
// light shadows
wgpu::BindGroupEntry {
binding: 9,
resource: global_model.shadow_mats.buf().as_entire_binding(),
},
// lod map (t_map)
wgpu::BindGroupEntry {
binding: 10,
resource: wgpu::BindingResource::TextureView(&lod_data.map.view),
},
wgpu::BindGroupEntry {
binding: 11,
resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
},
],
entries: &Self::bind_base_globals(global_model, lod_data, noise),
});
GlobalsBindGroup { bind_group }

View File

@ -116,7 +116,7 @@ pub fn create_col_lights(
array_layer_count: None,
};
renderer.create_texture_with_data_raw(
renderer.create_texture_with_data_raw::<4>(
&texture_info,
&view_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 core::fmt;
use std::mem;
use vek::*;
pub const VERT_PAGE_SIZE: u32 = 256;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 3],
pos_norm: u32,
// Because we try to restrict terrain sprite data to a 128×128 block
// we need an offset into the texture atlas.
atlas_pos: u32,
// ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col",
// ...AANNN
// A = AO
// N = Normal
norm_ao: u32,
/* ____BBBBBBBBGGGGGGGGRRRRRRRR
* col: u32 = "v_col",
* .....NNN
* A = AO
* N = Normal
*norm: u32, */
}
impl fmt::Display for Vertex {
// TODO: fix?
/*impl fmt::Display for Vertex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vertex")
.field("pos", &Vec3::<f32>::from(self.pos))
.field("pos_norm", &Vec3::<f32>::from(self.pos))
.field(
"atlas_pos",
&Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF),
)
.field("norm_ao", &self.norm_ao)
.finish()
}
}
}*/
impl Vertex {
// NOTE: Limit to 16 (x) × 16 (y) × 32 (z).
#[allow(clippy::collapsible_if)]
pub fn new(
atlas_pos: Vec2<u16>,
pos: Vec3<f32>,
norm: Vec3<f32>, /* , col: Rgb<f32>, ao: f32 */
) -> Self {
pub fn new(atlas_pos: Vec2<u16>, pos: Vec3<f32>, norm: Vec3<f32>) -> Self {
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)
let norm_bits = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 }
} 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
// | if meta { 1 } else { 0 } << 28
// | (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,
norm_ao: norm_bits,
}
}
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
/*fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 3] =
wgpu::vertex_attr_array![0 => Float3, 1 => Uint, 2 => Uint];
wgpu::VertexBufferLayout {
@ -68,53 +76,130 @@ impl Vertex {
step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES,
}
}
}*/
}
impl Default for Vertex {
fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(), Vec3::zero()) }
}
impl VertexTrait for Vertex {
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)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance {
pos_ori: u32,
inst_mat0: [f32; 4],
inst_mat1: [f32; 4],
inst_mat2: [f32; 4],
inst_mat3: [f32; 4],
inst_light: [f32; 4],
inst_wind_sway: f32,
pos_ori: u32,
inst_vert_page: u32,
inst_light: f32,
inst_glow: f32,
model_wind_sway: f32,
model_z_scale: f32,
}
impl Instance {
pub fn new(
mat: Mat4<f32>,
wind_sway: f32,
z_scale: f32,
pos: Vec3<i32>,
ori_bits: u8,
light: f32,
glow: f32,
vert_page: u32,
) -> Self {
const EXTRA_NEG_Z: i32 = 32768;
let mat_arr = mat.into_col_arrays();
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_mat1: mat_arr[1],
inst_mat2: mat_arr[2],
inst_mat3: mat_arr[3],
inst_light: [light, glow, 1.0, 1.0],
inst_wind_sway: wind_sway,
pos_ori: ((pos.x as u32) & 0x003F)
| ((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> {
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 {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Instance,
@ -124,10 +209,12 @@ impl 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)]
pub struct Locals {
// Each matrix performs rotatation, translation, and scaling, relative to the sprite
@ -138,8 +225,6 @@ pub struct Locals {
offs: [f32; 4],
}
pub type BoundLocals = Bound<Consts<Locals>>;
impl Default for Locals {
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],
}
}
}*/
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 locals: wgpu::BindGroupLayout,
pub globals: wgpu::BindGroupLayout,
//pub locals: wgpu::BindGroupLayout,
}
impl SpriteLayout {
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 {
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,
entries: &[
// locals
@ -177,18 +299,64 @@ impl SpriteLayout {
},
// instance buffer
wgpu::BindGroupLayoutEntry {
binding: 1,
binding: 1,
visibility: wgpu::ShaderStage::Vertex,
ty: wgpu::BufferBindingType::Buffer {
ty: wgpu::BufferBindingType::
ty: wgpu::BufferBindingType::
}
},
],
}),
}),*/
}
}
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 {
label: None,
layout: &self.locals,
@ -202,7 +370,7 @@ impl SpriteLayout {
bind_group,
with: locals,
}
}
}*/
}
pub struct SpritePipeline {
@ -226,10 +394,11 @@ impl SpritePipeline {
label: Some("Sprite pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&global_layout.globals,
&layout.globals,
&global_layout.shadow_textures,
&terrain_layout.locals,
&layout.locals,
//&layout.locals,
// Note: mergable with globals
&global_layout.col_light,
],
});
@ -248,7 +417,7 @@ impl SpritePipeline {
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[],
buffers: &[Instance::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,

View File

@ -320,7 +320,6 @@ impl Renderer {
.ok_or(RenderError::CouldNotFindAdapter)?;
let limits = wgpu::Limits {
max_bind_groups: 5,
max_push_constant_size: 64,
..Default::default()
};
@ -1182,7 +1181,7 @@ impl Renderer {
}
/// 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,
texture_info: &wgpu::TextureDescriptor,
view_info: &wgpu::TextureViewDescriptor,
@ -1191,7 +1190,7 @@ impl Renderer {
) -> Texture {
let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info);
tex.update(
tex.update::<BYTES_PER_PIXEL>(
&self.device,
&self.queue,
[0; 2],
@ -1257,7 +1256,7 @@ impl Renderer {
// texture.update(&mut self.encoder, offset, size, data)
data: &[[u8; 4]],
) {
texture.update(
texture.update::<4>(
&self.device,
&self.queue,
offset,

View File

@ -20,6 +20,21 @@ impl Renderer {
.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 {
let locals = self.create_consts(vals);
self.layouts.ui.bind_locals(&self.device, locals)
@ -54,10 +69,10 @@ impl Renderer {
self.layouts.shadow.bind_locals(&self.device, locals)
}
pub fn create_sprite_bound_locals(&mut self, locals: &[sprite::Locals]) -> sprite::BoundLocals {
let locals = self.create_consts(locals);
self.layouts.sprite.bind_locals(&self.device, locals)
}
//pub fn create_sprite_bound_locals(&mut self, locals: &[sprite::Locals]) ->
// sprite::BoundLocals { let locals = self.create_consts(locals);
// self.layouts.sprite.bind_locals(&self.device, locals)
//}
pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights<figure::Locals> {
self.layouts.global.bind_col_light(&self.device, col_light)

View File

@ -102,6 +102,7 @@ impl<'a> Drawer<'a> {
FirstPassDrawer {
render_pass,
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(super) render_pass: wgpu::RenderPass<'pass>,
pub renderer: &'pass Renderer,
globals: &'pass GlobalsBindGroup,
}
impl<'pass> FirstPassDrawer<'pass> {
@ -416,15 +418,18 @@ impl<'pass> FirstPassDrawer<'pass> {
pub fn draw_sprites<'data: 'pass>(
&mut self,
globals: &'data sprite::SpriteGlobalsBindGroup,
col_lights: &'data ColLights<sprite::Locals>,
) -> SpriteDrawer<'_, 'pass> {
self.render_pass
.set_pipeline(&self.renderer.sprite_pipeline.pipeline);
self.render_pass.set_bind_group(0, &globals.bind_group, &[]);
self.render_pass
.set_bind_group(4, &col_lights.bind_group, &[]);
.set_bind_group(3, &col_lights.bind_group, &[]);
SpriteDrawer {
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> {
render_pass: &'pass_ref mut wgpu::RenderPass<'pass>,
globals: &'pass GlobalsBindGroup,
}
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>(
&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>,
locals: &'data sprite::BoundLocals,
) {
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass
.set_vertex_buffer(1, instances.buf().slice(..));
self.render_pass.set_bind_group(3, &locals.bind_group, &[]);
.set_bind_group(2, &terrain_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
.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
/// 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,
device: &wgpu::Device,
queue: &wgpu::Queue,
@ -178,9 +180,10 @@ impl Texture {
size: [u32; 2],
data: &[u8],
) {
// Note: we only accept 4 bytes per pixel
// (enforce this in API?)
debug_assert_eq!(data.len(), size[0] as usize * size[1] as usize * 4);
debug_assert_eq!(
data.len(),
size[0] as usize * size[1] as usize * BYTES_PER_PIXEL as usize
);
// TODO: Only works for 2D images
queue.write_texture(
wgpu::TextureCopyViewBase {
@ -195,7 +198,7 @@ impl Texture {
data,
wgpu::TextureDataLayout {
offset: 0,
bytes_per_row: size[0] * 4,
bytes_per_row: size[0] * BYTES_PER_PIXEL,
rows_per_image: size[1],
},
wgpu::Extent3d {

View File

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

View File

@ -5,10 +5,11 @@ pub use self::watcher::BlocksOfInterest;
use crate::{
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_sprite, terrain::generate_mesh},
render::{
create_sprite_verts_texture,
pipelines::{self, ColLights},
ColLightInfo, Consts, Drawer, FirstPassDrawer, FluidVertex, FluidWaves, GlobalModel,
Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteInstance, SpriteLocals,
SpriteVertex, TerrainLocals, TerrainShadowDrawer, TerrainVertex, Texture,
Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteGlobalsBindGroup,
SpriteInstance, SpriteVertex, TerrainLocals, TerrainShadowDrawer, TerrainVertex, Texture,
},
};
@ -39,6 +40,8 @@ use treeculler::{BVol, Frustum, AABB};
use vek::*;
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)]
struct Visibility {
@ -67,7 +70,7 @@ pub struct TerrainChunkData {
col_lights: guillotiere::AllocId,
light_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,
pub blocks_of_interest: BlocksOfInterest,
@ -95,7 +98,7 @@ struct MeshWorkerResponse {
col_lights_info: ColLightInfo,
light_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,
blocks_of_interest: BlocksOfInterest,
}
@ -150,7 +153,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
max_texture_size: u16,
chunk: Arc<TerrainChunk>,
range: Aabb<i32>,
sprite_data: &HashMap<(SpriteKind, usize), Vec<SpriteData>>,
sprite_data: &HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
sprite_config: &SpriteSpec,
) -> MeshWorkerResponse {
span!(_guard, "mesh_worker");
@ -173,7 +176,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
// Extract sprite locations from volume
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 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);
// NOTE: Safe because we called sprite_config_for already.
// NOTE: Safe because 0 ≤ ori < 8
let sprite_data = &sprite_data[&key][0];
let instance = SpriteInstance::new(
Mat4::identity()
.translated_3d(sprite_data.offset)
let sprite_data_lod_0 = &sprite_data[&key][0];
let mat = Mat4::identity()
// NOTE: offset doesn't change with lod level
// 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)
.translated_3d(
(rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0))
/ SPRITE_SCALE,
),
cfg.wind_sway,
rel_pos,
ori,
light_map(wpos),
glow_map(wpos),
);
);
let light = light_map(wpos);
let glow = glow_map(wpos);
instances.entry(key).or_insert(Vec::new()).push(instance);
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,
sprite_data.scale.z,
rel_pos,
ori,
light,
glow,
page,
);
instances[lod_level].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 {
/* mat: Mat4<f32>, */
locals: pipelines::sprite::BoundLocals,
model: Model<SpriteVertex>,
/* scale: Vec3<f32>, */
// Sprite vert page ranges that need to be drawn
vert_pages: core::ops::Range<u32>,
// Scale
scale: Vec3<f32>,
// Offset
offset: Vec3<f32>,
}
@ -271,8 +301,10 @@ pub struct Terrain<V: RectRasterableVol = TerrainChunk> {
mesh_todos_active: Arc<AtomicU64>,
// 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>,
sprite_globals: SpriteGlobalsBindGroup,
sprite_col_lights: ColLights<pipelines::sprite::Locals>,
waves: FluidWaves,
@ -285,7 +317,7 @@ impl TerrainChunkData {
impl<V: RectRasterableVol> Terrain<V> {
#[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.
let sprite_config =
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_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
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;
// NOTE: Tracks the start vertex of the next model to be meshed.
let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter()
@ -342,66 +375,89 @@ impl<V: RectRasterableVol> Terrain<V> {
scale
}
});
let sprite_mat: Mat4<f32> =
Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE);
move |greedy: &mut GreedyMesh, renderer: &mut Renderer| {
(
(kind, variation),
scaled
.iter()
.map(|&lod_scale_orig| {
let lod_scale = model_scale
* if lod_scale_orig == 1.0 {
Vec3::broadcast(1.0)
} else {
lod_axes * lod_scale_orig
+ lod_axes
.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
};
// Mesh generation exclusively acts using side effects; it
// has no
// interesting return value, but updates the mesh.
let mut opaque_mesh = Mesh::new();
generate_mesh_base_vol_sprite(
Segment::from(&model.read().0).scaled_by(lod_scale),
(greedy, &mut opaque_mesh, false),
);
let model = renderer.create_model(&opaque_mesh).expect(
"Failed to upload sprite model data to the GPU!",
);
let sprite_scale = Vec3::one() / lod_scale;
let sprite_mat: Mat4<f32> =
sprite_mat * Mat4::scaling_3d(sprite_scale);
locals_buffer.iter_mut().enumerate().for_each(
|(ori, locals)| {
let sprite_mat = sprite_mat
.rotated_z(f32::consts::PI * 0.25 * ori as f32);
*locals = SpriteLocals::new(
sprite_mat,
sprite_scale,
offset,
wind_sway,
);
},
);
//let sprite_mat: Mat4<f32> =
// Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE);
move |greedy: &mut GreedyMesh, opaque_mesh: &mut Mesh<SpriteVertex>, /* , renderer: &mut Renderer */| {
((kind, variation), {
let mut iter = scaled.iter().map(|&lod_scale_orig| {
let lod_scale = model_scale
* if lod_scale_orig == 1.0 {
Vec3::broadcast(1.0)
} else {
lod_axes * lod_scale_orig
+ lod_axes.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
// has no interesting return value, but updates the mesh.
generate_mesh_base_vol_sprite(
Segment::from(&model.read().0).scaled_by(lod_scale),
//(
// greedy,
// opaque_mesh,
// wind_sway >= 0.4 && lod_scale_orig == 1.0,
//),
(greedy, opaque_mesh, false),
SpriteData {
/* vertex_range */ model,
offset,
locals: renderer
.create_sprite_bound_locals(&locals_buffer),
}
})
.collect::<Vec<_>>(),
)
);
// Get the number of pages after the model was meshed
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_mat: Mat4<f32> =
// sprite_mat * Mat4::scaling_3d(sprite_scale);
/*locals_buffer.iter_mut().enumerate().for_each(
|(ori, locals)| {
let sprite_mat = sprite_mat
.rotated_z(f32::consts::PI * 0.25 * ori as f32);
*locals = SpriteLocals::new(
sprite_mat,
sprite_scale,
offset,
wind_sway,
);
},
);*/
SpriteData {
vert_pages: start_page_num as u32..end_page_num as u32,
scale: sprite_scale,
offset,
/*locals: renderer
* .create_sprite_bound_locals(&locals_buffer), */
}
});
[
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
]
})
}
},
)
})
.map(|mut f| f(&mut greedy, renderer))
.map(|mut f| f(&mut greedy, &mut opaque_mesh, /* , renderer */))
.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());
Self {
@ -426,6 +482,7 @@ impl<V: RectRasterableVol> Terrain<V> {
renderer.fluid_bind_waves(waves_tex)
},
sprite_globals,
col_lights,
phantom: PhantomData,
}
@ -811,18 +868,20 @@ impl<V: RectRasterableVol> Terrain<V> {
col_lights: allocation.id,
light_map: response.light_map,
glow_map: response.glow_map,
sprite_instances: response
.sprite_instances
.into_iter()
.map(|(kind, instances)| {
(
kind,
renderer.create_instances(&instances).expect(
"Failed to upload chunk sprite instances to the GPU!",
),
)
})
.collect(),
sprite_instances: {
let mut iter = response.sprite_instances.iter().map(|instances| {
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(),
iter.next().unwrap(),
]
},
locals: renderer.create_terrain_bound_locals(&[TerrainLocals {
model_offs: Vec3::from(
response.pos.map2(VolGrid2d::<V>::chunk_size(), |e, sz| {
@ -1147,15 +1206,21 @@ impl<V: RectRasterableVol> Terrain<V> {
span!(guard, "Terrain sprites");
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 mut sprite_drawer = drawer.draw_sprites(&self.sprite_col_lights);
let sprite_low_detail_distance = sprite_render_distance * 0.75;
let sprite_mid_detail_distance = sprite_render_distance * 0.5;
let sprite_hid_detail_distance = sprite_render_distance * 0.35;
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)| {
let sprite_low_detail_distance = sprite_render_distance * 0.75;
let sprite_mid_detail_distance = sprite_render_distance * 0.5;
let sprite_hid_detail_distance = sprite_render_distance * 0.35;
let sprite_high_detail_distance = sprite_render_distance * 0.15;
// 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 focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center);
@ -1173,31 +1238,27 @@ impl<V: RectRasterableVol> Terrain<V> {
chunk_center + chunk_size.x * 0.5 - chunk_size.y * 0.5,
));
if focus_dist_sqrd < sprite_render_distance.powi(2) {
// TODO: skip if sprite_instances is empty
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
.elim_case_pure(&self.sprite_config.0)
.as_ref()
.map(|config| config.wind_sway >= 0.4)
.unwrap_or(false)
&& dist_sqrd <= chunk_mag
|| dist_sqrd < sprite_high_detail_distance.powi(2)
{
&self.sprite_data[&kind][0]
} else if dist_sqrd < sprite_hid_detail_distance.powi(2) {
&self.sprite_data[&kind][1]
} else if dist_sqrd < sprite_mid_detail_distance.powi(2) {
&self.sprite_data[&kind][2]
} else if dist_sqrd < sprite_low_detail_distance.powi(2) {
&self.sprite_data[&kind][3]
} else {
&self.sprite_data[&kind][4]
};
let lod_level = /*let SpriteData { model, locals, .. } = if kind
.0
.elim_case_pure(&self.sprite_config.0)
.as_ref()
.map(|config| config.wind_sway >= 0.4)
.unwrap_or(false)
&&*/ if dist_sqrd <= chunk_mag
|| dist_sqrd < sprite_high_detail_distance.powi(2)
{
0
} else if dist_sqrd < sprite_hid_detail_distance.powi(2) {
1
} else if dist_sqrd < sprite_mid_detail_distance.powi(2) {
2
} else if dist_sqrd < sprite_low_detail_distance.powi(2) {
3
} else {
4
};
chunk_sprite_drawer.draw(model, instances, locals);
}
sprite_drawer.draw(&chunk.locals, &chunk.sprite_instances[lod_level]);
}
});
drop(sprite_drawer);