Added support for block kinds in shaders

This commit is contained in:
Joshua Barretto
2023-05-15 17:31:21 +01:00
parent 3afeca67c5
commit aeb72bcf22
20 changed files with 718 additions and 420 deletions

View File

@ -651,8 +651,14 @@ vec3 greedy_extract_col_light_attr(texture2D t_col_light, sampler s_col_light, v
return srgb_to_linear(f_col); return srgb_to_linear(f_col);
} }
vec3 greedy_extract_col_light_terrain(texture2D t_col_light, sampler s_col_light, vec2 f_uv_pos, out float f_light, out float f_glow, out float f_ao, out float f_sky_exposure) { vec3 greedy_extract_col_light_kind_terrain(
texture2D t_col_light, sampler s_col_light,
texture2D t_kind, sampler s_kind,
vec2 f_uv_pos,
out float f_light, out float f_glow, out float f_ao, out float f_sky_exposure, out uint f_kind
) {
float _f_attr; float _f_attr;
f_kind = uint(texelFetch(sampler2D(t_kind, s_kind), ivec2(f_uv_pos), 0).r * 256);
return greedy_extract_col_light_attr(t_col_light, s_col_light, f_uv_pos, f_light, f_glow, f_ao, _f_attr, f_sky_exposure); return greedy_extract_col_light_attr(t_col_light, s_col_light, f_uv_pos, f_light, f_glow, f_ao, _f_attr, f_sky_exposure);
} }

View File

@ -49,6 +49,10 @@ layout(set = 2, binding = 0)
uniform texture2D t_col_light; uniform texture2D t_col_light;
layout(set = 2, binding = 1) layout(set = 2, binding = 1)
uniform sampler s_col_light; uniform sampler s_col_light;
layout(set = 2, binding = 2)
uniform texture2D t_kind;
layout(set = 2, binding = 3)
uniform sampler s_kind;
layout (std140, set = 3, binding = 0) layout (std140, set = 3, binding = 0)
uniform u_locals { uniform u_locals {
@ -86,7 +90,8 @@ void main() {
// vec4 f_col_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0)));//(f_uv_pos/* + 0.5*/) / texSize); // vec4 f_col_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0)));//(f_uv_pos/* + 0.5*/) / texSize);
// float f_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0))).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; // float f_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0))).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0;
float f_light, f_glow, f_ao, f_sky_exposure; float f_light, f_glow, f_ao, f_sky_exposure;
vec3 f_col = greedy_extract_col_light_terrain(t_col_light, s_col_light, f_uv_pos, f_light, f_glow, f_ao, f_sky_exposure); uint f_kind;
vec3 f_col = greedy_extract_col_light_kind_terrain(t_col_light, s_col_light, t_kind, s_kind, f_uv_pos, f_light, f_glow, f_ao, f_sky_exposure, f_kind);
#ifdef EXPERIMENTAL_BAREMINIMUM #ifdef EXPERIMENTAL_BAREMINIMUM
tgt_color = vec4(simple_lighting(f_pos.xyz, f_col, f_light), 1); tgt_color = vec4(simple_lighting(f_pos.xyz, f_col, f_light), 1);

View File

@ -10,7 +10,8 @@
option_get_or_insert_default, option_get_or_insert_default,
map_try_insert, map_try_insert,
slice_as_chunks, slice_as_chunks,
let_chains let_chains,
generic_const_exprs
)] )]
#![recursion_limit = "2048"] #![recursion_limit = "2048"]

View File

@ -1,4 +1,4 @@
use crate::render::{mesh::Quad, ColLightInfo, TerrainVertex, Vertex}; use crate::render::{mesh::Quad, pipelines::AtlasData, Vertex};
use common_base::{prof_span, span}; use common_base::{prof_span, span};
use vek::*; use vek::*;
@ -78,7 +78,7 @@ pub struct GreedyConfig<D, FA, FL, FG, FO, FS, FP, FT> {
/// coloring part as a continuation. When called with a final tile size and /// coloring part as a continuation. When called with a final tile size and
/// vector, the continuation will consume the color data and write it to the /// vector, the continuation will consume the color data and write it to the
/// vector. /// vector.
pub type SuspendedMesh<'a> = dyn for<'r> FnOnce(&'r mut ColLightInfo) + 'a; pub type SuspendedMesh<'a, A> = dyn for<'r> FnOnce(&'r mut A, Vec2<u16>) + 'a;
/// Abstraction over different atlas allocators. Useful to swap out the /// Abstraction over different atlas allocators. Useful to swap out the
/// allocator implementation for specific cases (e.g. sprites). /// allocator implementation for specific cases (e.g. sprites).
@ -316,15 +316,19 @@ pub type SpriteAtlasAllocator = GuillotiereTiled;
/// Shared state for a greedy mesh, potentially passed along to multiple models. /// Shared state for a greedy mesh, potentially passed along to multiple models.
/// ///
/// For an explanation of why we want this, see `SuspendedMesh`. /// For an explanation of why we want this, see `SuspendedMesh`.
pub struct GreedyMesh<'a, Allocator: AtlasAllocator = guillotiere::SimpleAtlasAllocator> { pub struct GreedyMesh<
'a,
A: AtlasData,
Allocator: AtlasAllocator = guillotiere::SimpleAtlasAllocator,
> {
//atlas: guillotiere::SimpleAtlasAllocator, //atlas: guillotiere::SimpleAtlasAllocator,
atlas: Allocator, atlas: Allocator,
col_lights_size: Vec2<u16>, tex_size: Vec2<u16>,
max_size: Vec2<u16>, max_size: Vec2<u16>,
suspended: Vec<Box<SuspendedMesh<'a>>>, suspended: Vec<Box<SuspendedMesh<'a, A>>>,
} }
impl<'a, Allocator: AtlasAllocator> GreedyMesh<'a, Allocator> { impl<'a, A: AtlasData, Allocator: AtlasAllocator> GreedyMesh<'a, A, Allocator> {
/// Construct a new greedy mesher. /// Construct a new greedy mesher.
/// ///
/// Takes as input the maximum allowable size of the texture atlas used to /// Takes as input the maximum allowable size of the texture atlas used to
@ -350,10 +354,10 @@ impl<'a, Allocator: AtlasAllocator> GreedyMesh<'a, Allocator> {
max_size max_size
); );
let atlas = Allocator::with_max_size(max_size, config); let atlas = Allocator::with_max_size(max_size, config);
let col_lights_size = Vec2::new(1, 1); let tex_size = Vec2::new(1, 1);
Self { Self {
atlas, atlas,
col_lights_size, tex_size,
max_size, max_size,
suspended: Vec::new(), suspended: Vec::new(),
} }
@ -380,12 +384,13 @@ impl<'a, Allocator: AtlasAllocator> GreedyMesh<'a, Allocator> {
FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a, FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a,
FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>, FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>,
FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M), FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M),
FT: for<'r> FnMut(&'r mut D, Vec3<i32>, u8, u8, bool) -> [u8; 4] + 'a, FT: for<'r> FnMut(<A::SliceMut<'_> as Iterator>::Item, &'r mut D, Vec3<i32>, u8, u8, bool)
+ 'a,
{ {
span!(_guard, "push", "GreedyMesh::push"); span!(_guard, "push", "GreedyMesh::push");
let cont = greedy_mesh( let cont = greedy_mesh::<_, _, _, _, _, _, _, _, _, A, _>(
&mut self.atlas, &mut self.atlas,
&mut self.col_lights_size, &mut self.tex_size,
self.max_size, self.max_size,
config, config,
); );
@ -401,24 +406,32 @@ impl<'a, Allocator: AtlasAllocator> GreedyMesh<'a, Allocator> {
/// potentially use a single staged upload to the GPU. /// potentially use a single staged upload to the GPU.
/// ///
/// Returns the ColLightsInfo corresponding to the constructed atlas. /// Returns the ColLightsInfo corresponding to the constructed atlas.
pub fn finalize(self) -> ColLightInfo { pub fn finalize(self) -> (A, Vec2<u16>) {
span!(_guard, "finalize", "GreedyMesh::finalize"); span!(_guard, "finalize", "GreedyMesh::finalize");
let cur_size = self.col_lights_size; let mut atlas_texture_data = A::blank_with_size(self.tex_size);
let col_lights = vec![
TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254), true);
cur_size.x as usize * cur_size.y as usize
];
let mut col_lights_info = (col_lights, cur_size);
self.suspended.into_iter().for_each(|cont| { self.suspended.into_iter().for_each(|cont| {
cont(&mut col_lights_info); cont(&mut atlas_texture_data, self.tex_size);
}); });
col_lights_info (atlas_texture_data, self.tex_size)
} }
pub fn max_size(&self) -> Vec2<u16> { self.max_size } pub fn max_size(&self) -> Vec2<u16> { self.max_size }
} }
fn greedy_mesh<'a, M: PartialEq, D: 'a, FA, FL, FG, FO, FS, FP, FT, Allocator: AtlasAllocator>( fn greedy_mesh<
'a,
M: PartialEq,
D: 'a,
FA,
FL,
FG,
FO,
FS,
FP,
FT,
A: AtlasData,
Allocator: AtlasAllocator,
>(
atlas: &mut Allocator, atlas: &mut Allocator,
col_lights_size: &mut Vec2<u16>, col_lights_size: &mut Vec2<u16>,
max_size: Vec2<u16>, max_size: Vec2<u16>,
@ -435,7 +448,7 @@ fn greedy_mesh<'a, M: PartialEq, D: 'a, FA, FL, FG, FO, FS, FP, FT, Allocator: A
mut push_quad, mut push_quad,
make_face_texel, make_face_texel,
}: GreedyConfig<D, FA, FL, FG, FO, FS, FP, FT>, }: GreedyConfig<D, FA, FL, FG, FO, FS, FP, FT>,
) -> Box<SuspendedMesh<'a>> ) -> Box<SuspendedMesh<'a, A>>
where where
FA: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a, FA: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a,
FL: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a, FL: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a,
@ -443,7 +456,7 @@ where
FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a, FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a,
FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>, FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>,
FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M), FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M),
FT: for<'r> FnMut(&'r mut D, Vec3<i32>, u8, u8, bool) -> [u8; 4] + 'a, FT: for<'r> FnMut(<A::SliceMut<'_> as Iterator>::Item, &'r mut D, Vec3<i32>, u8, u8, bool) + 'a,
{ {
span!(_guard, "greedy_mesh"); span!(_guard, "greedy_mesh");
// TODO: Collect information to see if we can choose a good value here. // TODO: Collect information to see if we can choose a good value here.
@ -572,10 +585,11 @@ where
}, },
); );
Box::new(move |col_lights_info| { Box::new(move |atlas_texture_data, cur_size| {
let mut data = data; let mut data = data;
draw_col_lights( draw_col_lights::<_, A>(
col_lights_info, atlas_texture_data,
cur_size,
&mut data, &mut data,
todo_rects, todo_rects,
draw_delta, draw_delta,
@ -727,8 +741,9 @@ fn add_to_atlas<Allocator: AtlasAllocator>(
// to provide builtin support for what we're doing here. // to provide builtin support for what we're doing here.
// //
// TODO: See if we can speed this up using SIMD. // TODO: See if we can speed this up using SIMD.
fn draw_col_lights<D>( fn draw_col_lights<D, A: AtlasData>(
(col_lights, cur_size): &mut ColLightInfo, atlas_texture_data: &mut A,
cur_size: Vec2<u16>,
data: &mut D, data: &mut D,
todo_rects: Vec<TodoRect>, todo_rects: Vec<TodoRect>,
draw_delta: Vec3<i32>, draw_delta: Vec3<i32>,
@ -736,7 +751,14 @@ fn draw_col_lights<D>(
mut get_light: impl FnMut(&mut D, Vec3<i32>) -> f32, mut get_light: impl FnMut(&mut D, Vec3<i32>) -> f32,
mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32, mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32,
mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool, mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool,
mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8, bool) -> [u8; 4], mut make_face_texel: impl FnMut(
<A::SliceMut<'_> as Iterator>::Item,
&mut D,
Vec3<i32>,
u8,
u8,
bool,
),
) { ) {
todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| { todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| {
// NOTE: Conversions are safe because width, height, and offset must be // NOTE: Conversions are safe because width, height, and offset must be
@ -751,8 +773,8 @@ fn draw_col_lights<D>(
(0..height).for_each(|v| { (0..height).for_each(|v| {
let start = cur_size.x as usize * usize::from(top + v) + usize::from(left); let start = cur_size.x as usize * usize::from(top + v) + usize::from(left);
(0..width) (0..width)
.zip(&mut col_lights[start..start + usize::from(width)]) .zip(atlas_texture_data.slice_mut(start..start + usize::from(width)))
.for_each(|(u, col_light)| { .for_each(|(u, texel)| {
let pos = pos + uv.x * i32::from(u) + uv.y * i32::from(v); let pos = pos + uv.x * i32::from(u) + uv.y * i32::from(v);
// TODO: Consider optimizing to take advantage of the fact that this whole // TODO: Consider optimizing to take advantage of the fact that this whole
// face should be facing nothing but air (this is not currently true, but // face should be facing nothing but air (this is not currently true, but
@ -822,7 +844,7 @@ fn draw_col_lights<D>(
let light = (darkness * 31.5) as u8; let light = (darkness * 31.5) as u8;
let glow = (glowiness * 31.5) as u8; let glow = (glowiness * 31.5) as u8;
let ao = ao > 0.7; let ao = ao > 0.7;
*col_light = make_face_texel(data, pos, light, glow, ao); make_face_texel(texel, data, pos, light, glow, ao);
}); });
}); });
}); });

View File

@ -4,7 +4,7 @@ use crate::{
terrain::FaceKind, terrain::FaceKind,
MeshGen, MeshGen,
}, },
render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex}, render::{pipelines::FigureSpriteAtlasData, Mesh, ParticleVertex, SpriteVertex, TerrainVertex},
scene::math, scene::math,
}; };
use common::{ use common::{
@ -21,7 +21,7 @@ use vek::*;
pub fn generate_mesh_base_vol_figure<'a: 'b, 'b, V: 'a>( pub fn generate_mesh_base_vol_figure<'a: 'b, 'b, V: 'a>(
vol: V, vol: V,
(greedy, opaque_mesh, offs, scale, bone_idx): ( (greedy, opaque_mesh, offs, scale, bone_idx): (
&'b mut GreedyMesh<'a>, &'b mut GreedyMesh<'a, FigureSpriteAtlasData>,
&'b mut Mesh<TerrainVertex>, &'b mut Mesh<TerrainVertex>,
Vec3<f32>, Vec3<f32>,
Vec3<f32>, Vec3<f32>,
@ -91,7 +91,7 @@ where
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
)); ));
}, },
make_face_texel: |vol: &mut V, pos, light, _, _| { make_face_texel: |col_light: &mut [u8; 4], vol: &mut V, pos, light, _, _| {
let cell = vol.get(pos).ok(); let cell = vol.get(pos).ok();
let (glowy, shiny) = cell let (glowy, shiny) = cell
.map(|c| (c.is_glowy(), c.is_shiny())) .map(|c| (c.is_glowy(), c.is_shiny()))
@ -99,7 +99,7 @@ where
let col = cell let col = cell
.and_then(|vox| vox.get_color()) .and_then(|vox| vox.get_color())
.unwrap_or_else(Rgb::zero); .unwrap_or_else(Rgb::zero);
TerrainVertex::make_col_light_figure(light, glowy, shiny, col) *col_light = TerrainVertex::make_col_light_figure(light, glowy, shiny, col);
}, },
}); });
let bounds = math::Aabb { let bounds = math::Aabb {
@ -118,7 +118,7 @@ where
pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>( pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>(
vol: V, vol: V,
(greedy, opaque_mesh, offs, scale, bone_idx): ( (greedy, opaque_mesh, offs, scale, bone_idx): (
&'b mut GreedyMesh<'a>, &'b mut GreedyMesh<'a, FigureSpriteAtlasData>,
&'b mut Mesh<TerrainVertex>, &'b mut Mesh<TerrainVertex>,
Vec3<f32>, Vec3<f32>,
Vec3<f32>, Vec3<f32>,
@ -201,13 +201,13 @@ where
}, },
FaceKind::Fluid => {}, FaceKind::Fluid => {},
}, },
make_face_texel: |vol: &mut V, pos, light, _, _| { make_face_texel: |col_light: &mut [u8; 4], vol: &mut V, pos, light, _, _| {
let block = vol.get(pos).ok(); let block = vol.get(pos).ok();
let glowy = block.map(|c| c.get_glow().is_some()).unwrap_or_default(); let glowy = block.map(|c| c.get_glow().is_some()).unwrap_or_default();
let col = block let col = block
.and_then(|vox| vox.get_color()) .and_then(|vox| vox.get_color())
.unwrap_or_else(Rgb::zero); .unwrap_or_else(Rgb::zero);
TerrainVertex::make_col_light_figure(light, glowy, false, col) *col_light = TerrainVertex::make_col_light_figure(light, glowy, false, col);
}, },
}); });
let bounds = math::Aabb { let bounds = math::Aabb {
@ -223,7 +223,7 @@ where
pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>( pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>(
vol: V, vol: V,
(greedy, opaque_mesh, vertical_stripes): ( (greedy, opaque_mesh, vertical_stripes): (
&'b mut GreedyMesh<'a, greedy::SpriteAtlasAllocator>, &'b mut GreedyMesh<'a, FigureSpriteAtlasData, greedy::SpriteAtlasAllocator>,
&'b mut Mesh<SpriteVertex>, &'b mut Mesh<SpriteVertex>,
bool, bool,
), ),
@ -336,10 +336,11 @@ where
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
)); ));
}, },
make_face_texel: move |flat: &mut _, pos, light, _glow, _ao| { make_face_texel: move |col_light: &mut [u8; 4], flat: &mut _, pos, light, _glow, _ao| {
let cell = flat_get(flat, pos); let cell = flat_get(flat, pos);
let (glowy, shiny) = (cell.is_glowy(), cell.is_shiny()); let (glowy, shiny) = (cell.is_glowy(), cell.is_shiny());
TerrainVertex::make_col_light_figure(light, glowy, shiny, get_color(flat, pos)) *col_light =
TerrainVertex::make_col_light_figure(light, glowy, shiny, get_color(flat, pos));
}, },
}); });
@ -348,7 +349,7 @@ where
pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>( pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>(
vol: V, vol: V,
greedy: &'b mut GreedyMesh<'a>, greedy: &'b mut GreedyMesh<'a, FigureSpriteAtlasData>,
) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()> ) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()>
where where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol, V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
@ -421,8 +422,8 @@ where
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
)); ));
}, },
make_face_texel: move |vol: &mut V, pos, light, glow, ao| { make_face_texel: move |col_light: &mut [u8; 4], vol: &mut V, pos, light, glow, ao| {
TerrainVertex::make_col_light(light, glow, get_color(vol, pos), ao) *col_light = TerrainVertex::make_col_light(light, glow, get_color(vol, pos), ao);
}, },
}); });

