mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Almost implement instance merging for sprites (validation error and segfault)
This commit is contained in:
parent
0ba1fe6a7d
commit
3a6a96b70b
@ -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;
|
||||
|
@ -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 =
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -253,16 +253,14 @@ 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 new(device: &wgpu::Device) -> Self {
|
||||
let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("Globals layout"),
|
||||
entries: &[
|
||||
pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
|
||||
vec![
|
||||
// Global uniform
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
@ -388,7 +386,13 @@ impl GlobalsLayouts {
|
||||
},
|
||||
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 {
|
||||
@ -470,17 +474,13 @@ impl GlobalsLayouts {
|
||||
}
|
||||
}
|
||||
|
||||
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: &[
|
||||
// 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,
|
||||
@ -537,7 +537,20 @@ impl GlobalsLayouts {
|
||||
binding: 11,
|
||||
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 }
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -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 {
|
||||
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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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, &[]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
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,
|
||||
sprite_data.scale.z,
|
||||
rel_pos,
|
||||
ori,
|
||||
light_map(wpos),
|
||||
glow_map(wpos),
|
||||
light,
|
||||
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 {
|
||||
/* 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,38 +375,48 @@ 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 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 })
|
||||
+ 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.
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
// has no interesting return value, but updates the mesh.
|
||||
generate_mesh_base_vol_sprite(
|
||||
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(
|
||||
"Failed to upload sprite model data to the GPU!",
|
||||
// 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(
|
||||
//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);
|
||||
@ -384,24 +427,37 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
wind_sway,
|
||||
);
|
||||
},
|
||||
);
|
||||
);*/
|
||||
|
||||
SpriteData {
|
||||
/* vertex_range */ model,
|
||||
vert_pages: start_page_num as u32..end_page_num as u32,
|
||||
scale: sprite_scale,
|
||||
offset,
|
||||
locals: renderer
|
||||
.create_sprite_bound_locals(&locals_buffer),
|
||||
/*locals: renderer
|
||||
* .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();
|
||||
|
||||
// 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,16 +1206,22 @@ 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);
|
||||
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;
|
||||
|
||||
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 focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center);
|
||||
let dist_sqrd =
|
||||
@ -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
|
||||
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)
|
||||
&& dist_sqrd <= chunk_mag
|
||||
&&*/ if dist_sqrd <= chunk_mag
|
||||
|| dist_sqrd < sprite_high_detail_distance.powi(2)
|
||||
{
|
||||
&self.sprite_data[&kind][0]
|
||||
0
|
||||
} 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) {
|
||||
&self.sprite_data[&kind][2]
|
||||
2
|
||||
} else if dist_sqrd < sprite_low_detail_distance.powi(2) {
|
||||
&self.sprite_data[&kind][3]
|
||||
3
|
||||
} 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);
|
||||
|
Loading…
Reference in New Issue
Block a user