View File

@ -5,7 +5,7 @@ use crate::{
greedy::{self, GreedyConfig, GreedyMesh}, greedy::{self, GreedyConfig, GreedyMesh},
MeshGen, MeshGen,
}, },
render::{AltIndices, ColLightInfo, FluidVertex, Mesh, TerrainVertex, Vertex}, render::{AltIndices, FluidVertex, Mesh, TerrainAtlasData, TerrainVertex, Vertex},
scene::terrain::{BlocksOfInterest, DEEP_ALT, SHALLOW_ALT}, scene::terrain::{BlocksOfInterest, DEEP_ALT, SHALLOW_ALT},
}; };
use common::{ use common::{
@ -235,7 +235,8 @@ pub fn generate_mesh<'a>(
TerrainVertex, TerrainVertex,
( (
Aabb<f32>, Aabb<f32>,
ColLightInfo, TerrainAtlasData,
Vec2<u16>,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
AltIndices, AltIndices,
@ -390,6 +391,7 @@ pub fn generate_mesh<'a>(
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min); let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
let get_color = let get_color =
|_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or_else(Rgb::zero); |_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or_else(Rgb::zero);
let get_kind = |_: &mut (), pos: Vec3<i32>| flat_get(pos).kind() as u8;
let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque(); let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque();
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| { let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
should_draw_greedy(pos, delta, &flat_get) should_draw_greedy(pos, delta, &flat_get)
@ -430,8 +432,10 @@ pub fn generate_mesh<'a>(
FluidVertex::new(pos + mesh_delta, norm, vel.xy()) FluidVertex::new(pos + mesh_delta, norm, vel.xy())
}; };
let mut greedy = let mut greedy = GreedyMesh::<TerrainAtlasData, guillotiere::SimpleAtlasAllocator>::new(
GreedyMesh::<guillotiere::SimpleAtlasAllocator>::new(max_size, greedy::general_config()); max_size,
greedy::general_config(),
);
let mut opaque_deep = Vec::new(); let mut opaque_deep = Vec::new();
let mut opaque_shallow = Vec::new(); let mut opaque_shallow = Vec::new();
let mut opaque_surface = Vec::new(); let mut opaque_surface = Vec::new();
@ -486,8 +490,14 @@ pub fn generate_mesh<'a>(
)); ));
}, },
}, },
make_face_texel: |data: &mut (), pos, light, glow, ao| { make_face_texel: |(col_light, kind): (&mut [u8; 4], &mut u8),
TerrainVertex::make_col_light(light, glow, get_color(data, pos), ao) data: &mut (),
pos,
light,
glow,
ao| {
*col_light = TerrainVertex::make_col_light(light, glow, get_color(data, pos), ao);
*kind = get_kind(data, pos);
}, },
}); });
@ -496,7 +506,7 @@ pub fn generate_mesh<'a>(
min: min_bounds, min: min_bounds,
max: max_bounds + min_bounds, max: max_bounds + min_bounds,
}; };
let (col_lights, col_lights_size) = greedy.finalize(); let (atlas_data, atlas_size) = greedy.finalize();
let deep_end = opaque_deep.len() let deep_end = opaque_deep.len()
* if TerrainVertex::QUADS_INDEX.is_some() { * if TerrainVertex::QUADS_INDEX.is_some() {
@ -526,7 +536,8 @@ pub fn generate_mesh<'a>(
Mesh::new(), Mesh::new(),
( (
bounds, bounds,
(col_lights, col_lights_size), atlas_data,
atlas_size,
Arc::new(light), Arc::new(light),
Arc::new(glow), Arc::new(glow),
alt_indices, alt_indices,

View File

@ -46,7 +46,8 @@ pub use self::{
TextureBindGroup as UiTextureBindGroup, UploadBatchId as UiUploadBatchId, TextureBindGroup as UiTextureBindGroup, UploadBatchId as UiUploadBatchId,
Vertex as UiVertex, Vertex as UiVertex,
}, },
GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light, Shadow, FigureSpriteAtlasData, GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light,
Shadow, TerrainAtlasData,
}, },
renderer::{ renderer::{
drawer::{ drawer::{
@ -55,7 +56,7 @@ pub use self::{
TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, TrailDrawer, TerrainDrawer, TerrainShadowDrawer, ThirdPassDrawer, TrailDrawer,
TransparentPassDrawer, UiDrawer, VolumetricPassDrawer, UI_PREMULTIPLY_PASS, TransparentPassDrawer, UiDrawer, VolumetricPassDrawer, UI_PREMULTIPLY_PASS,
}, },
AltIndices, ColLightInfo, CullingMode, Renderer, AltIndices, CullingMode, Renderer,
}, },
texture::Texture, texture::Texture,
}; };

View File

@ -1,6 +1,7 @@
use super::{ use super::{
super::{AaMode, Bound, Consts, GlobalsLayouts, Mesh, Model}, super::{AaMode, Bound, Consts, GlobalsLayouts, Mesh, Model},
terrain::Vertex, terrain::Vertex,
AtlasData,
}; };
use crate::mesh::greedy::GreedyMesh; use crate::mesh::greedy::GreedyMesh;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
@ -87,7 +88,7 @@ pub struct FigureModel {
impl FigureModel { impl FigureModel {
/// Start a greedy mesh designed for figure bones. /// Start a greedy mesh designed for figure bones.
pub fn make_greedy<'a>() -> GreedyMesh<'a> { pub fn make_greedy<'a>() -> GreedyMesh<'a, FigureSpriteAtlasData> {
// NOTE: Required because we steal two bits from the normal in the shadow uint // NOTE: Required because we steal two bits from the normal in the shadow uint
// in order to store the bone index. The two bits are instead taken out // in order to store the bone index. The two bits are instead taken out
// of the atlas coordinates, which is why we "only" allow 1 << 15 per // of the atlas coordinates, which is why we "only" allow 1 << 15 per
@ -185,7 +186,7 @@ impl FigurePipeline {
bind_group_layouts: &[ bind_group_layouts: &[
&global_layout.globals, &global_layout.globals,
&global_layout.shadow_textures, &global_layout.shadow_textures,
&global_layout.col_light, global_layout.figure_sprite_atlas_layout.layout(),
&layout.locals, &layout.locals,
], ],
}); });
@ -253,3 +254,57 @@ impl FigurePipeline {
} }
} }
} }
/// Represents texture that can be converted into texture atlases for figures
/// and sprites.
pub struct FigureSpriteAtlasData {
pub col_lights: Vec<[u8; 4]>,
}
impl AtlasData for FigureSpriteAtlasData {
type SliceMut<'a> = std::slice::IterMut<'a, [u8; 4]>;
const TEXTURES: usize = 1;
fn blank_with_size(sz: Vec2<u16>) -> Self {
let col_lights =
vec![Vertex::make_col_light(254, 0, Rgb::broadcast(254), true); sz.as_().product()];
Self { col_lights }
}
fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES] {
[(
wgpu::TextureFormat::Rgba8Unorm,
bytemuck::cast_slice(self.col_lights.as_slice()),
)]
}
fn layout() -> Vec<wgpu::BindGroupLayoutEntry> {
vec![
// col lights
wgpu::BindGroupLayoutEntry {
binding: 0,
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: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
]
}
fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_> {
self.col_lights[range].iter_mut()
}
}

View File

@ -16,12 +16,15 @@ pub mod terrain;
pub mod trail; pub mod trail;
pub mod ui; pub mod ui;
use super::{Consts, Texture}; use super::{Consts, Renderer, Texture};
use crate::scene::camera::CameraMode; use crate::scene::camera::CameraMode;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use common::terrain::BlockKind; use common::terrain::BlockKind;
use std::marker::PhantomData;
use vek::*; use vek::*;
pub use self::{figure::FigureSpriteAtlasData, terrain::TerrainAtlasData};
// TODO: auto insert these into shaders // TODO: auto insert these into shaders
pub const MAX_POINT_LIGHT_COUNT: usize = 20; pub const MAX_POINT_LIGHT_COUNT: usize = 20;
pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; pub const MAX_FIGURE_SHADOW_COUNT: usize = 24;
@ -278,16 +281,114 @@ pub struct ShadowTexturesBindGroup {
pub struct GlobalsLayouts { pub struct GlobalsLayouts {
pub globals: wgpu::BindGroupLayout, pub globals: wgpu::BindGroupLayout,
pub col_light: wgpu::BindGroupLayout, pub figure_sprite_atlas_layout: VoxelAtlasLayout<FigureSpriteAtlasData>,
pub terrain_atlas_layout: VoxelAtlasLayout<TerrainAtlasData>,
pub shadow_textures: wgpu::BindGroupLayout, pub shadow_textures: wgpu::BindGroupLayout,
} }
pub struct ColLights<Locals> { /// A type representing a set of textures that have the same atlas layout and
/// pertain to a greedy voxel structure.
pub struct AtlasTextures<Locals, S: AtlasData>
where
[(); S::TEXTURES]:,
{
pub(super) bind_group: wgpu::BindGroup, pub(super) bind_group: wgpu::BindGroup,
pub texture: Texture, pub textures: [Texture; S::TEXTURES],
phantom: std::marker::PhantomData<Locals>, phantom: std::marker::PhantomData<Locals>,
} }
pub struct VoxelAtlasLayout<S: AtlasData>(wgpu::BindGroupLayout, PhantomData<S>);
impl<S: AtlasData> VoxelAtlasLayout<S> {
pub fn new(device: &wgpu::Device) -> Self {
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &S::layout(),
});
Self(layout, PhantomData)
}
pub fn layout(&self) -> &wgpu::BindGroupLayout { &self.0 }
}
/// A trait implemented by texture atlas groups.
///
/// Terrain, figures, sprites, etc. all use texture atlases but have different
/// requirements, such as that layers provided by each atlas. This trait
/// abstracts over these cases.
pub trait AtlasData {
/// The number of texture channels that this atlas has.
const TEXTURES: usize;
/// Abstracts over a slice into the texture data, as returned by
/// [`AtlasData::slice_mut`].
type SliceMut<'a>: Iterator
where
Self: 'a;
/// Return blank atlas data upon which texels can be applied.
fn blank_with_size(sz: Vec2<u16>) -> Self;
/// Return an array of texture formats and data for each texture layer in
/// the atlas.
fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES];
/// Return a layout entry that corresponds to the texture layers in the
/// atlas.
fn layout() -> Vec<wgpu::BindGroupLayoutEntry>;
/// Take a sub-slice of the texture data for each layer in the atlas.
fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_>;
/// Create textures on the GPU corresponding to the layers in the atlas.
fn create_textures(
&self,
renderer: &mut Renderer,
atlas_size: Vec2<u16>,
) -> [Texture; Self::TEXTURES] {
self.as_texture_data().map(|(fmt, data)| {
let texture_info = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: u32::from(atlas_size.x),
height: u32::from(atlas_size.y),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: fmt,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
border_color: Some(wgpu::SamplerBorderColor::TransparentBlack),
..Default::default()
};
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(fmt),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
renderer.create_texture_with_data_raw(&texture_info, &view_info, &sampler_info, data)
})
}
}
impl GlobalsLayouts { impl GlobalsLayouts {
pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> { pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
vec![ vec![
@ -456,32 +557,6 @@ impl GlobalsLayouts {
entries: &Self::base_globals_layout(), entries: &Self::base_globals_layout(),
}); });
let col_light = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// col lights
wgpu::BindGroupLayoutEntry {
binding: 0,
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: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
],
});
let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None, label: None,
entries: &[ entries: &[
@ -550,8 +625,9 @@ impl GlobalsLayouts {
Self { Self {
globals, globals,
col_light,
shadow_textures, shadow_textures,
terrain_atlas_layout: VoxelAtlasLayout::new(device),
figure_sprite_atlas_layout: VoxelAtlasLayout::new(device),
} }
} }
@ -691,28 +767,35 @@ impl GlobalsLayouts {
ShadowTexturesBindGroup { bind_group } ShadowTexturesBindGroup { bind_group }
} }
pub fn bind_col_light<Locals>( pub fn bind_atlas_textures<Locals, S: AtlasData>(
&self, &self,
device: &wgpu::Device, device: &wgpu::Device,
col_light: Texture, layout: &VoxelAtlasLayout<S>,
) -> ColLights<Locals> { textures: [Texture; S::TEXTURES],
) -> AtlasTextures<Locals, S> {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None, label: None,
layout: &self.col_light, layout: layout.layout(),
entries: &[ entries: &textures
wgpu::BindGroupEntry { .iter()
binding: 0, .enumerate()
resource: wgpu::BindingResource::TextureView(&col_light.view), .flat_map(|(i, tex)| {
}, [
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: i as u32 * 2,
resource: wgpu::BindingResource::Sampler(&col_light.sampler), resource: wgpu::BindingResource::TextureView(&tex.view),
}, },
], wgpu::BindGroupEntry {
binding: i as u32 * 2 + 1,
resource: wgpu::BindingResource::Sampler(&tex.sampler),
},
]
})
.collect::<Vec<_>>(),
}); });
ColLights { AtlasTextures {
texture: col_light, textures,
bind_group, bind_group,
phantom: std::marker::PhantomData, phantom: std::marker::PhantomData,
} }

View File

@ -1,6 +1,6 @@
use super::super::{ use super::super::{
AaMode, Bound, ColLightInfo, Consts, DebugLayout, DebugVertex, FigureLayout, GlobalsLayouts, AaMode, Bound, Consts, DebugLayout, DebugVertex, FigureLayout, GlobalsLayouts, TerrainLayout,
Renderer, TerrainLayout, TerrainVertex, Texture, TerrainVertex,
}; };
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
@ -79,55 +79,6 @@ impl Default for PointLightMatrix {
fn default() -> Self { Self::new(Mat4::identity()) } fn default() -> Self { Self::new(Mat4::identity()) }
} }
pub fn create_col_lights(
renderer: &mut Renderer,
(col_lights, col_lights_size): &ColLightInfo,
) -> Texture {
let texture_info = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: u32::from(col_lights_size.x),
height: u32::from(col_lights_size.y),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
border_color: Some(wgpu::SamplerBorderColor::TransparentBlack),
..Default::default()
};
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8Unorm),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
bytemuck::cast_slice(col_lights),
)
}
pub struct ShadowFigurePipeline { pub struct ShadowFigurePipeline {
pub pipeline: wgpu::RenderPipeline, pub pipeline: wgpu::RenderPipeline,
} }

View File

@ -1,8 +1,6 @@
use super::{ use super::{
super::{ super::{buffer::Buffer, AaMode, GlobalsLayouts, Mesh, TerrainLayout, Vertex as VertexTrait},
buffer::Buffer, AaMode, GlobalsLayouts, Mesh, TerrainLayout, Texture, Vertex as VertexTrait, lod_terrain, GlobalModel, Texture,
},
lod_terrain, GlobalModel,
}; };
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::mem; use std::mem;
@ -278,7 +276,7 @@ impl SpritePipeline {
&layout.globals, &layout.globals,
&global_layout.shadow_textures, &global_layout.shadow_textures,
// Note: mergable with globals // Note: mergable with globals
&global_layout.col_light, global_layout.figure_sprite_atlas_layout.layout(),
&terrain_layout.locals, &terrain_layout.locals,
], ],
}); });

View File

@ -1,4 +1,7 @@
use super::super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait}; use super::{
super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait},
AtlasData,
};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::mem; use std::mem;
use vek::*; use vek::*;
@ -237,7 +240,7 @@ impl TerrainPipeline {
bind_group_layouts: &[ bind_group_layouts: &[
&global_layout.globals, &global_layout.globals,
&global_layout.shadow_textures, &global_layout.shadow_textures,
&global_layout.col_light, global_layout.terrain_atlas_layout.layout(),
&layout.locals, &layout.locals,
], ],
}); });
@ -305,3 +308,84 @@ impl TerrainPipeline {
} }
} }
} }
/// Represents texture that can be converted into texture atlases for terrain.
pub struct TerrainAtlasData {
pub col_lights: Vec<[u8; 4]>,
pub kinds: Vec<u8>,
}
impl AtlasData for TerrainAtlasData {
type SliceMut<'a> =
std::iter::Zip<std::slice::IterMut<'a, [u8; 4]>, std::slice::IterMut<'a, u8>>;
const TEXTURES: usize = 2;
fn blank_with_size(sz: Vec2<u16>) -> Self {
let col_lights =
vec![Vertex::make_col_light(254, 0, Rgb::broadcast(254), true); sz.as_().product()];
let kinds = vec![0; sz.as_().product()];
Self { col_lights, kinds }
}
fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES] {
[
(
wgpu::TextureFormat::Rgba8Unorm,
bytemuck::cast_slice(&self.col_lights),
),
(wgpu::TextureFormat::R8Unorm, &self.kinds),
]
}
fn layout() -> Vec<wgpu::BindGroupLayoutEntry> {
vec![
// col lights
wgpu::BindGroupLayoutEntry {
binding: 0,
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: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// kind
wgpu::BindGroupLayoutEntry {
binding: 2,
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: 3,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
]
}
fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_> {
self.col_lights[range.clone()]
.iter_mut()
.zip(self.kinds[range].iter_mut())
}
}

View File

@ -40,12 +40,6 @@ use std::sync::Arc;
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use vek::*; use vek::*;
// TODO: yeet this somewhere else
/// A type representing data that can be converted to an immutable texture map
/// of ColLight data (used for texture atlases created during greedy meshing).
// TODO: revert to u16
pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u16>);
const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000;
const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000; const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000;
@ -1427,13 +1421,12 @@ impl Renderer {
/// Update a texture with the provided offset, size, and data. /// Update a texture with the provided offset, size, and data.
/// ///
/// Currently only supports Rgba8Srgb /// Currently only supports Rgba8Srgb
pub fn update_texture( pub fn update_texture<T: bytemuck::Pod>(
&mut self, &mut self,
texture: &Texture, /* <T> */ texture: &Texture,
offset: [u32; 2], offset: [u32; 2],
size: [u32; 2], size: [u32; 2],
// TODO: be generic over pixel type data: &[T],
data: &[[u8; 4]],
) { ) {
texture.update(&self.queue, offset, size, bytemuck::cast_slice(data)) texture.update(&self.queue, offset, size, bytemuck::cast_slice(data))
} }

View File

@ -3,8 +3,8 @@ use crate::render::pipelines::rain_occlusion;
use super::{ use super::{
super::{ super::{
pipelines::{ pipelines::{
debug, figure, lod_terrain, shadow, sprite, terrain, ui, ColLights, GlobalModel, debug, figure, lod_terrain, shadow, sprite, terrain, ui, AtlasTextures,
GlobalsBindGroup, FigureSpriteAtlasData, GlobalModel, GlobalsBindGroup, TerrainAtlasData,
}, },
texture::Texture, texture::Texture,
}, },
@ -90,15 +90,37 @@ impl Renderer {
.bind_locals(&self.device, locals) .bind_locals(&self.device, locals)
} }
pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights<figure::Locals> { pub fn figure_bind_atlas_textures(
self.layouts.global.bind_col_light(&self.device, col_light) &self,
col_light: Texture,
) -> AtlasTextures<figure::Locals, FigureSpriteAtlasData> {
self.layouts.global.bind_atlas_textures(
&self.device,
&self.layouts.global.figure_sprite_atlas_layout,
[col_light],
)
} }
pub fn terrain_bind_col_light(&self, col_light: Texture) -> ColLights<terrain::Locals> { pub fn terrain_bind_atlas_textures(
self.layouts.global.bind_col_light(&self.device, col_light) &self,
col_light: Texture,
kinds: Texture,
) -> AtlasTextures<terrain::Locals, TerrainAtlasData> {
self.layouts.global.bind_atlas_textures(
&self.device,
&self.layouts.global.terrain_atlas_layout,
[col_light, kinds],
)
} }
pub fn sprite_bind_col_light(&self, col_light: Texture) -> ColLights<sprite::Locals> { pub fn sprite_bind_atlas_textures(
self.layouts.global.bind_col_light(&self.device, col_light) &self,
col_light: Texture,
) -> AtlasTextures<sprite::Locals, FigureSpriteAtlasData> {
self.layouts.global.bind_atlas_textures(
&self.device,
&self.layouts.global.figure_sprite_atlas_layout,
[col_light],
)
} }
} }

View File

@ -7,7 +7,8 @@ use super::{
model::{DynamicModel, Model, SubModel}, model::{DynamicModel, Model, SubModel},
pipelines::{ pipelines::{
blit, bloom, clouds, debug, figure, fluid, lod_object, lod_terrain, particle, shadow, blit, bloom, clouds, debug, figure, fluid, lod_object, lod_terrain, particle, shadow,
skybox, sprite, terrain, trail, ui, ColLights, GlobalsBindGroup, skybox, sprite, terrain, trail, ui, AtlasTextures, FigureSpriteAtlasData,
GlobalsBindGroup, TerrainAtlasData,
}, },
AltIndices, CullingMode, AltIndices, CullingMode,
}, },
@ -950,7 +951,7 @@ impl<'pass> FirstPassDrawer<'pass> {
TerrainDrawer { TerrainDrawer {
render_pass, render_pass,
col_lights: None, atlas_textures: None,
} }
} }
@ -966,14 +967,14 @@ impl<'pass> FirstPassDrawer<'pass> {
pub fn draw_sprites<'data: 'pass>( pub fn draw_sprites<'data: 'pass>(
&mut self, &mut self,
globals: &'data sprite::SpriteGlobalsBindGroup, globals: &'data sprite::SpriteGlobalsBindGroup,
col_lights: &'data ColLights<sprite::Locals>, atlas_textures: &'data AtlasTextures<sprite::Locals, FigureSpriteAtlasData>,
) -> SpriteDrawer<'_, 'pass> { ) -> SpriteDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("sprites", self.borrow.device); let mut render_pass = self.render_pass.scope("sprites", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.sprite.pipeline); render_pass.set_pipeline(&self.pipelines.sprite.pipeline);
set_quad_index_buffer::<sprite::Vertex>(&mut render_pass, self.borrow); set_quad_index_buffer::<sprite::Vertex>(&mut render_pass, self.borrow);
render_pass.set_bind_group(0, &globals.bind_group, &[]); render_pass.set_bind_group(0, &globals.bind_group, &[]);
render_pass.set_bind_group(2, &col_lights.bind_group, &[]); render_pass.set_bind_group(2, &atlas_textures.bind_group, &[]);
SpriteDrawer { SpriteDrawer {
render_pass, render_pass,
@ -1028,10 +1029,10 @@ impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
model: SubModel<'data, terrain::Vertex>, model: SubModel<'data, terrain::Vertex>,
locals: &'data figure::BoundLocals, locals: &'data figure::BoundLocals,
// TODO: don't rebind this every time once they are shared between figures // TODO: don't rebind this every time once they are shared between figures
col_lights: &'data ColLights<figure::Locals>, atlas_textures: &'data AtlasTextures<figure::Locals, FigureSpriteAtlasData>,
) { ) {
self.render_pass self.render_pass
.set_bind_group(2, &col_lights.bind_group, &[]); .set_bind_group(2, &atlas_textures.bind_group, &[]);
self.render_pass.set_bind_group(3, &locals.bind_group, &[]); self.render_pass.set_bind_group(3, &locals.bind_group, &[]);
self.render_pass.set_vertex_buffer(0, model.buf()); self.render_pass.set_vertex_buffer(0, model.buf());
self.render_pass self.render_pass
@ -1042,14 +1043,14 @@ impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
#[must_use] #[must_use]
pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> { pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
col_lights: Option<&'pass_ref Arc<ColLights<terrain::Locals>>>, atlas_textures: Option<&'pass_ref Arc<AtlasTextures<terrain::Locals, TerrainAtlasData>>>,
} }
impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> { impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>( pub fn draw<'data: 'pass>(
&mut self, &mut self,
model: &'data Model<terrain::Vertex>, model: &'data Model<terrain::Vertex>,
col_lights: &'data Arc<ColLights<terrain::Locals>>, atlas_textures: &'data Arc<AtlasTextures<terrain::Locals, TerrainAtlasData>>,
locals: &'data terrain::BoundLocals, locals: &'data terrain::BoundLocals,
alt_indices: &'data AltIndices, alt_indices: &'data AltIndices,
culling_mode: CullingMode, culling_mode: CullingMode,
@ -1067,15 +1068,15 @@ impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> {
let submodel = model.submodel(index_range); let submodel = model.submodel(index_range);
if self.col_lights if self.atlas_textures
// Check if we are still using the same atlas texture as the previous drawn // Check if we are still using the same atlas texture as the previous drawn
// chunk // chunk
.filter(|current_col_lights| Arc::ptr_eq(current_col_lights, col_lights)) .filter(|current_atlas_textures| Arc::ptr_eq(current_atlas_textures, atlas_textures))
.is_none() .is_none()
{ {
self.render_pass self.render_pass
.set_bind_group(2, &col_lights.bind_group, &[]); .set_bind_group(2, &atlas_textures.bind_group, &[]);
self.col_lights = Some(col_lights); self.atlas_textures = Some(atlas_textures);
}; };
self.render_pass.set_bind_group(3, &locals.bind_group, &[]); self.render_pass.set_bind_group(3, &locals.bind_group, &[]);

View File

@ -8,8 +8,8 @@ use crate::{
segment::{generate_mesh_base_vol_figure, generate_mesh_base_vol_terrain}, segment::{generate_mesh_base_vol_figure, generate_mesh_base_vol_terrain},
}, },
render::{ render::{
BoneMeshes, ColLightInfo, FigureModel, Instances, Mesh, Renderer, SpriteInstance, pipelines, BoneMeshes, FigureModel, FigureSpriteAtlasData, Instances, Mesh, Renderer,
TerrainVertex, SpriteInstance, TerrainVertex,
}, },
scene::{ scene::{
camera::CameraMode, camera::CameraMode,
@ -42,7 +42,8 @@ use vek::*;
/// A type produced by mesh worker threads corresponding to the information /// A type produced by mesh worker threads corresponding to the information
/// needed to mesh figures. /// needed to mesh figures.
pub struct MeshWorkerResponse<const N: usize> { pub struct MeshWorkerResponse<const N: usize> {
col_light: ColLightInfo, atlas_texture_data: FigureSpriteAtlasData,
atlas_size: Vec2<u16>,
opaque: Mesh<TerrainVertex>, opaque: Mesh<TerrainVertex>,
bounds: anim::vek::Aabb<f32>, bounds: anim::vek::Aabb<f32>,
vertex_range: [Range<u32>; N], vertex_range: [Range<u32>; N],
@ -51,7 +52,10 @@ pub struct MeshWorkerResponse<const N: usize> {
/// A type produced by mesh worker threads corresponding to the information /// A type produced by mesh worker threads corresponding to the information
/// needed to mesh figures. /// needed to mesh figures.
pub struct TerrainMeshWorkerResponse<const N: usize> { pub struct TerrainMeshWorkerResponse<const N: usize> {
col_light: ColLightInfo, // TODO: This probably needs fixing to use `TerrainAtlasData`. However, right now, we just
// treat volume entities like regular figures for the sake of rendering.
atlas_texture_data: FigureSpriteAtlasData,
atlas_size: Vec2<u16>,
opaque: Mesh<TerrainVertex>, opaque: Mesh<TerrainVertex>,
bounds: anim::vek::Aabb<f32>, bounds: anim::vek::Aabb<f32>,
vertex_range: [Range<u32>; N], vertex_range: [Range<u32>; N],
@ -323,7 +327,7 @@ where
pub fn get_model<'b>( pub fn get_model<'b>(
&'b self, &'b self,
// TODO: If we ever convert to using an atlas here, use this. // TODO: If we ever convert to using an atlas here, use this.
_col_lights: &super::FigureColLights, _atlas: &super::FigureAtlas,
body: Skel::Body, body: Skel::Body,
inventory: Option<&Inventory>, inventory: Option<&Inventory>,
// TODO: Consider updating the tick by putting it in a Cell. // TODO: Consider updating the tick by putting it in a Cell.
@ -358,7 +362,7 @@ where
pub fn clear_models(&mut self) { self.models.clear(); } pub fn clear_models(&mut self) { self.models.clear(); }
pub fn clean(&mut self, col_lights: &mut super::FigureColLights, tick: u64) pub fn clean(&mut self, atlas: &mut super::FigureAtlas, tick: u64)
where where
<Skel::Body as BodySpec>::Spec: Clone, <Skel::Body as BodySpec>::Spec: Clone,
{ {
@ -370,7 +374,7 @@ where
let alive = *last_used + delta > tick; let alive = *last_used + delta > tick;
if !alive { if !alive {
if let Some(model_entry) = model_entry.get_done() { if let Some(model_entry) = model_entry.get_done() {
col_lights.atlas.deallocate(model_entry.allocation().id); atlas.atlas.deallocate(model_entry.allocation().id);
} }
} }
alive alive
@ -391,7 +395,7 @@ where
pub fn get_or_create_model<'c>( pub fn get_or_create_model<'c>(
&'c mut self, &'c mut self,
renderer: &mut Renderer, renderer: &mut Renderer,
col_lights: &mut super::FigureColLights, atlas: &mut super::FigureAtlas,
body: Skel::Body, body: Skel::Body,
inventory: Option<&Inventory>, inventory: Option<&Inventory>,
extra: <Skel::Body as BodySpec>::Extra, extra: <Skel::Body as BodySpec>::Extra,
@ -428,15 +432,17 @@ where
match model { match model {
FigureModelEntryFuture::Pending(recv) => { FigureModelEntryFuture::Pending(recv) => {
if let Some(MeshWorkerResponse { if let Some(MeshWorkerResponse {
col_light, atlas_texture_data,
atlas_size,
opaque, opaque,
bounds, bounds,
vertex_range, vertex_range,
}) = Arc::get_mut(recv).take().and_then(|cell| cell.take()) }) = Arc::get_mut(recv).take().and_then(|cell| cell.take())
{ {
let model_entry = col_lights.create_figure( let model_entry = atlas.create_figure(
renderer, renderer,
col_light, atlas_texture_data,
atlas_size,
(opaque, bounds), (opaque, bounds),
vertex_range, vertex_range,
); );
@ -480,7 +486,7 @@ where
// list. Returns the vertex bounds of the meshed model within the opaque // list. Returns the vertex bounds of the meshed model within the opaque
// mesh. // mesh.
let mut make_model = |generate_mesh: for<'a, 'b> fn( let mut make_model = |generate_mesh: for<'a, 'b> fn(
&mut GreedyMesh<'a>, &mut GreedyMesh<'a, FigureSpriteAtlasData>,
&'b mut _, &'b mut _,
&'a _, &'a _,
_, _,
@ -528,7 +534,7 @@ where
}; };
fn generate_mesh<'a>( fn generate_mesh<'a>(
greedy: &mut GreedyMesh<'a>, greedy: &mut GreedyMesh<'a, FigureSpriteAtlasData>,
opaque_mesh: &mut Mesh<TerrainVertex>, opaque_mesh: &mut Mesh<TerrainVertex>,
segment: &'a Segment, segment: &'a Segment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -542,7 +548,7 @@ where
} }
fn generate_mesh_lod_mid<'a>( fn generate_mesh_lod_mid<'a>(
greedy: &mut GreedyMesh<'a>, greedy: &mut GreedyMesh<'a, FigureSpriteAtlasData>,
opaque_mesh: &mut Mesh<TerrainVertex>, opaque_mesh: &mut Mesh<TerrainVertex>,
segment: &'a Segment, segment: &'a Segment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -563,7 +569,7 @@ where
} }
fn generate_mesh_lod_low<'a>( fn generate_mesh_lod_low<'a>(
greedy: &mut GreedyMesh<'a>, greedy: &mut GreedyMesh<'a, FigureSpriteAtlasData>,
opaque_mesh: &mut Mesh<TerrainVertex>, opaque_mesh: &mut Mesh<TerrainVertex>,
segment: &'a Segment, segment: &'a Segment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -589,8 +595,10 @@ where
make_model(generate_mesh_lod_low), make_model(generate_mesh_lod_low),
]; ];
let (atlas_texture_data, atlas_size) = greedy.finalize();
slot_.store(Some(MeshWorkerResponse { slot_.store(Some(MeshWorkerResponse {
col_light: greedy.finalize(), atlas_texture_data,
atlas_size,
opaque, opaque,
bounds: figure_bounds, bounds: figure_bounds,
vertex_range: models, vertex_range: models,
@ -619,7 +627,7 @@ where
pub fn get_or_create_terrain_model<'c>( pub fn get_or_create_terrain_model<'c>(
&'c mut self, &'c mut self,
renderer: &mut Renderer, renderer: &mut Renderer,
col_lights: &mut super::FigureColLights, atlas: &mut super::FigureAtlas,
body: Skel::Body, body: Skel::Body,
extra: <Skel::Body as BodySpec>::Extra, extra: <Skel::Body as BodySpec>::Extra,
tick: u64, tick: u64,
@ -647,7 +655,8 @@ where
match model { match model {
TerrainModelEntryFuture::Pending(recv) => { TerrainModelEntryFuture::Pending(recv) => {
if let Some(TerrainMeshWorkerResponse { if let Some(TerrainMeshWorkerResponse {
col_light, atlas_texture_data,
atlas_size,
opaque, opaque,
bounds, bounds,
vertex_range, vertex_range,
@ -656,9 +665,10 @@ where
blocks_offset, blocks_offset,
}) = Arc::get_mut(recv).take().and_then(|cell| cell.take()) }) = Arc::get_mut(recv).take().and_then(|cell| cell.take())
{ {
let model_entry = col_lights.create_terrain( let model_entry = atlas.create_terrain(
renderer, renderer,
col_light, atlas_texture_data,
atlas_size,
(opaque, bounds), (opaque, bounds),
vertex_range, vertex_range,
sprite_instances, sprite_instances,
@ -707,7 +717,7 @@ where
// list. Returns the vertex bounds of the meshed model within the opaque // list. Returns the vertex bounds of the meshed model within the opaque
// mesh. // mesh.
let mut make_model = |generate_mesh: for<'a, 'b> fn( let mut make_model = |generate_mesh: for<'a, 'b> fn(
&mut GreedyMesh<'a>, &mut GreedyMesh<'a, FigureSpriteAtlasData>,
&'b mut _, &'b mut _,
&'a _, &'a _,
_, _,
@ -755,7 +765,7 @@ where
}; };
fn generate_mesh<'a>( fn generate_mesh<'a>(
greedy: &mut GreedyMesh<'a>, greedy: &mut GreedyMesh<'a, FigureSpriteAtlasData>,
opaque_mesh: &mut Mesh<TerrainVertex>, opaque_mesh: &mut Mesh<TerrainVertex>,
segment: &'a TerrainSegment, segment: &'a TerrainSegment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -769,7 +779,7 @@ where
} }
fn generate_mesh_lod_mid<'a>( fn generate_mesh_lod_mid<'a>(
greedy: &mut GreedyMesh<'a>, greedy: &mut GreedyMesh<'a, FigureSpriteAtlasData>,
opaque_mesh: &mut Mesh<TerrainVertex>, opaque_mesh: &mut Mesh<TerrainVertex>,
segment: &'a TerrainSegment, segment: &'a TerrainSegment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -790,7 +800,7 @@ where
} }
fn generate_mesh_lod_low<'a>( fn generate_mesh_lod_low<'a>(
greedy: &mut GreedyMesh<'a>, greedy: &mut GreedyMesh<'a, FigureSpriteAtlasData>,
opaque_mesh: &mut Mesh<TerrainVertex>, opaque_mesh: &mut Mesh<TerrainVertex>,
segment: &'a TerrainSegment, segment: &'a TerrainSegment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -819,13 +829,15 @@ where
let (dyna, offset) = &meshes[0].as_ref().unwrap(); let (dyna, offset) = &meshes[0].as_ref().unwrap();
let block_iter = dyna.vol_iter(Vec3::zero(), dyna.sz.as_()).map(|(pos, block)| (pos, *block)); let block_iter = dyna.vol_iter(Vec3::zero(), dyna.sz.as_()).map(|(pos, block)| (pos, *block));
let (atlas_texture_data, atlas_size) = greedy.finalize();
slot_.store(Some(TerrainMeshWorkerResponse { slot_.store(Some(TerrainMeshWorkerResponse {
col_light: greedy.finalize(), atlas_texture_data,
atlas_size,
opaque, opaque,
bounds: figure_bounds, bounds: figure_bounds,
vertex_range: models, vertex_range: models,
sprite_instances: { sprite_instances: {
let mut instances = from_fn(|_| Vec::new()); let mut instances = from_fn::<Vec<pipelines::sprite::Instance>, SPRITE_LOD_LEVELS, _>(|_| Vec::new());
get_sprite_instances( get_sprite_instances(
&mut instances, &mut instances,
|lod, instance, _| { |lod, instance, _| {

View File

@ -12,11 +12,11 @@ use crate::{
pipelines::{ pipelines::{
self, self,
terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals}, terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals},
trail, ColLights, trail, AtlasData, AtlasTextures, FigureSpriteAtlasData,
}, },
AltIndices, ColLightInfo, CullingMode, FigureBoneData, FigureDrawer, FigureLocals, AltIndices, CullingMode, FigureBoneData, FigureDrawer, FigureLocals, FigureModel,
FigureModel, FigureShadowDrawer, Instances, Mesh, Quad, RenderError, Renderer, FigureShadowDrawer, Instances, Mesh, Quad, RenderError, Renderer, SpriteDrawer,
SpriteDrawer, SpriteInstance, SubModel, TerrainVertex, SpriteInstance, SubModel, TerrainVertex,
}, },
scene::{ scene::{
camera::{Camera, CameraMode, Dependents}, camera::{Camera, CameraMode, Dependents},
@ -80,7 +80,7 @@ pub type CameraData<'a> = (&'a Camera, f32);
pub type FigureModelRef<'a> = ( pub type FigureModelRef<'a> = (
&'a pipelines::figure::BoundLocals, &'a pipelines::figure::BoundLocals,
SubModel<'a, TerrainVertex>, SubModel<'a, TerrainVertex>,
&'a ColLights<pipelines::figure::Locals>, &'a AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData>,
); );
pub trait ModelEntry { pub trait ModelEntry {
@ -88,7 +88,7 @@ pub trait ModelEntry {
fn lod_model(&self, lod: usize) -> Option<SubModel<TerrainVertex>>; fn lod_model(&self, lod: usize) -> Option<SubModel<TerrainVertex>>;
fn col_lights(&self) -> &ColLights<pipelines::figure::Locals>; fn atlas_textures(&self) -> &AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData>;
} }
/// An entry holding enough information to draw or destroy a figure in a /// An entry holding enough information to draw or destroy a figure in a
@ -104,7 +104,7 @@ pub struct FigureModelEntry<const N: usize> {
/// Texture used to store color/light information for this figure entry. /// Texture used to store color/light information for this figure entry.
/* TODO: Consider using mipmaps instead of storing multiple texture atlases for different /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different
* LOD levels. */ * LOD levels. */
col_lights: ColLights<pipelines::figure::Locals>, atlas_textures: AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData>,
/// Vertex ranges stored in this figure entry; there may be several for one /// Vertex ranges stored in this figure entry; there may be several for one
/// figure, because of LOD models. /// figure, because of LOD models.
lod_vertex_ranges: [Range<u32>; N], lod_vertex_ranges: [Range<u32>; N],
@ -122,7 +122,9 @@ impl<const N: usize> ModelEntry for FigureModelEntry<N> {
.map(|m| m.submodel(self.lod_vertex_ranges[lod].clone())) .map(|m| m.submodel(self.lod_vertex_ranges[lod].clone()))
} }
fn col_lights(&self) -> &ColLights<pipelines::figure::Locals> { &self.col_lights } fn atlas_textures(&self) -> &AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData> {
&self.atlas_textures
}
} }
/// An entry holding enough information to draw or destroy a figure in a /// An entry holding enough information to draw or destroy a figure in a
@ -138,7 +140,7 @@ pub struct TerrainModelEntry<const N: usize> {
/// Texture used to store color/light information for this figure entry. /// Texture used to store color/light information for this figure entry.
/* TODO: Consider using mipmaps instead of storing multiple texture atlases for different /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different
* LOD levels. */ * LOD levels. */
col_lights: ColLights<pipelines::figure::Locals>, atlas_textures: AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData>,
/// Vertex ranges stored in this figure entry; there may be several for one /// Vertex ranges stored in this figure entry; there may be several for one
/// figure, because of LOD models. /// figure, because of LOD models.
lod_vertex_ranges: [Range<u32>; N], lod_vertex_ranges: [Range<u32>; N],
@ -162,7 +164,9 @@ impl<const N: usize> ModelEntry for TerrainModelEntry<N> {
.map(|m| m.submodel(self.lod_vertex_ranges[lod].clone())) .map(|m| m.submodel(self.lod_vertex_ranges[lod].clone()))
} }
fn col_lights(&self) -> &ColLights<pipelines::figure::Locals> { &self.col_lights } fn atlas_textures(&self) -> &AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData> {
&self.atlas_textures
}
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -179,10 +183,12 @@ impl<'a, const N: usize> ModelEntryRef<'a, N> {
} }
} }
fn col_lights(&self) -> &'a ColLights<pipelines::figure::Locals> { fn atlas_textures(
&self,
) -> &'a AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData> {
match self { match self {
ModelEntryRef::Figure(e) => e.col_lights(), ModelEntryRef::Figure(e) => e.atlas_textures(),
ModelEntryRef::Terrain(e) => e.col_lights(), ModelEntryRef::Terrain(e) => e.atlas_textures(),
} }
} }
} }
@ -498,7 +504,7 @@ impl FigureMgrStates {
} }
pub struct FigureMgr { pub struct FigureMgr {
col_lights: FigureColLights, atlas: FigureAtlas,
model_cache: FigureModelCache, model_cache: FigureModelCache,
theropod_model_cache: FigureModelCache<TheropodSkeleton>, theropod_model_cache: FigureModelCache<TheropodSkeleton>,
quadruped_small_model_cache: FigureModelCache<QuadrupedSmallSkeleton>, quadruped_small_model_cache: FigureModelCache<QuadrupedSmallSkeleton>,
@ -523,7 +529,7 @@ pub struct FigureMgr {
impl FigureMgr { impl FigureMgr {
pub fn new(renderer: &mut Renderer) -> Self { pub fn new(renderer: &mut Renderer) -> Self {
Self { Self {
col_lights: FigureColLights::new(renderer), atlas: FigureAtlas::new(renderer),
model_cache: FigureModelCache::new(), model_cache: FigureModelCache::new(),
theropod_model_cache: FigureModelCache::new(), theropod_model_cache: FigureModelCache::new(),
quadruped_small_model_cache: FigureModelCache::new(), quadruped_small_model_cache: FigureModelCache::new(),
@ -546,7 +552,7 @@ impl FigureMgr {
} }
} }
pub fn col_lights(&self) -> &FigureColLights { &self.col_lights } pub fn atlas(&self) -> &FigureAtlas { &self.atlas }
fn any_watcher_reloaded(&mut self) -> bool { fn any_watcher_reloaded(&mut self) -> bool {
self.model_cache.watcher_reloaded() self.model_cache.watcher_reloaded()
@ -573,7 +579,7 @@ impl FigureMgr {
span!(_guard, "clean", "FigureManager::clean"); span!(_guard, "clean", "FigureManager::clean");
if self.any_watcher_reloaded() { if self.any_watcher_reloaded() {
self.col_lights.atlas.clear(); self.atlas.atlas.clear();
self.model_cache.clear_models(); self.model_cache.clear_models();
self.theropod_model_cache.clear_models(); self.theropod_model_cache.clear_models();
@ -595,33 +601,26 @@ impl FigureMgr {
self.arthropod_model_cache.clear_models(); self.arthropod_model_cache.clear_models();
} }
self.model_cache.clean(&mut self.col_lights, tick); self.model_cache.clean(&mut self.atlas, tick);
self.theropod_model_cache.clean(&mut self.col_lights, tick); self.theropod_model_cache.clean(&mut self.atlas, tick);
self.quadruped_small_model_cache self.quadruped_small_model_cache
.clean(&mut self.col_lights, tick); .clean(&mut self.atlas, tick);
self.quadruped_medium_model_cache self.quadruped_medium_model_cache
.clean(&mut self.col_lights, tick); .clean(&mut self.atlas, tick);
self.quadruped_low_model_cache self.quadruped_low_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick); self.bird_medium_model_cache.clean(&mut self.atlas, tick);
self.bird_medium_model_cache self.bird_large_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick); self.dragon_model_cache.clean(&mut self.atlas, tick);
self.bird_large_model_cache self.fish_medium_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick); self.fish_small_model_cache.clean(&mut self.atlas, tick);
self.dragon_model_cache.clean(&mut self.col_lights, tick); self.biped_large_model_cache.clean(&mut self.atlas, tick);
self.fish_medium_model_cache self.biped_small_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick); self.object_model_cache.clean(&mut self.atlas, tick);
self.fish_small_model_cache self.item_drop_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick); self.ship_model_cache.clean(&mut self.atlas, tick);
self.biped_large_model_cache self.golem_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick); self.volume_model_cache.clean(&mut self.atlas, tick);
self.biped_small_model_cache self.arthropod_model_cache.clean(&mut self.atlas, tick);
.clean(&mut self.col_lights, tick);
self.object_model_cache.clean(&mut self.col_lights, tick);
self.item_drop_model_cache.clean(&mut self.col_lights, tick);
self.ship_model_cache.clean(&mut self.col_lights, tick);
self.golem_model_cache.clean(&mut self.col_lights, tick);
self.volume_model_cache.clean(&mut self.col_lights, tick);
self.arthropod_model_cache.clean(&mut self.col_lights, tick);
} }
pub fn update_lighting(&mut self, scene_data: &SceneData) { pub fn update_lighting(&mut self, scene_data: &SceneData) {
@ -1092,7 +1091,7 @@ impl FigureMgr {
Body::Humanoid(body) => { Body::Humanoid(body) => {
let (model, skeleton_attr) = self.model_cache.get_or_create_model( let (model, skeleton_attr) = self.model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -2212,7 +2211,7 @@ impl FigureMgr {
let (model, skeleton_attr) = let (model, skeleton_attr) =
self.quadruped_small_model_cache.get_or_create_model( self.quadruped_small_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -2412,7 +2411,7 @@ impl FigureMgr {
let (model, skeleton_attr) = let (model, skeleton_attr) =
self.quadruped_medium_model_cache.get_or_create_model( self.quadruped_medium_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -2789,7 +2788,7 @@ impl FigureMgr {
let (model, skeleton_attr) = let (model, skeleton_attr) =
self.quadruped_low_model_cache.get_or_create_model( self.quadruped_low_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -3185,7 +3184,7 @@ impl FigureMgr {
Body::BirdMedium(body) => { Body::BirdMedium(body) => {
let (model, skeleton_attr) = self.bird_medium_model_cache.get_or_create_model( let (model, skeleton_attr) = self.bird_medium_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -3515,7 +3514,7 @@ impl FigureMgr {
Body::FishMedium(body) => { Body::FishMedium(body) => {
let (model, skeleton_attr) = self.fish_medium_model_cache.get_or_create_model( let (model, skeleton_attr) = self.fish_medium_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -3598,7 +3597,7 @@ impl FigureMgr {
Body::BipedSmall(body) => { Body::BipedSmall(body) => {
let (model, skeleton_attr) = self.biped_small_model_cache.get_or_create_model( let (model, skeleton_attr) = self.biped_small_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -4159,7 +4158,7 @@ impl FigureMgr {
Body::Dragon(body) => { Body::Dragon(body) => {
let (model, skeleton_attr) = self.dragon_model_cache.get_or_create_model( let (model, skeleton_attr) = self.dragon_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -4246,7 +4245,7 @@ impl FigureMgr {
Body::Theropod(body) => { Body::Theropod(body) => {
let (model, skeleton_attr) = self.theropod_model_cache.get_or_create_model( let (model, skeleton_attr) = self.theropod_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -4425,7 +4424,7 @@ impl FigureMgr {
Body::Arthropod(body) => { Body::Arthropod(body) => {
let (model, skeleton_attr) = self.arthropod_model_cache.get_or_create_model( let (model, skeleton_attr) = self.arthropod_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -4717,7 +4716,7 @@ impl FigureMgr {
Body::BirdLarge(body) => { Body::BirdLarge(body) => {
let (model, skeleton_attr) = self.bird_large_model_cache.get_or_create_model( let (model, skeleton_attr) = self.bird_large_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -5040,7 +5039,7 @@ impl FigureMgr {
Body::FishSmall(body) => { Body::FishSmall(body) => {
let (model, skeleton_attr) = self.fish_small_model_cache.get_or_create_model( let (model, skeleton_attr) = self.fish_small_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -5123,7 +5122,7 @@ impl FigureMgr {
Body::BipedLarge(body) => { Body::BipedLarge(body) => {
let (model, skeleton_attr) = self.biped_large_model_cache.get_or_create_model( let (model, skeleton_attr) = self.biped_large_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -5828,7 +5827,7 @@ impl FigureMgr {
Body::Golem(body) => { Body::Golem(body) => {
let (model, skeleton_attr) = self.golem_model_cache.get_or_create_model( let (model, skeleton_attr) = self.golem_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -6069,7 +6068,7 @@ impl FigureMgr {
Body::Object(body) => { Body::Object(body) => {
let (model, skeleton_attr) = self.object_model_cache.get_or_create_model( let (model, skeleton_attr) = self.object_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -6191,7 +6190,7 @@ impl FigureMgr {
let item_key = item.map(ItemKey::from); let item_key = item.map(ItemKey::from);
let (model, skeleton_attr) = self.item_drop_model_cache.get_or_create_model( let (model, skeleton_attr) = self.item_drop_model_cache.get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
inventory, inventory,
(), (),
@ -6255,7 +6254,7 @@ impl FigureMgr {
let (model, _skeleton_attr) = let (model, _skeleton_attr) =
self.volume_model_cache.get_or_create_terrain_model( self.volume_model_cache.get_or_create_terrain_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
vk, vk,
Arc::clone(vol), Arc::clone(vol),
tick, tick,
@ -6282,7 +6281,7 @@ impl FigureMgr {
} else if body.manifest_entry().is_some() { } else if body.manifest_entry().is_some() {
self.ship_model_cache.get_or_create_terrain_model( self.ship_model_cache.get_or_create_terrain_model(
renderer, renderer,
&mut self.col_lights, &mut self.atlas,
body, body,
(), (),
tick, tick,
@ -6560,7 +6559,7 @@ impl FigureMgr {
// Don't render player // Don't render player
.filter(|(entity, _, _, _, _, _, _)| *entity != viewpoint_entity) .filter(|(entity, _, _, _, _, _, _)| *entity != viewpoint_entity)
{ {
if let Some((bound, model, col_lights)) = self.get_model_for_render( if let Some((bound, model, atlas)) = self.get_model_for_render(
tick, tick,
camera, camera,
character_state, character_state,
@ -6582,7 +6581,7 @@ impl FigureMgr {
None None
}, },
) { ) {
drawer.draw(model, bound, col_lights); drawer.draw(model, bound, atlas);
} }
} }
} }
@ -6616,7 +6615,7 @@ impl FigureMgr {
let inventory_storage = ecs.read_storage::<Inventory>(); let inventory_storage = ecs.read_storage::<Inventory>();
let inventory = inventory_storage.get(viewpoint_entity); let inventory = inventory_storage.get(viewpoint_entity);
if let Some((bound, model, col_lights)) = self.get_model_for_render( if let Some((bound, model, atlas)) = self.get_model_for_render(
tick, tick,
camera, camera,
character_state, character_state,
@ -6635,10 +6634,10 @@ impl FigureMgr {
None None
}, },
) { ) {
drawer.draw(model, bound, col_lights); drawer.draw(model, bound, atlas);
/*renderer.render_player_shadow( /*renderer.render_player_shadow(
model, model,
&col_lights, &atlas,
global, global,
bone_consts, bone_consts,
lod, lod,
@ -6677,7 +6676,7 @@ impl FigureMgr {
let character_state = if is_viewpoint { character_state } else { None }; let character_state = if is_viewpoint { character_state } else { None };
let FigureMgr { let FigureMgr {
col_lights: ref col_lights_, atlas: ref atlas_,
model_cache, model_cache,
theropod_model_cache, theropod_model_cache,
quadruped_small_model_cache, quadruped_small_model_cache,
@ -6718,7 +6717,7 @@ impl FigureMgr {
arthropod_states, arthropod_states,
}, },
} = self; } = self;
let col_lights = col_lights_; let atlas = atlas_;
if let Some((bound, model_entry)) = match body { if let Some((bound, model_entry)) = match body {
Body::Humanoid(body) => character_states Body::Humanoid(body) => character_states
.get(&entity) .get(&entity)
@ -6728,7 +6727,7 @@ impl FigureMgr {
state.bound(), state.bound(),
model_cache model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6747,7 +6746,7 @@ impl FigureMgr {
state.bound(), state.bound(),
quadruped_small_model_cache quadruped_small_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6766,7 +6765,7 @@ impl FigureMgr {
state.bound(), state.bound(),
quadruped_medium_model_cache quadruped_medium_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6785,7 +6784,7 @@ impl FigureMgr {
state.bound(), state.bound(),
quadruped_low_model_cache quadruped_low_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6804,7 +6803,7 @@ impl FigureMgr {
state.bound(), state.bound(),
bird_medium_model_cache bird_medium_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6823,7 +6822,7 @@ impl FigureMgr {
state.bound(), state.bound(),
fish_medium_model_cache fish_medium_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6842,7 +6841,7 @@ impl FigureMgr {
state.bound(), state.bound(),
theropod_model_cache theropod_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6861,7 +6860,7 @@ impl FigureMgr {
state.bound(), state.bound(),
dragon_model_cache dragon_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6880,7 +6879,7 @@ impl FigureMgr {
state.bound(), state.bound(),
bird_large_model_cache bird_large_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6899,7 +6898,7 @@ impl FigureMgr {
state.bound(), state.bound(),
fish_small_model_cache fish_small_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6918,7 +6917,7 @@ impl FigureMgr {
state.bound(), state.bound(),
biped_large_model_cache biped_large_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6937,7 +6936,7 @@ impl FigureMgr {
state.bound(), state.bound(),
biped_small_model_cache biped_small_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6956,7 +6955,7 @@ impl FigureMgr {
state.bound(), state.bound(),
golem_model_cache golem_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6975,7 +6974,7 @@ impl FigureMgr {
state.bound(), state.bound(),
arthropod_model_cache arthropod_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -6994,7 +6993,7 @@ impl FigureMgr {
state.bound(), state.bound(),
object_model_cache object_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -7013,7 +7012,7 @@ impl FigureMgr {
state.bound(), state.bound(),
item_drop_model_cache item_drop_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
inventory, inventory,
tick, tick,
@ -7034,7 +7033,7 @@ impl FigureMgr {
state.bound(), state.bound(),
volume_model_cache volume_model_cache
.get_model( .get_model(
col_lights, atlas,
VolumeKey { entity, mut_count }, VolumeKey { entity, mut_count },
None, None,
tick, tick,
@ -7054,7 +7053,7 @@ impl FigureMgr {
state.bound(), state.bound(),
ship_model_cache ship_model_cache
.get_model( .get_model(
col_lights, atlas,
body, body,
None, None,
tick, tick,
@ -7097,7 +7096,7 @@ impl FigureMgr {
model_entry.lod_model(0) model_entry.lod_model(0)
}; };
Some((bound, model?, col_lights_.texture(model_entry))) Some((bound, model?, atlas_.texture(model_entry)))
} else { } else {
// trace!("Body has no saved figure"); // trace!("Body has no saved figure");
None None
@ -7252,16 +7251,16 @@ impl FigureMgr {
pub fn figure_count_visible(&self) -> usize { self.states.count_visible() } pub fn figure_count_visible(&self) -> usize { self.states.count_visible() }
} }
pub struct FigureColLights { pub struct FigureAtlas {
atlas: AtlasAllocator, atlas: AtlasAllocator,
// col_lights: Texture<ColLightFmt>, // atlas_texture: Texture<ColLightFmt>,
} }
impl FigureColLights { impl FigureAtlas {
pub fn new(renderer: &mut Renderer) -> Self { pub fn new(renderer: &mut Renderer) -> Self {
let atlas = Self::make_atlas(renderer).expect("Failed to create texture atlas for figures"); let atlas = Self::make_atlas(renderer).expect("Failed to create texture atlas for figures");
Self { Self {
atlas, /* col_lights, */ atlas, /* atlas_texture, */
} }
} }
@ -7269,9 +7268,9 @@ impl FigureColLights {
pub fn texture<'a, const N: usize>( pub fn texture<'a, const N: usize>(
&'a self, &'a self,
model: ModelEntryRef<'a, N>, model: ModelEntryRef<'a, N>,
) -> &'a ColLights<pipelines::figure::Locals> { ) -> &'a AtlasTextures<pipelines::figure::Locals, FigureSpriteAtlasData> {
/* &self.col_lights */ /* &self.atlas_texture */
model.col_lights() model.atlas_textures()
} }
/// NOTE: Panics if the opaque model's length does not fit in a u32. /// NOTE: Panics if the opaque model's length does not fit in a u32.
@ -7285,17 +7284,21 @@ impl FigureColLights {
pub fn create_figure<const N: usize>( pub fn create_figure<const N: usize>(
&mut self, &mut self,
renderer: &mut Renderer, renderer: &mut Renderer,
(tex, tex_size): ColLightInfo, atlas_texture_data: FigureSpriteAtlasData,
atlas_size: Vec2<u16>,
(opaque, bounds): (Mesh<TerrainVertex>, math::Aabb<f32>), (opaque, bounds): (Mesh<TerrainVertex>, math::Aabb<f32>),
vertex_ranges: [Range<u32>; N], vertex_ranges: [Range<u32>; N],
) -> FigureModelEntry<N> { ) -> FigureModelEntry<N> {
span!(_guard, "create_figure", "FigureColLights::create_figure"); span!(_guard, "create_figure", "FigureColLights::create_figure");
let atlas = &mut self.atlas; let atlas = &mut self.atlas;
let allocation = atlas let allocation = atlas
.allocate(guillotiere::Size::new(tex_size.x as i32, tex_size.y as i32)) .allocate(guillotiere::Size::new(
atlas_size.x as i32,
atlas_size.y as i32,
))
.expect("Not yet implemented: allocate new atlas on allocation failure."); .expect("Not yet implemented: allocate new atlas on allocation failure.");
let col_lights = pipelines::shadow::create_col_lights(renderer, &(tex, tex_size)); let [atlas_textures] = atlas_texture_data.create_textures(renderer, atlas_size);
let col_lights = renderer.figure_bind_col_light(col_lights); let atlas_textures = renderer.figure_bind_atlas_textures(atlas_textures);
let model_len = u32::try_from(opaque.vertices().len()) let model_len = u32::try_from(opaque.vertices().len())
.expect("The model size for this figure does not fit in a u32!"); .expect("The model size for this figure does not fit in a u32!");
let model = renderer.create_model(&opaque); let model = renderer.create_model(&opaque);
@ -7313,7 +7316,7 @@ impl FigureColLights {
FigureModelEntry { FigureModelEntry {
_bounds: bounds, _bounds: bounds,
allocation, allocation,
col_lights, atlas_textures,
lod_vertex_ranges: vertex_ranges, lod_vertex_ranges: vertex_ranges,
model: FigureModel { opaque: model }, model: FigureModel { opaque: model },
} }
@ -7330,7 +7333,9 @@ impl FigureColLights {
pub fn create_terrain<const N: usize>( pub fn create_terrain<const N: usize>(
&mut self, &mut self,
renderer: &mut Renderer, renderer: &mut Renderer,
(tex, tex_size): ColLightInfo, // TODO: Use `TerrainAtlasData`
atlas_texture_data: FigureSpriteAtlasData,
atlas_size: Vec2<u16>,
(opaque, bounds): (Mesh<TerrainVertex>, math::Aabb<f32>), (opaque, bounds): (Mesh<TerrainVertex>, math::Aabb<f32>),
vertex_ranges: [Range<u32>; N], vertex_ranges: [Range<u32>; N],
sprite_instances: [Vec<SpriteInstance>; SPRITE_LOD_LEVELS], sprite_instances: [Vec<SpriteInstance>; SPRITE_LOD_LEVELS],
@ -7340,10 +7345,14 @@ impl FigureColLights {
span!(_guard, "create_figure", "FigureColLights::create_figure"); span!(_guard, "create_figure", "FigureColLights::create_figure");
let atlas = &mut self.atlas; let atlas = &mut self.atlas;
let allocation = atlas let allocation = atlas
.allocate(guillotiere::Size::new(tex_size.x as i32, tex_size.y as i32)) .allocate(guillotiere::Size::new(
atlas_size.x as i32,
atlas_size.y as i32,
))
.expect("Not yet implemented: allocate new atlas on allocation failure."); .expect("Not yet implemented: allocate new atlas on allocation failure.");
let col_lights = pipelines::shadow::create_col_lights(renderer, &(tex, tex_size)); let [col_lights] = atlas_texture_data.create_textures(renderer, atlas_size);
let col_lights = renderer.figure_bind_col_light(col_lights); // TODO: Use `kinds` texture for volume entities
let atlas_textures = renderer.figure_bind_atlas_textures(col_lights);
let model_len = u32::try_from(opaque.vertices().len()) let model_len = u32::try_from(opaque.vertices().len())
.expect("The model size for this figure does not fit in a u32!"); .expect("The model size for this figure does not fit in a u32!");
let model = renderer.create_model(&opaque); let model = renderer.create_model(&opaque);
@ -7364,7 +7373,7 @@ impl FigureColLights {
TerrainModelEntry { TerrainModelEntry {
_bounds: bounds, _bounds: bounds,
allocation, allocation,
col_lights, atlas_textures,
lod_vertex_ranges: vertex_ranges, lod_vertex_ranges: vertex_ranges,
model: FigureModel { opaque: model }, model: FigureModel { opaque: model },
sprite_instances, sprite_instances,

View File

@ -1403,7 +1403,7 @@ impl Scene {
// Draws sprites // Draws sprites
let mut sprite_drawer = first_pass.draw_sprites( let mut sprite_drawer = first_pass.draw_sprites(
&self.terrain.sprite_globals, &self.terrain.sprite_globals,
&self.terrain.sprite_col_lights, &self.terrain.sprite_atlas_textures,
); );
self.figure_mgr.render_sprites( self.figure_mgr.render_sprites(
&mut sprite_drawer, &mut sprite_drawer,

View File

@ -1,14 +1,15 @@
use crate::{ use crate::{
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_figure}, mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_figure},
render::{ render::{
create_skybox_mesh, BoneMeshes, Consts, FigureModel, FirstPassDrawer, GlobalModel, Globals, create_skybox_mesh, pipelines::FigureSpriteAtlasData, BoneMeshes, Consts, FigureModel,
GlobalsBindGroup, Light, LodData, Mesh, Model, PointLightMatrix, RainOcclusionLocals, FirstPassDrawer, GlobalModel, Globals, GlobalsBindGroup, Light, LodData, Mesh, Model,
Renderer, Shadow, ShadowLocals, SkyboxVertex, TerrainVertex, PointLightMatrix, RainOcclusionLocals, Renderer, Shadow, ShadowLocals, SkyboxVertex,
TerrainVertex,
}, },
scene::{ scene::{
camera::{self, Camera, CameraMode}, camera::{self, Camera, CameraMode},
figure::{ figure::{
load_mesh, FigureColLights, FigureModelCache, FigureModelEntry, FigureState, load_mesh, FigureAtlas, FigureModelCache, FigureModelEntry, FigureState,
FigureUpdateCommonParameters, FigureUpdateCommonParameters,
}, },
}, },
@ -46,7 +47,7 @@ impl ReadVol for VoidVol {
} }
fn generate_mesh( fn generate_mesh(
greedy: &mut GreedyMesh<'_>, greedy: &mut GreedyMesh<'_, FigureSpriteAtlasData>,
mesh: &mut Mesh<TerrainVertex>, mesh: &mut Mesh<TerrainVertex>,
segment: Segment, segment: Segment,
offset: Vec3<f32>, offset: Vec3<f32>,
@ -70,7 +71,7 @@ pub struct Scene {
lod: LodData, lod: LodData,
map_bounds: Vec2<f32>, map_bounds: Vec2<f32>,
col_lights: FigureColLights, figure_atlas: FigureAtlas,
backdrop: Option<(FigureModelEntry<1>, FigureState<FixtureSkeleton>)>, backdrop: Option<(FigureModelEntry<1>, FigureState<FixtureSkeleton>)>,
figure_model_cache: FigureModelCache, figure_model_cache: FigureModelCache,
figure_state: Option<FigureState<CharacterSkeleton>>, figure_state: Option<FigureState<CharacterSkeleton>>,
@ -108,7 +109,7 @@ impl Scene {
camera.set_distance(3.4); camera.set_distance(3.4);
camera.set_orientation(Vec3::new(start_angle, 0.0, 0.0)); camera.set_orientation(Vec3::new(start_angle, 0.0, 0.0));
let mut col_lights = FigureColLights::new(renderer); let mut figure_atlas = FigureAtlas::new(renderer);
let data = GlobalModel { let data = GlobalModel {
globals: renderer.create_consts(&[Globals::default()]), globals: renderer.create_consts(&[Globals::default()]),
@ -147,9 +148,14 @@ impl Scene {
// total size is bounded by 2^24 * 3 * 1.5 which is bounded by // total size is bounded by 2^24 * 3 * 1.5 which is bounded by
// 2^27, which fits in a u32. // 2^27, which fits in a u32.
let range = 0..opaque_mesh.vertices().len() as u32; let range = 0..opaque_mesh.vertices().len() as u32;
let model = let (atlas_texture_data, atlas_size) = greedy.finalize();
col_lights let model = figure_atlas.create_figure(
.create_figure(renderer, greedy.finalize(), (opaque_mesh, bounds), [range]); renderer,
atlas_texture_data,
atlas_size,
(opaque_mesh, bounds),
[range],
);
let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; let mut buf = [Default::default(); anim::MAX_BONE_COUNT];
let common_params = FigureUpdateCommonParameters { let common_params = FigureUpdateCommonParameters {
entity: None, entity: None,
@ -182,7 +188,7 @@ impl Scene {
); );
(model, state) (model, state)
}), }),
col_lights, figure_atlas,
camera, camera,
@ -283,7 +289,7 @@ impl Scene {
)]); )]);
self.figure_model_cache self.figure_model_cache
.clean(&mut self.col_lights, scene_data.tick); .clean(&mut self.figure_atlas, scene_data.tick);
let item_info = |equip_slot| { let item_info = |equip_slot| {
inventory inventory
@ -327,7 +333,7 @@ impl Scene {
.figure_model_cache .figure_model_cache
.get_or_create_model( .get_or_create_model(
renderer, renderer,
&mut self.col_lights, &mut self.figure_atlas,
body, body,
inventory, inventory,
(), (),
@ -376,7 +382,7 @@ impl Scene {
let mut figure_drawer = drawer.draw_figures(); let mut figure_drawer = drawer.draw_figures();
if let Some(body) = body { if let Some(body) = body {
let model = &self.figure_model_cache.get_model( let model = &self.figure_model_cache.get_model(
&self.col_lights, &self.figure_atlas,
body, body,
inventory, inventory,
tick, tick,
@ -390,7 +396,7 @@ impl Scene {
figure_drawer.draw( figure_drawer.draw(
lod, lod,
figure_state.bound(), figure_state.bound(),
self.col_lights.texture(ModelEntryRef::Figure(model)), self.figure_atlas.texture(ModelEntryRef::Figure(model)),
); );
} }
} }
@ -401,7 +407,7 @@ impl Scene {
figure_drawer.draw( figure_drawer.draw(
lod, lod,
state.bound(), state.bound(),
self.col_lights.texture(ModelEntryRef::Figure(model)), self.figure_atlas.texture(ModelEntryRef::Figure(model)),
); );
} }
} }

View File

@ -9,11 +9,11 @@ use crate::{
terrain::{generate_mesh, SUNLIGHT, SUNLIGHT_INV}, terrain::{generate_mesh, SUNLIGHT, SUNLIGHT_INV},
}, },
render::{ render::{
pipelines::{self, ColLights}, pipelines::{self, AtlasData, AtlasTextures},
AltIndices, ColLightInfo, CullingMode, FirstPassDrawer, FluidVertex, GlobalModel, AltIndices, CullingMode, FigureSpriteAtlasData, FirstPassDrawer, FluidVertex, GlobalModel,
Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteDrawer, Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteDrawer,
SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainLocals, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainAtlasData,
TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE,
}, },
}; };
@ -80,14 +80,14 @@ pub struct TerrainChunkData {
fluid_model: Option<Model<FluidVertex>>, fluid_model: Option<Model<FluidVertex>>,
/// If this is `None`, this texture is not allocated in the current atlas, /// If this is `None`, this texture is not allocated in the current atlas,
/// and therefore there is no need to free its allocation. /// and therefore there is no need to free its allocation.
col_lights_alloc: Option<guillotiere::AllocId>, atlas_alloc: Option<guillotiere::AllocId>,
/// The actual backing texture for this chunk. Use this for rendering /// The actual backing texture for this chunk. Use this for rendering
/// purposes. The texture is reference-counted, so it will be /// purposes. The texture is reference-counted, so it will be
/// automatically freed when no chunks are left that need it (though /// automatically freed when no chunks are left that need it (though
/// shadow chunks will still keep it alive; we could deal with this by /// shadow chunks will still keep it alive; we could deal with this by
/// making this an `Option`, but it probably isn't worth it since they /// making this an `Option`, but it probably isn't worth it since they
/// shouldn't be that much more nonlocal than regular chunks). /// shouldn't be that much more nonlocal than regular chunks).
col_lights: Arc<ColLights<pipelines::terrain::Locals>>, atlas_textures: Arc<AtlasTextures<pipelines::terrain::Locals, TerrainAtlasData>>,
light_map: LightMapFn, light_map: LightMapFn,
glow_map: LightMapFn, glow_map: LightMapFn,
sprite_instances: [(Instances<SpriteInstance>, AltIndices); SPRITE_LOD_LEVELS], sprite_instances: [(Instances<SpriteInstance>, AltIndices); SPRITE_LOD_LEVELS],
@ -133,7 +133,8 @@ pub struct MeshWorkerResponseMesh {
sun_occluder_z_bounds: (f32, f32), sun_occluder_z_bounds: (f32, f32),
opaque_mesh: Mesh<TerrainVertex>, opaque_mesh: Mesh<TerrainVertex>,
fluid_mesh: Mesh<FluidVertex>, fluid_mesh: Mesh<FluidVertex>,
col_lights_info: ColLightInfo, atlas_texture_data: TerrainAtlasData,
atlas_size: Vec2<u16>,
light_map: LightMapFn, light_map: LightMapFn,
glow_map: LightMapFn, glow_map: LightMapFn,
alt_indices: AltIndices, alt_indices: AltIndices,
@ -343,7 +344,15 @@ fn mesh_worker(
opaque_mesh, opaque_mesh,
fluid_mesh, fluid_mesh,
_shadow_mesh, _shadow_mesh,
(bounds, col_lights_info, light_map, glow_map, alt_indices, sun_occluder_z_bounds), (
bounds,
atlas_texture_data,
atlas_size,
light_map,
glow_map,
alt_indices,
sun_occluder_z_bounds,
),
) = generate_mesh( ) = generate_mesh(
&volume, &volume,
( (
@ -358,7 +367,8 @@ fn mesh_worker(
sun_occluder_z_bounds, sun_occluder_z_bounds,
opaque_mesh, opaque_mesh,
fluid_mesh, fluid_mesh,
col_lights_info, atlas_texture_data,
atlas_size,
light_map, light_map,
glow_map, glow_map,
alt_indices, alt_indices,
@ -496,12 +506,12 @@ pub struct Terrain<V: RectRasterableVol = TerrainChunk> {
// Maps sprite kind + variant to data detailing how to render it // Maps sprite kind + variant to data detailing how to render it
pub sprite_data: Arc<HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>>, pub sprite_data: Arc<HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>>,
pub sprite_globals: SpriteGlobalsBindGroup, pub sprite_globals: SpriteGlobalsBindGroup,
pub sprite_col_lights: Arc<ColLights<pipelines::sprite::Locals>>, pub sprite_atlas_textures: Arc<AtlasTextures<pipelines::sprite::Locals, FigureSpriteAtlasData>>,
/// As stated previously, this is always the very latest texture into which /// As stated previously, this is always the very latest texture into which
/// we allocate. Code cannot assume that this is the assigned texture /// we allocate. Code cannot assume that this is the assigned texture
/// for any particular chunk; look at the `texture` field in /// for any particular chunk; look at the `texture` field in
/// `TerrainChunkData` for that. /// `TerrainChunkData` for that.
col_lights: Arc<ColLights<pipelines::terrain::Locals>>, atlas_textures: Arc<AtlasTextures<pipelines::terrain::Locals, TerrainAtlasData>>,
phantom: PhantomData<V>, phantom: PhantomData<V>,
} }
@ -515,7 +525,7 @@ pub struct SpriteRenderContext {
sprite_config: Arc<SpriteSpec>, sprite_config: Arc<SpriteSpec>,
// Maps sprite kind + variant to data detailing how to render it // Maps sprite kind + variant to data detailing how to render it
sprite_data: Arc<HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>>, sprite_data: Arc<HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>>,
sprite_col_lights: Arc<ColLights<pipelines::sprite::Locals>>, sprite_atlas_textures: Arc<AtlasTextures<pipelines::sprite::Locals, FigureSpriteAtlasData>>,
sprite_verts_buffer: Arc<SpriteVerts>, sprite_verts_buffer: Arc<SpriteVerts>,
} }
@ -528,7 +538,8 @@ impl SpriteRenderContext {
struct SpriteWorkerResponse { struct SpriteWorkerResponse {
sprite_config: Arc<SpriteSpec>, sprite_config: Arc<SpriteSpec>,
sprite_data: HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>, sprite_data: HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
sprite_col_lights: ColLightInfo, sprite_atlas_texture_data: FigureSpriteAtlasData,
sprite_atlas_size: Vec2<u16>,
sprite_mesh: Mesh<SpriteVertex>, sprite_mesh: Mesh<SpriteVertex>,
} }
@ -539,7 +550,7 @@ impl SpriteRenderContext {
Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned(); Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned();
let max_size = Vec2::from(u16::try_from(max_texture_size).unwrap_or(u16::MAX)); let max_size = Vec2::from(u16::try_from(max_texture_size).unwrap_or(u16::MAX));
let mut greedy = GreedyMesh::<SpriteAtlasAllocator>::new( let mut greedy = GreedyMesh::<FigureSpriteAtlasData, SpriteAtlasAllocator>::new(
max_size, max_size,
crate::mesh::greedy::sprite_config(), crate::mesh::greedy::sprite_config(),
); );
@ -584,7 +595,10 @@ impl SpriteRenderContext {
scale scale
} }
}); });
move |greedy: &mut GreedyMesh<SpriteAtlasAllocator>, move |greedy: &mut GreedyMesh<
FigureSpriteAtlasData,
SpriteAtlasAllocator,
>,
sprite_mesh: &mut Mesh<SpriteVertex>| { sprite_mesh: &mut Mesh<SpriteVertex>| {
prof_span!("mesh sprite"); prof_span!("mesh sprite");
let lod_sprite_data = scaled.map(|lod_scale_orig| { let lod_sprite_data = scaled.map(|lod_scale_orig| {
@ -635,7 +649,7 @@ impl SpriteRenderContext {
.map(|f| f(&mut greedy, &mut sprite_mesh)) .map(|f| f(&mut greedy, &mut sprite_mesh))
.collect(); .collect();
let sprite_col_lights = { let (sprite_atlas_texture_data, sprite_atlas_size) = {
prof_span!("finalize"); prof_span!("finalize");
greedy.finalize() greedy.finalize()
}; };
@ -643,7 +657,8 @@ impl SpriteRenderContext {
SpriteWorkerResponse { SpriteWorkerResponse {
sprite_config, sprite_config,
sprite_data, sprite_data,
sprite_col_lights, sprite_atlas_texture_data,
sprite_atlas_size,
sprite_mesh, sprite_mesh,
} }
}); });
@ -658,7 +673,8 @@ impl SpriteRenderContext {
let SpriteWorkerResponse { let SpriteWorkerResponse {
sprite_config, sprite_config,
sprite_data, sprite_data,
sprite_col_lights, sprite_atlas_texture_data,
sprite_atlas_size,
sprite_mesh, sprite_mesh,
} = join_handle } = join_handle
.take() .take()
@ -669,9 +685,9 @@ impl SpriteRenderContext {
.join() .join()
.unwrap(); .unwrap();
let sprite_col_lights = let [sprite_col_lights] =
pipelines::shadow::create_col_lights(renderer, &sprite_col_lights); sprite_atlas_texture_data.create_textures(renderer, sprite_atlas_size);
let sprite_col_lights = renderer.sprite_bind_col_light(sprite_col_lights); let sprite_atlas_textures = renderer.sprite_bind_atlas_textures(sprite_col_lights);
// Write sprite model to a 1D texture // Write sprite model to a 1D texture
let sprite_verts_buffer = renderer.create_sprite_verts(sprite_mesh); let sprite_verts_buffer = renderer.create_sprite_verts(sprite_mesh);
@ -680,7 +696,7 @@ impl SpriteRenderContext {
// TODO: these are all Arcs, would it makes sense to factor out the Arc? // TODO: these are all Arcs, would it makes sense to factor out the Arc?
sprite_config: Arc::clone(&sprite_config), sprite_config: Arc::clone(&sprite_config),
sprite_data: Arc::new(sprite_data), sprite_data: Arc::new(sprite_data),
sprite_col_lights: Arc::new(sprite_col_lights), sprite_atlas_textures: Arc::new(sprite_atlas_textures),
sprite_verts_buffer: Arc::new(sprite_verts_buffer), sprite_verts_buffer: Arc::new(sprite_verts_buffer),
} }
}; };
@ -699,7 +715,7 @@ impl<V: RectRasterableVol> Terrain<V> {
// with worker threads that are meshing chunks. // with worker threads that are meshing chunks.
let (send, recv) = channel::unbounded(); let (send, recv) = channel::unbounded();
let (atlas, col_lights) = let (atlas, atlas_textures) =
Self::make_atlas(renderer).expect("Failed to create atlas texture"); Self::make_atlas(renderer).expect("Failed to create atlas texture");
Self { Self {
@ -713,20 +729,26 @@ impl<V: RectRasterableVol> Terrain<V> {
mesh_todos_active: Arc::new(AtomicU64::new(0)), mesh_todos_active: Arc::new(AtomicU64::new(0)),
mesh_recv_overflow: 0.0, mesh_recv_overflow: 0.0,
sprite_data: sprite_render_context.sprite_data, sprite_data: sprite_render_context.sprite_data,
sprite_col_lights: sprite_render_context.sprite_col_lights, sprite_atlas_textures: sprite_render_context.sprite_atlas_textures,
sprite_globals: renderer.bind_sprite_globals( sprite_globals: renderer.bind_sprite_globals(
global_model, global_model,
lod_data, lod_data,
&sprite_render_context.sprite_verts_buffer, &sprite_render_context.sprite_verts_buffer,
), ),
col_lights: Arc::new(col_lights), atlas_textures: Arc::new(atlas_textures),
phantom: PhantomData, phantom: PhantomData,
} }
} }
fn make_atlas( fn make_atlas(
renderer: &mut Renderer, renderer: &mut Renderer,
) -> Result<(AtlasAllocator, ColLights<pipelines::terrain::Locals>), RenderError> { ) -> Result<
(
AtlasAllocator,
AtlasTextures<pipelines::terrain::Locals, TerrainAtlasData>,
),
RenderError,
> {
span!(_guard, "make_atlas", "Terrain::make_atlas"); span!(_guard, "make_atlas", "Terrain::make_atlas");
let max_texture_size = renderer.max_texture_size(); let max_texture_size = renderer.max_texture_size();
let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32); let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
@ -736,50 +758,56 @@ impl<V: RectRasterableVol> Terrain<V> {
large_size_threshold: 1024, large_size_threshold: 1024,
..guillotiere::AllocatorOptions::default() ..guillotiere::AllocatorOptions::default()
}); });
let texture = renderer.create_texture_raw( let [col_lights, kinds] = [
&wgpu::TextureDescriptor { wgpu::TextureFormat::Rgba8Unorm,
label: Some("Atlas texture"), wgpu::TextureFormat::R8Unorm,
size: wgpu::Extent3d { ]
width: max_texture_size, .map(|fmt| {
height: max_texture_size, renderer.create_texture_raw(
depth_or_array_layers: 1, &wgpu::TextureDescriptor {
label: Some("Color & lights atlas texture"),
size: wgpu::Extent3d {
width: max_texture_size,
height: max_texture_size,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: fmt,
usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED,
}, },
mip_level_count: 1, &wgpu::TextureViewDescriptor {
sample_count: 1, label: Some("Color & lights atlas texture view"),
dimension: wgpu::TextureDimension::D2, format: Some(fmt),
format: wgpu::TextureFormat::Rgba8Unorm, dimension: Some(wgpu::TextureViewDimension::D2),
usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED, aspect: wgpu::TextureAspect::All,
}, base_mip_level: 0,
&wgpu::TextureViewDescriptor { mip_level_count: None,
label: Some("Atlas texture view"), base_array_layer: 0,
format: Some(wgpu::TextureFormat::Rgba8Unorm), array_layer_count: None,
dimension: Some(wgpu::TextureViewDimension::D2), },
aspect: wgpu::TextureAspect::All, &wgpu::SamplerDescriptor {
base_mip_level: 0, label: Some("Color & lights atlas texture sampler"),
mip_level_count: None, address_mode_u: wgpu::AddressMode::ClampToEdge,
base_array_layer: 0, address_mode_v: wgpu::AddressMode::ClampToEdge,
array_layer_count: None, address_mode_w: wgpu::AddressMode::ClampToEdge,
}, mag_filter: wgpu::FilterMode::Linear,
&wgpu::SamplerDescriptor { min_filter: wgpu::FilterMode::Linear,
label: Some("Atlas sampler"), mipmap_filter: wgpu::FilterMode::Nearest,
address_mode_u: wgpu::AddressMode::ClampToEdge, ..Default::default()
address_mode_v: wgpu::AddressMode::ClampToEdge, },
address_mode_w: wgpu::AddressMode::ClampToEdge, )
mag_filter: wgpu::FilterMode::Linear, });
min_filter: wgpu::FilterMode::Linear, let textures = renderer.terrain_bind_atlas_textures(col_lights, kinds);
mipmap_filter: wgpu::FilterMode::Nearest, Ok((atlas, textures))
..Default::default()
},
);
let col_light = renderer.terrain_bind_col_light(texture);
Ok((atlas, col_light))
} }
fn remove_chunk_meta(&mut self, _pos: Vec2<i32>, chunk: &TerrainChunkData) { fn remove_chunk_meta(&mut self, _pos: Vec2<i32>, chunk: &TerrainChunkData) {
// No need to free the allocation if the chunk is not allocated in the current // No need to free the allocation if the chunk is not allocated in the current
// atlas, since we don't bother tracking it at that point. // atlas, since we don't bother tracking it at that point.
if let Some(col_lights) = chunk.col_lights_alloc { if let Some(atlas_alloc) = chunk.atlas_alloc {
self.atlas.deallocate(col_lights); self.atlas.deallocate(atlas_alloc);
} }
/* let (zmin, zmax) = chunk.z_bounds; /* let (zmin, zmax) = chunk.z_bounds;
self.z_index_up.remove(Vec3::from(zmin, pos.x, pos.y)); self.z_index_up.remove(Vec3::from(zmin, pos.x, pos.y));
@ -1250,16 +1278,17 @@ impl<V: RectRasterableVol> Terrain<V> {
.map(|chunk| chunk.load_time) .map(|chunk| chunk.load_time)
.unwrap_or(current_time as f32); .unwrap_or(current_time as f32);
// TODO: Allocate new atlas on allocation failure. // TODO: Allocate new atlas on allocation failure.
let (tex, tex_size) = mesh.col_lights_info;
let atlas = &mut self.atlas; let atlas = &mut self.atlas;
let chunks = &mut self.chunks; let chunks = &mut self.chunks;
let col_lights = &mut self.col_lights; let atlas_textures = &mut self.atlas_textures;
let alloc_size = let alloc_size = guillotiere::Size::new(
guillotiere::Size::new(i32::from(tex_size.x), i32::from(tex_size.y)); i32::from(mesh.atlas_size.x),
i32::from(mesh.atlas_size.y),
);
let allocation = atlas.allocate(alloc_size).unwrap_or_else(|| { let allocation = atlas.allocate(alloc_size).unwrap_or_else(|| {
// Atlas allocation failure: try allocating a new texture and atlas. // Atlas allocation failure: try allocating a new texture and atlas.
let (new_atlas, new_col_lights) = let (new_atlas, new_atlas_textures) =
Self::make_atlas(renderer).expect("Failed to create atlas texture"); Self::make_atlas(renderer).expect("Failed to create atlas texture");
// We reset the atlas and clear allocations from existing chunks, // We reset the atlas and clear allocations from existing chunks,
@ -1271,10 +1300,10 @@ impl<V: RectRasterableVol> Terrain<V> {
// TODO: Consider attempting defragmentation first rather than just // TODO: Consider attempting defragmentation first rather than just
// always moving everything into the new chunk. // always moving everything into the new chunk.
chunks.iter_mut().for_each(|(_, chunk)| { chunks.iter_mut().for_each(|(_, chunk)| {
chunk.col_lights_alloc = None; chunk.atlas_alloc = None;
}); });
*atlas = new_atlas; *atlas = new_atlas;
*col_lights = Arc::new(new_col_lights); *atlas_textures = Arc::new(new_atlas_textures);
atlas atlas
.allocate(alloc_size) .allocate(alloc_size)
@ -1286,19 +1315,27 @@ impl<V: RectRasterableVol> Terrain<V> {
allocation.rectangle.min.x as u32, allocation.rectangle.min.x as u32,
allocation.rectangle.min.y as u32, allocation.rectangle.min.y as u32,
); );
// Update col_lights texture
renderer.update_texture( renderer.update_texture(
&col_lights.texture, &atlas_textures.textures[0],
atlas_offs.into_array(), atlas_offs.into_array(),
tex_size.map(u32::from).into_array(), mesh.atlas_size.as_().into_array(),
&tex, &mesh.atlas_texture_data.col_lights,
);
// Update kinds texture
renderer.update_texture(
&atlas_textures.textures[1],
atlas_offs.into_array(),
mesh.atlas_size.as_().into_array(),
&mesh.atlas_texture_data.kinds,
); );
self.insert_chunk(response.pos, TerrainChunkData { self.insert_chunk(response.pos, TerrainChunkData {
load_time, load_time,
opaque_model: renderer.create_model(&mesh.opaque_mesh), opaque_model: renderer.create_model(&mesh.opaque_mesh),
fluid_model: renderer.create_model(&mesh.fluid_mesh), fluid_model: renderer.create_model(&mesh.fluid_mesh),
col_lights_alloc: Some(allocation.id), atlas_alloc: Some(allocation.id),
col_lights: Arc::clone(&self.col_lights), atlas_textures: Arc::clone(&self.atlas_textures),
light_map: mesh.light_map, light_map: mesh.light_map,
glow_map: mesh.glow_map, glow_map: mesh.glow_map,
sprite_instances, sprite_instances,
@ -1705,19 +1742,19 @@ impl<V: RectRasterableVol> Terrain<V> {
Some(( Some((
rpos, rpos,
chunk.opaque_model.as_ref()?, chunk.opaque_model.as_ref()?,
&chunk.col_lights, &chunk.atlas_textures,
&chunk.locals, &chunk.locals,
&chunk.alt_indices, &chunk.alt_indices,
)) ))
}) })
.for_each(|(rpos, model, col_lights, locals, alt_indices)| { .for_each(|(rpos, model, atlas_textures, locals, alt_indices)| {
// Always draw all of close chunks to avoid terrain 'popping' // Always draw all of close chunks to avoid terrain 'popping'
let culling_mode = if rpos.magnitude_squared() < NEVER_CULL_DIST.pow(2) { let culling_mode = if rpos.magnitude_squared() < NEVER_CULL_DIST.pow(2) {
CullingMode::None CullingMode::None
} else { } else {
culling_mode culling_mode
}; };
drawer.draw(model, col_lights, locals, alt_indices, culling_mode) drawer.draw(model, atlas_textures, locals, alt_indices, culling_mode)
}); });
} }