More fixes

This commit is contained in:
Capucho 2020-09-26 16:43:59 +01:00 committed by Imbris
parent 8c6e43572f
commit 8c3995298b
33 changed files with 1037 additions and 1153 deletions

1
Cargo.lock generated
View File

@ -6052,6 +6052,7 @@ dependencies = [
name = "veloren-voxygen-anim"
version = "0.9.0"
dependencies = [
"bytemuck",
"find_folder",
"lazy_static",
"libloading 0.7.0",

View File

@ -20,3 +20,4 @@ libloading = {version = "0.7", optional = true}
notify = {version = "5.0.0-pre.2", optional = true}
tracing = {version = "0.1", optional = true}
vek = {version = "=0.14.1", features = ["serde"]}
bytemuck = { version="1.4", features=["derive"] }

View File

@ -74,16 +74,19 @@ pub use dyn_lib::init;
use std::ffi::CStr;
use self::vek::*;
use bytemuck::{Pod, Zeroable};
type MatRaw = [[f32; 4]; 4];
pub type FigureBoneData = (MatRaw, MatRaw);
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
pub struct FigureBoneData(pub MatRaw, pub MatRaw);
pub const MAX_BONE_COUNT: usize = 16;
fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
let normal = mat.map_cols(Vec4::normalized);
(mat.into_col_arrays(), normal.into_col_arrays())
FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
}
pub type Bone = Transform<f32, f32, f32>;

View File

@ -1,9 +1,7 @@
use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline};
use crate::render::{mesh::Quad, ColLightInfo, TerrainVertex, Vertex};
use common_base::span;
use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type TodoRect = (
Vec3<i32>,
Vec2<Vec3<u16>>,
@ -84,7 +82,7 @@ pub type SuspendedMesh<'a> = dyn for<'r> FnOnce(&'r mut ColLightInfo) + 'a;
/// For an explanation of why we want this, see `SuspendedMesh`.
pub struct GreedyMesh<'a> {
atlas: guillotiere::SimpleAtlasAllocator,
col_lights_size: Vec2<u16>,
col_lights_size: Vec2<u32>,
max_size: guillotiere::Size,
suspended: Vec<Box<SuspendedMesh<'a>>>,
}
@ -123,7 +121,7 @@ impl<'a> GreedyMesh<'a> {
small_size_threshold,
large_size_threshold,
});
let col_lights_size = Vec2::new(1u16, 1u16);
let col_lights_size = Vec2::new(1, 1);
Self {
atlas,
col_lights_size,
@ -178,7 +176,7 @@ impl<'a> GreedyMesh<'a> {
let cur_size = self.col_lights_size;
let col_lights = vec![
TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254));
usize::from(cur_size.x) * usize::from(cur_size.y)
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| {
@ -192,7 +190,7 @@ impl<'a> GreedyMesh<'a> {
fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FG, FO, FS, FP, FT>(
atlas: &mut guillotiere::SimpleAtlasAllocator,
col_lights_size: &mut Vec2<u16>,
col_lights_size: &mut Vec2<u32>,
max_size: guillotiere::Size,
GreedyConfig {
mut data,
@ -430,7 +428,7 @@ fn add_to_atlas(
norm: Vec3<i16>,
faces_forward: bool,
max_size: guillotiere::Size,
cur_size: &mut Vec2<u16>,
cur_size: &mut Vec2<u32>,
) -> guillotiere::Rectangle {
// TODO: Check this conversion.
let atlas_rect;
@ -475,8 +473,8 @@ fn add_to_atlas(
// a u16 and we never grew the atlas, meaning all valid coordinates within the
// atlas also fit into a u16.
*cur_size = Vec2::new(
cur_size.x.max(atlas_rect.max.x as u16),
cur_size.y.max(atlas_rect.max.y as u16),
cur_size.x.max(atlas_rect.max.x as u32),
cur_size.y.max(atlas_rect.max.y as u32),
);
// NOTE: pos can be converted safely from usize to i32 because all legal block
@ -507,7 +505,7 @@ fn draw_col_lights<D>(
mut get_light: 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 make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType,
mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> [u8; 4],
) {
todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| {
// NOTE: Conversions are safe because width, height, and offset must be
@ -520,7 +518,7 @@ fn draw_col_lights<D>(
let uv = uv.map(|e| e.map(i32::from));
let pos = pos + draw_delta;
(0..height).for_each(|v| {
let start = usize::from(cur_size.x) * usize::from(top + v) + usize::from(left);
let start = cur_size.x as usize * usize::from(top + v) + usize::from(left);
(0..width)
.zip(&mut col_lights[start..start + usize::from(width)])
.for_each(|(u, col_light)| {
@ -622,14 +620,14 @@ fn create_quad_greedy<M>(
push_quad(atlas_pos, dim, origin, draw_dim, norm, meta);
}
pub fn create_quad<O: render::Pipeline, M>(
pub fn create_quad<O: Vertex, M>(
atlas_pos: Vec2<u16>,
dim: Vec2<Vec2<u16>>,
origin: Vec3<f32>,
draw_dim: Vec2<Vec3<f32>>,
norm: Vec3<f32>,
meta: &M,
create_vertex: impl Fn(Vec2<u16>, Vec3<f32>, Vec3<f32>, &M) -> O::Vertex,
create_vertex: impl Fn(Vec2<u16>, Vec3<f32>, Vec3<f32>, &M) -> O,
) -> Quad<O> {
Quad::new(
create_vertex(atlas_pos, origin, norm, meta),

View File

@ -2,25 +2,6 @@ pub mod greedy;
pub mod segment;
pub mod terrain;
use crate::render::{self, Mesh};
use crate::render::Mesh;
pub type MeshGen<P, T, M> = (
Mesh<<M as Meshable<P, T>>::Pipeline>,
Mesh<<M as Meshable<P, T>>::TranslucentPipeline>,
Mesh<<M as Meshable<P, T>>::ShadowPipeline>,
<M as Meshable<P, T>>::Result,
);
/// FIXME: Remove this whole trait at some point. This "abstraction" is never
/// abstracted over, and is organized completely differently from how we
/// actually mesh things nowadays.
pub trait Meshable<P: render::Pipeline, T> {
type Pipeline: render::Pipeline;
type TranslucentPipeline: render::Pipeline;
type ShadowPipeline: render::Pipeline;
type Supplement;
type Result;
// Generate meshes - one opaque, one translucent, one shadow
fn generate_mesh(self, supp: Self::Supplement) -> MeshGen<P, T, Self>;
}
pub type MeshGen<V, T, S, R> = (Mesh<V>, Mesh<T>, Mesh<S>, R);

View File

@ -1,12 +1,9 @@
use crate::{
mesh::{
greedy::{self, GreedyConfig, GreedyMesh},
MeshGen, Meshable,
},
render::{
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
TerrainPipeline,
MeshGen,
},
render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex},
scene::math,
};
use common::{
@ -16,353 +13,315 @@ use common::{
use core::convert::TryFrom;
use vek::*;
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type ParticleVertex = <ParticlePipeline as render::Pipeline>::Vertex;
impl<'a: 'b, 'b, V: 'a> Meshable<FigurePipeline, &'b mut GreedyMesh<'a>> for V
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = TerrainPipeline;
type Result = math::Aabb<f32>;
type ShadowPipeline = ShadowPipeline;
/// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
/// point).
type Supplement = (
// /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
// /// point).
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
// TODO: this function name...
pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>(
vol: V,
(greedy, opaque_mesh, offs, scale, bone_idx): (
&'b mut GreedyMesh<'a>,
&'b mut Mesh<Self::Pipeline>,
&'b mut Mesh<TerrainVertex>,
Vec3<f32>,
Vec3<f32>,
u8,
);
type TranslucentPipeline = FigurePipeline;
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
(greedy, opaque_mesh, offs, scale, bone_idx): Self::Supplement,
) -> MeshGen<FigurePipeline, &'b mut GreedyMesh<'a>, Self> {
assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 15);
let lower_bound = self.lower_bound();
let upper_bound = self.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// NOTE: Figure sizes should be no more than 512 along each axis.
let greedy_size = upper_bound - lower_bound + 1;
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_opacity =
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |atlas_pos, pos, norm| {
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
};
greedy.push(GreedyConfig {
data: self,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: |vol: &mut V, pos, light, _| {
let cell = vol.get(pos).ok();
let (glowy, shiny) = cell
.map(|c| (c.is_glowy(), c.is_shiny()))
.unwrap_or_default();
let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero());
TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
},
});
let bounds = math::Aabb {
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
}
.made_valid();
(Mesh::new(), Mesh::new(), Mesh::new(), bounds)
}
}
impl<'a: 'b, 'b, V: 'a> Meshable<SpritePipeline, &'b mut GreedyMesh<'a>> for V
),
) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = SpritePipeline;
type Result = ();
type ShadowPipeline = ShadowPipeline;
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool);
type TranslucentPipeline = SpritePipeline;
assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 15);
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
(greedy, opaque_mesh, vertical_stripes): Self::Supplement,
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> {
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
let lower_bound = vol.lower_bound();
let upper_bound = vol.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// NOTE: Figure sizes should be no more than 512 along each axis.
let greedy_size = upper_bound - lower_bound + 1;
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let lower_bound = self.lower_bound();
let upper_bound = self.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// Lower bound coordinates must fit in an i16 (which means upper bound
// coordinates fit as integers in a f23).
assert!(
i16::try_from(lower_bound.x).is_ok()
&& i16::try_from(lower_bound.y).is_ok()
&& i16::try_from(lower_bound.z).is_ok(),
"Sprite offsets should fit in i16",
);
let greedy_size = upper_bound - lower_bound + 1;
// TODO: Should this be 16, 16, 64?
assert!(
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
greedy_size - 1
);
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |atlas_pos, pos, norm| {
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
};
let (flat, flat_get) = {
let (w, h, d) = (greedy_size + 2).into_tuple();
let flat = {
let vol = self;
greedy.push(GreedyConfig {
data: vol,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: |vol: &mut V, pos, light, _| {
let cell = vol.get(pos).ok();
let (glowy, shiny) = cell
.map(|c| (c.is_glowy(), c.is_shiny()))
.unwrap_or_default();
let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero());
TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
},
});
let bounds = math::Aabb {
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
}
.made_valid();
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
let mut i = 0;
for x in -1..greedy_size.x + 1 {
for y in -1..greedy_size.y + 1 {
for z in -1..greedy_size.z + 1 {
let wpos = lower_bound + Vec3::new(x, y, z);
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
flat[i] = block;
i += 1;
}
(Mesh::new(), Mesh::new(), Mesh::new(), bounds)
}
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>(
vol: V,
(greedy, opaque_mesh, vertical_stripes): (
&'b mut GreedyMesh<'a>,
&'b mut Mesh<SpriteVertex>,
bool,
),
) -> MeshGen<SpriteVertex, SpriteVertex, TerrainVertex, ()>
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
{
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
let lower_bound = vol.lower_bound();
let upper_bound = vol.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// Lower bound coordinates must fit in an i16 (which means upper bound
// coordinates fit as integers in a f23).
assert!(
i16::try_from(lower_bound.x).is_ok()
&& i16::try_from(lower_bound.y).is_ok()
&& i16::try_from(lower_bound.z).is_ok(),
"Sprite offsets should fit in i16",
);
let greedy_size = upper_bound - lower_bound + 1;
// TODO: Should this be 16, 16, 64?
assert!(
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
greedy_size - 1
);
let (flat, flat_get) = {
let (w, h, d) = (greedy_size + 2).into_tuple();
let flat = {
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
let mut i = 0;
for x in -1..greedy_size.x + 1 {
for y in -1..greedy_size.y + 1 {
for z in -1..greedy_size.z + 1 {
let wpos = lower_bound + Vec3::new(x, y, z);
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
flat[i] = block;
i += 1;
}
}
flat
};
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
.get((x * h * d + y * d + z) as usize)
.copied()
{
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
};
(flat, flat_get)
};
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = Vec3::new(1, 1, 1);
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
if flat_get(flat, pos).is_empty() {
1.0
} else {
0.0
}
};
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_color = move |flat: &mut _, pos: Vec3<i32>| {
flat_get(flat, pos).get_color().unwrap_or(Rgb::zero())
};
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
};
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
// position, pos + mesh_delta, is guaranteed to fit in an f32).
let mesh_delta = lower_bound.as_::<f32>();
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
flat
};
greedy.push(GreedyConfig {
data: flat,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
make_face_texel: move |flat: &mut _, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
},
});
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
.get((x * h * d + y * d + z) as usize)
.copied()
{
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
};
(Mesh::new(), Mesh::new(), Mesh::new(), ())
}
(flat, flat_get)
};
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = Vec3::new(1, 1, 1);
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
if flat_get(flat, pos).is_empty() {
1.0
} else {
0.0
}
};
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_color =
move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).get_color().unwrap_or(Rgb::zero());
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
};
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
// position, pos + mesh_delta, is guaranteed to fit in an f32).
let mesh_delta = lower_bound.as_::<f32>();
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
};
greedy.push(GreedyConfig {
data: flat,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
make_face_texel: move |flat: &mut _, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
},
});
(Mesh::new(), Mesh::new(), Mesh::new(), ())
}
impl<'a: 'b, 'b, V: 'a> Meshable<ParticlePipeline, &'b mut GreedyMesh<'a>> for V
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>(
vol: V,
greedy: &'b mut GreedyMesh<'a>,
) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()>
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = ParticlePipeline;
type Result = ();
type ShadowPipeline = ShadowPipeline;
type Supplement = &'b mut GreedyMesh<'a>;
type TranslucentPipeline = ParticlePipeline;
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
greedy: Self::Supplement,
) -> MeshGen<ParticlePipeline, &'b mut GreedyMesh<'a>, Self> {
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
let lower_bound = vol.lower_bound();
let upper_bound = vol.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
let greedy_size = upper_bound - lower_bound + 1;
assert!(
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
greedy_size - 1
);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let lower_bound = self.lower_bound();
let upper_bound = self.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
let greedy_size = upper_bound - lower_bound + 1;
assert!(
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
greedy_size - 1
);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_color = |vol: &mut V, pos: Vec3<i32>| {
vol.get(pos)
.ok()
.and_then(|vox| vox.get_color())
.unwrap_or(Rgb::zero())
};
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_color = |vol: &mut V, pos: Vec3<i32>| {
vol.get(pos)
.ok()
.and_then(|vox| vox.get_color())
.unwrap_or(Rgb::zero())
};
let get_opacity =
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
let mut opaque_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: vol,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: move |vol: &mut V, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(vol, pos))
},
});
let mut opaque_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: self,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: move |vol: &mut V, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(vol, pos))
},
});
(opaque_mesh, Mesh::new(), Mesh::new(), ())
}
(opaque_mesh, Mesh::new(), Mesh::new(), ())
}
fn should_draw_greedy(

View File

@ -3,9 +3,9 @@
use crate::{
mesh::{
greedy::{self, GreedyConfig, GreedyMesh},
MeshGen, Meshable,
MeshGen,
},
render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline},
render::{ColLightInfo, FluidVertex, Mesh, TerrainVertex},
scene::terrain::BlocksOfInterest,
};
use common::{
@ -19,9 +19,6 @@ use std::{collections::VecDeque, fmt::Debug, sync::Arc};
use tracing::error;
use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
#[derive(Clone, Copy, PartialEq)]
enum FaceKind {
/// Opaque face that is facing something non-opaque; either
@ -227,243 +224,234 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
}
}
impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
Meshable<TerrainPipeline, FluidPipeline> for &'a VolGrid2d<V>
{
type Pipeline = TerrainPipeline;
#[allow(clippy::type_complexity)]
type Result = (
#[allow(clippy::collapsible_if)]
#[allow(clippy::many_single_char_names)]
#[allow(clippy::needless_range_loop)] // TODO: Pending review in #587
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>(
vol: &'a VolGrid2d<V>,
(range, max_texture_size): (Aabb<i32>, Vec2<u16>),
(range, max_texture_size, _boi): (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest),
) -> MeshGen<
TerrainVertex,
FluidVertex,
TerrainVertex,
(
Aabb<f32>,
ColLightInfo,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
),
> {
span!(
_guard,
"generate_mesh",
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
);
type ShadowPipeline = ShadowPipeline;
type Supplement = (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest);
type TranslucentPipeline = FluidPipeline;
#[allow(clippy::collapsible_if)]
#[allow(clippy::many_single_char_names)]
#[allow(clippy::needless_range_loop)] // TODO: Pending review in #587
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
(range, max_texture_size, _boi): Self::Supplement,
) -> MeshGen<TerrainPipeline, FluidPipeline, Self> {
span!(
_guard,
"generate_mesh",
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
);
// Find blocks that should glow
// TODO: Search neighbouring chunks too!
// let glow_blocks = boi.lights
// .iter()
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
// Find blocks that should glow
// TODO: Search neighbouring chunks too!
// let glow_blocks = boi.lights
// .iter()
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
let mut glow_blocks = Vec::new();
let mut glow_blocks = Vec::new();
// TODO: This expensive, use BlocksOfInterest instead
let mut volume = self.cached();
for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST {
for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST {
for z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
volume
.get(wpos)
.ok()
.and_then(|b| b.get_glow())
.map(|glow| glow_blocks.push((wpos, glow)));
}
// TODO: This expensive, use BlocksOfInterest instead
let mut volume = self.cached();
for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST {
for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST {
for z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
volume
.get(wpos)
.ok()
.and_then(|b| b.get_glow())
.map(|glow| glow_blocks.push((wpos, glow)));
}
}
}
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty());
let glow = calc_light(false, 0, range, self, glow_blocks.into_iter());
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty());
let glow = calc_light(false, 0, range, self, glow_blocks.into_iter());
let mut opaque_limits = None::<Limits>;
let mut fluid_limits = None::<Limits>;
let mut air_limits = None::<Limits>;
let flat_get = {
span!(_guard, "copy to flat array");
let (w, h, d) = range.size().into_tuple();
// z can range from -1..range.size().d + 1
let d = d + 2;
let flat = {
let mut volume = self.cached();
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
// TODO: Once we can manage it sensibly, consider using something like
// Option<Block> instead of just assuming air.
let mut flat = vec![AIR; (w * h * d) as usize];
let mut i = 0;
for x in 0..range.size().w {
for y in 0..range.size().h {
for z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
let block = volume
.get(wpos)
.map(|b| *b)
// TODO: Replace with None or some other more reasonable value,
// since it's not clear this will work properly with liquid.
.unwrap_or(AIR);
if block.is_opaque() {
opaque_limits = opaque_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else if block.is_liquid() {
fluid_limits = fluid_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else {
// Assume air
air_limits = air_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
};
flat[i] = block;
i += 1;
}
let mut opaque_limits = None::<Limits>;
let mut fluid_limits = None::<Limits>;
let mut air_limits = None::<Limits>;
let flat_get = {
span!(_guard, "copy to flat array");
let (w, h, d) = range.size().into_tuple();
// z can range from -1..range.size().d + 1
let d = d + 2;
let flat = {
let mut volume = vol.cached();
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
// TODO: Once we can manage it sensibly, consider using something like
// Option<Block> instead of just assuming air.
let mut flat = vec![AIR; (w * h * d) as usize];
let mut i = 0;
for x in 0..range.size().w {
for y in 0..range.size().h {
for z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
let block = volume
.get(wpos)
.map(|b| *b)
// TODO: Replace with None or some other more reasonable value,
// since it's not clear this will work properly with liquid.
.unwrap_or(AIR);
if block.is_opaque() {
opaque_limits = opaque_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else if block.is_liquid() {
fluid_limits = fluid_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else {
// Assume air
air_limits = air_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
};
flat[i] = block;
i += 1;
}
}
flat
};
move |Vec3 { x, y, z }| {
// z can range from -1..range.size().d + 1
let z = z + 1;
match flat.get((x * h * d + y * d + z) as usize).copied() {
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
}
}
flat
};
// Constrain iterated area
let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) {
(Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque),
(Some(air), Some(fluid), None) => air.intersection(fluid),
(Some(air), None, Some(opaque)) => air.intersection(opaque),
(None, Some(fluid), Some(opaque)) => fluid.intersection(opaque),
// No interfaces (Note: if there are multiple fluid types this could change)
(Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None,
(None, None, None) => {
error!("Impossible unless given an input AABB that has a height of zero");
None
},
move |Vec3 { x, y, z }| {
// z can range from -1..range.size().d + 1
let z = z + 1;
match flat.get((x * h * d + y * d + z) as usize).copied() {
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
}
}
.map_or((0, 0), |limits| {
let (start, end) = limits.into_tuple();
let start = start.max(0);
let end = end.min(range.size().d - 1).max(start);
(start, end)
});
};
let max_size =
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
assert!(z_end >= z_start);
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
// + 14). FIXME: Make this function fallible, since the terrain
// information might be dynamically generated which would make this hard
// to enforce.
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a f32.
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
let draw_delta = Vec3::new(1, 1, z_start);
let get_light = |_: &mut (), pos: Vec3<i32>| {
if flat_get(pos).is_opaque() {
0.0
} else {
light(pos + range.min)
}
};
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
let get_color =
|_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque();
let flat_get = |pos| flat_get(pos);
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
should_draw_greedy(pos, delta, flat_get)
};
// NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16.
let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
let create_opaque = |atlas_pos, pos, norm, meta| {
TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta)
};
let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm);
let mut greedy = GreedyMesh::new(max_size);
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: (),
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
FaceKind::Opaque(meta) => {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
FaceKind::Fluid => {
fluid_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
&(),
|atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm),
));
},
},
make_face_texel: |data: &mut (), pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(data, pos))
},
});
let min_bounds = mesh_delta;
let bounds = Aabb {
min: min_bounds,
max: max_bounds + min_bounds,
};
let (col_lights, col_lights_size) = greedy.finalize();
(
opaque_mesh,
fluid_mesh,
Mesh::new(),
(
bounds,
(col_lights, col_lights_size),
Arc::new(light),
Arc::new(glow),
),
)
// Constrain iterated area
let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) {
(Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque),
(Some(air), Some(fluid), None) => air.intersection(fluid),
(Some(air), None, Some(opaque)) => air.intersection(opaque),
(None, Some(fluid), Some(opaque)) => fluid.intersection(opaque),
// No interfaces (Note: if there are multiple fluid types this could change)
(Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None,
(None, None, None) => {
error!("Impossible unless given an input AABB that has a height of zero");
None
},
}
.map_or((0, 0), |limits| {
let (start, end) = limits.into_tuple();
let start = start.max(0);
let end = end.min(range.size().d - 1).max(start);
(start, end)
});
let max_size =
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
assert!(z_end >= z_start);
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
// + 14). FIXME: Make this function fallible, since the terrain
// information might be dynamically generated which would make this hard
// to enforce.
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a f32.
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
let draw_delta = Vec3::new(1, 1, z_start);
let get_light = |_: &mut (), pos: Vec3<i32>| {
if flat_get(pos).is_opaque() {
0.0
} else {
light(pos + range.min)
}
};
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
let get_color = |_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque();
let flat_get = |pos| flat_get(pos);
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
should_draw_greedy(pos, delta, flat_get)
};
// NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16.
let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
let create_opaque =
|atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm);
let mut greedy = GreedyMesh::new(max_size);
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: (),
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
FaceKind::Opaque(meta) => {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
FaceKind::Fluid => {
fluid_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
&(),
|atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm),
));
},
},
make_face_texel: |data: &mut (), pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(data, pos))
},
});
let min_bounds = mesh_delta;
let bounds = Aabb {
min: min_bounds,
max: max_bounds + min_bounds,
};
let (col_lights, col_lights_size) = greedy.finalize();
(
opaque_mesh,
fluid_mesh,
Mesh::new(),
(
bounds,
(col_lights, col_lights_size),
Arc::new(light),
Arc::new(glow),
),
)
}
/// NOTE: Make sure to reflect any changes to how meshing is performanced in

View File

@ -1,7 +1,6 @@
use bytemuck::Pod;
use wgpu::util::DeviceExt;
#[derive(Clone)]
pub struct Buffer<T: Copy + Pod> {
pub buf: wgpu::Buffer,
// bytes
@ -10,7 +9,7 @@ pub struct Buffer<T: Copy + Pod> {
}
impl<T: Copy + Pod> Buffer<T> {
pub fn new(device: &mut wgpu::Device, cap: usize, usage: wgpu::BufferUsage) -> Self {
pub fn new(device: &wgpu::Device, cap: u64, usage: wgpu::BufferUsage) -> Self {
Self {
buf: device.create_buffer(&wgpu::BufferDescriptor {
label: None,
@ -23,8 +22,8 @@ impl<T: Copy + Pod> Buffer<T> {
}
}
pub fn new_with_data(device: &mut wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self {
let contents = data.as_bytes();
pub fn new_with_data(device: &wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self {
let contents = bytemuck::cast_slice(data);
Self {
buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -37,15 +36,9 @@ impl<T: Copy + Pod> Buffer<T> {
}
}
pub fn update(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
offset: usize,
) {
pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
if !vals.is_empty() {
queue.write_buffer(&self.buf, offset, vals.as_bytes())
queue.write_buffer(&self.buf, offset, bytemuck::cast_slice(vals))
}
}

View File

@ -4,29 +4,22 @@ use bytemuck::Pod;
/// A handle to a series of constants sitting on the GPU. This is used to hold
/// information used in the rendering process that does not change throughout a
/// single render pass.
#[derive(Clone)]
pub struct Consts<T: Copy + Pod> {
buf: Buffer<T>,
}
impl<T: Copy + Pod> Consts<T> {
/// Create a new `Const<T>`.
pub fn new(device: &mut wgpu::Device, len: usize) -> Self {
pub fn new(device: &wgpu::Device, len: u64) -> Self {
Self {
buf: Buffer::new(device, len, wgpu::BufferUsage::UNIFORM),
}
}
/// Update the GPU-side value represented by this constant handle.
pub fn update(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
offset: usize,
) {
pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
self.buf.update(device, queue, vals, offset)
}
pub fn buf(&self) -> &wgpu::Buffer { self.buf.buf }
pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf }
}

View File

@ -1,14 +1,13 @@
use super::{buffer::Buffer, RenderError};
use super::buffer::Buffer;
use bytemuck::Pod;
/// Represents a mesh that has been sent to the GPU.
#[derive(Clone)]
pub struct Instances<T: Copy + Pod> {
buf: Buffer<T>,
}
impl<T: Copy + Pod> Instances<T> {
pub fn new(device: &mut wgpu::Device, len: usize) -> Self {
pub fn new(device: &wgpu::Device, len: u64) -> Self {
Self {
buf: Buffer::new(device, len, wgpu::BufferUsage::VERTEX),
}
@ -16,15 +15,9 @@ impl<T: Copy + Pod> Instances<T> {
pub fn count(&self) -> usize { self.buf.count() }
pub fn update(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
offset: usize,
) -> Result<(), RenderError> {
pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
self.buf.update(device, queue, vals, offset)
}
pub fn buf(&self) -> &wgpu::Buffer { self.buf.buf }
pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf }
}

View File

@ -1,4 +1,4 @@
use super::{buffer::Buffer, mesh::Mesh, RenderError, Vertex};
use super::{buffer::Buffer, mesh::Mesh, Vertex};
use std::ops::Range;
/// Represents a mesh that has been sent to the GPU.
@ -24,7 +24,7 @@ impl<V: Vertex> Model<V> {
}
}
pub fn new_dynamic(device: &wgpu::Device, size: usize) -> Self {
pub fn new_dynamic(device: &wgpu::Device, size: u64) -> Self {
Self {
vbuf: Buffer::new(device, size, wgpu::BufferUsage::VERTEX),
}
@ -45,10 +45,10 @@ impl<V: Vertex> Model<V> {
device: &wgpu::Device,
queue: &wgpu::Queue,
mesh: &Mesh<V>,
offset: usize,
) -> Result<(), RenderError> {
self.buf.update(device, queue, mesh.vertices(), offset)
offset: u64,
) {
self.vbuf.update(device, queue, mesh.vertices(), offset)
}
pub fn buf(&self) -> &wgpu::Buffer { self.vbuf.buf }
pub fn buf(&self) -> &wgpu::Buffer { &self.vbuf.buf }
}

View File

@ -3,11 +3,11 @@ use super::{
terrain::Vertex,
};
use crate::mesh::greedy::GreedyMesh;
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
model_mat: [[f32; 4]; 4],
highlight_col: [f32; 4],
@ -19,7 +19,7 @@ pub struct Locals {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct BoneData {
bone_mat: [[f32; 4]; 4],
normals_mat: [[f32; 4]; 4],

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos_norm: u32,
}

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts, Renderer, Texture};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 2],
}
@ -39,7 +39,7 @@ pub struct LodData {
impl LodData {
pub fn new(
renderer: &mut Renderer,
map_size: Vec2<u16>,
map_size: Vec2<u32>,
lod_base: &[u32],
lod_alt: &[u32],
lod_horizon: &[u32],
@ -72,28 +72,41 @@ impl LodData {
..Default::default()
};
let mut view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
let map = renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
map_size.x * 4,
[map_size.x, map_size.y],
lod_base.as_bytes(),
bytemuck::cast_slice(lod_base),
);
texture_info = wgpu::TextureFormat::Rg16Uint;
texture_info.format = wgpu::TextureFormat::Rg16Uint;
view_info.format = Some(wgpu::TextureFormat::Rg16Uint);
let alt = renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
map_size.x * 4,
[map_size.x, map_size.y],
lod_base.as_bytes(),
bytemuck::cast_slice(lod_base),
);
texture_info = wgpu::TextureFormat::Rgba8Unorm;
texture_info.format = wgpu::TextureFormat::Rgba8Unorm;
view_info.format = Some(wgpu::TextureFormat::Rg16Uint);
let horizon = renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
map_size.x * 4,
[map_size.x, map_size.y],
lod_base.as_bytes(),
bytemuck::cast_slice(lod_base),
);
Self {

View File

@ -12,7 +12,7 @@ pub mod ui;
use super::Consts;
use crate::scene::camera::CameraMode;
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use common::terrain::BlockKind;
use vek::*;
@ -21,7 +21,7 @@ pub const MAX_FIGURE_SHADOW_COUNT: usize = 24;
pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Globals {
view_mat: [[f32; 4]; 4],
proj_mat: [[f32; 4]; 4],
@ -55,14 +55,14 @@ pub struct Globals {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Light {
pos: [f32; 4],
col: [f32; 4],
pub pos: [f32; 4],
pub col: [f32; 4],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Shadow {
pos_radius: [f32; 4],
}

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 3],
// ____BBBBBBBBGGGGGGGGRRRRRRRR
@ -79,7 +79,7 @@ impl ParticleMode {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance {
// created_at time, so we can calculate time relativity, needed for relative animation.
// can save 32 bits per instance, for particles that are not relatively animated.

View File

@ -1,5 +1,5 @@
use super::super::{AaMode, GlobalsLayouts, Mesh, Tri};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
@ -23,7 +23,7 @@ impl Locals {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pub pos: [f32; 2],
}

View File

@ -2,11 +2,11 @@ use super::super::{
AaMode, ColLightInfo, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout, TerrainVertex,
Texture,
};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
shadow_matrices: [[f32; 4]; 4],
texture_mats: [[f32; 4]; 4],
@ -80,12 +80,23 @@ pub fn create_col_lights(
..Default::default()
};
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
col_lights_size.x * 4,
[col_lights_size.x, col_lights_size.y],
col_lights.as_bytes(),
bytemuck::cast_slice(&col_lights),
)
}

View File

@ -1,8 +1,8 @@
use super::super::{AaMode, GlobalsLayouts, Mesh, Quad};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pub pos: [f32; 3],
}

View File

@ -1,10 +1,10 @@
use super::super::{AaMode, GlobalsLayouts, TerrainLayout};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use core::fmt;
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 3],
// Because we try to restrict terrain sprite data to a 128×128 block
@ -70,7 +70,7 @@ impl Vertex {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance {
pos_ori: u32,
inst_mat0: [f32; 4],
@ -122,7 +122,7 @@ impl Default for Instance {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
// Each matrix performs rotatation, translation, and scaling, relative to the sprite
// origin, for all sprite instances. The matrix will be in an array indexed by the

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos_norm: u32,
atlas_pos: u32,
@ -118,7 +118,7 @@ impl Vertex {
self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27);
}
fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
pub fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
use std::mem;
wgpu::VertexBufferDescriptor {
stride: mem::size_of::<Self>() as wgpu::BufferAddress,
@ -129,7 +129,7 @@ impl Vertex {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
model_offs: [f32; 3],
load_time: f32,

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts, Quad, Tri};
use bytemuck::Pod;
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 2],
uv: [f32; 2],
@ -24,7 +24,7 @@ impl Vertex {
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
pos: [f32; 4],
}

View File

@ -19,7 +19,7 @@ use vek::*;
/// A type representing data that can be converted to an immutable texture map
/// of ColLight data (used for texture atlases created during greedy meshing).
pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u16>);
pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u32>);
/// Load from a GLSL file.
pub struct Glsl(String);
@ -150,11 +150,9 @@ impl assets::Compound for Shaders {
pub struct ShadowMapRenderer {
// directed_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
// point_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
directed_depth_stencil_view: wgpu::TextureView,
directed_sampler: wgpu::Sampler,
directed_depth_stencil: Texture,
point_depth_stencil_view: wgpu::TextureView,
point_sampler: wgpu::Sampler,
point_depth_stencil: Texture,
point_pipeline: shadow::ShadowPipeline,
terrain_directed_pipeline: shadow::ShadowPipeline,
@ -179,7 +177,9 @@ pub struct Layouts {
/// rendering subsystem and contains any state necessary to interact with the
/// GPU, along with pipeline state objects (PSOs) needed to renderer different
/// kinds of models to the screen.
pub struct Renderer {
pub struct Renderer<'a> {
window: &'a winit::window::Window,
device: wgpu::Device,
queue: wgpu::Queue,
swap_chain: wgpu::SwapChain,
@ -218,13 +218,10 @@ pub struct Renderer {
mode: RenderMode,
}
impl Renderer {
impl<'a> Renderer<'a> {
/// Create a new `Renderer` from a variety of backend-specific components
/// and the window targets.
pub async fn new(
window: &winit::window::Window,
mode: RenderMode,
) -> Result<Self, RenderError> {
pub fn new(window: &'a winit::window::Window, mode: RenderMode) -> Result<Self, RenderError> {
// Enable seamless cubemaps globally, where available--they are essentially a
// strict improvement on regular cube maps.
//
@ -241,29 +238,27 @@ impl Renderer {
#[allow(unsafe_code)]
let surface = unsafe { instance.create_surface(window) };
let adapter = instance
.request_adapter(wgpu::RequestAdapterOptionsBase {
let adapter = futures::executor::block_on(instance.request_adapter(
&wgpu::RequestAdapterOptionsBase {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: Some(surface),
})
.await
.ok_or(RenderError::CouldNotFindAdapter)?;
compatible_surface: Some(&surface),
},
))
.ok_or(RenderError::CouldNotFindAdapter)?;
use wgpu::{Features, Limits};
let (device, queue) = adapter
.request_device(
wgpu::DeviceDescriptor {
// TODO
features: Features::DEPTH_CLAMPING | Features::ADDRESS_MODE_CLAMP_TO_BORDER,
limits: Limits::default(),
shader_validation: true,
},
None,
)
.await?;
let (device, queue) = futures::executor::block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
// TODO
features: Features::DEPTH_CLAMPING | Features::ADDRESS_MODE_CLAMP_TO_BORDER,
limits: Limits::default(),
shader_validation: true,
},
None,
))?;
let info = device.get_info();
let info = adapter.get_info();
info!(
?info.name,
?info.vendor,
@ -276,8 +271,8 @@ impl Renderer {
let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
width: dims.0,
height: dims.1,
width: dims.width,
height: dims.height,
present_mode: wgpu::PresentMode::Immediate,
};
@ -285,7 +280,7 @@ impl Renderer {
let shadow_views = Self::create_shadow_views(
&device,
(dims.0, dims.1),
(dims.width, dims.height),
&ShadowMapMode::try_from(mode.shadow).unwrap_or_default(),
)
.map_err(|err| {
@ -334,14 +329,10 @@ impl Renderer {
point_shadow_pipeline,
terrain_directed_shadow_pipeline,
figure_directed_shadow_pipeline,
) = create_pipelines(
&device,
&layouts & mode,
shadow_views.is_some(),
)?;
) = create_pipelines(&device, &layouts, &mode, &sc_desc, shadow_views.is_some())?;
let (tgt_color_view, tgt_depth_stencil_view, tgt_color_pp_view, win_depth_view) =
Self::create_rt_views(&device, (dims.0, dims.1), &mode)?;
Self::create_rt_views(&device, (dims.width, dims.height), &mode)?;
let shadow_map = if let (
Some(point_pipeline),
@ -354,25 +345,16 @@ impl Renderer {
figure_directed_shadow_pipeline,
shadow_views,
) {
let (
point_depth_stencil_view,
point_res,
point_sampler,
directed_depth_stencil_view,
directed_res,
directed_sampler,
) = shadow_views;
let (point_depth_stencil, directed_depth_stencil) = shadow_views;
let layout = shadow::ShadowLayout::new(&device);
Some(ShadowMapRenderer {
directed_depth_stencil_view,
directed_sampler,
directed_depth_stencil,
// point_encoder: factory.create_command_buffer().into(),
// directed_encoder: factory.create_command_buffer().into(),
point_depth_stencil_view,
point_sampler,
point_depth_stencil,
point_pipeline,
terrain_directed_pipeline,
@ -405,6 +387,8 @@ impl Renderer {
)?;
Ok(Self {
window,
device,
queue,
swap_chain,
@ -472,43 +456,24 @@ impl Renderer {
/// Resize internal render targets to match window render target dimensions.
pub fn on_resize(&mut self) -> Result<(), RenderError> {
let dims = self.win_color_view.get_dimensions();
let dims = self.window.inner_size();
// Avoid panics when creating texture with w,h of 0,0.
if dims.0 != 0 && dims.1 != 0 {
let (
tgt_color_view,
tgt_depth_stencil_view,
tgt_color_pp_view,
tgt_color_res,
tgt_depth_res,
tgt_color_res_pp,
) = Self::create_rt_views(&mut self.factory, (dims.0, dims.1), &self.mode)?;
self.tgt_color_res = tgt_color_res;
self.tgt_depth_res = tgt_depth_res;
self.tgt_color_res_pp = tgt_color_res_pp;
if dims.width != 0 && dims.height != 0 {
let (tgt_color_view, tgt_depth_stencil_view, tgt_color_pp_view, win_depth_view) =
Self::create_rt_views(&mut self.device, (dims.width, dims.height), &self.mode)?;
self.win_depth_view = win_depth_view;
self.tgt_color_view = tgt_color_view;
self.tgt_depth_stencil_view = tgt_depth_stencil_view;
self.tgt_color_pp_view = tgt_color_pp_view;
if let (Some(shadow_map), ShadowMode::Map(mode)) =
(self.shadow_map.as_mut(), self.mode.shadow)
{
match Self::create_shadow_views(&mut self.factory, (dims.0, dims.1), &mode) {
Ok((
point_depth_stencil_view,
point_res,
point_sampler,
directed_depth_stencil_view,
directed_res,
directed_sampler,
)) => {
shadow_map.point_depth_stencil_view = point_depth_stencil_view;
shadow_map.point_res = point_res;
shadow_map.point_sampler = point_sampler;
shadow_map.directed_depth_stencil_view = directed_depth_stencil_view;
shadow_map.directed_res = directed_res;
shadow_map.directed_sampler = directed_sampler;
match Self::create_shadow_views(&mut self.device, (dims.width, dims.height), &mode)
{
Ok((point_depth_stencil, directed_depth_stencil)) => {
shadow_map.point_depth_stencil = point_depth_stencil;
shadow_map.directed_depth_stencil = directed_depth_stencil;
},
Err(err) => {
warn!("Could not create shadow map views: {:?}", err);
@ -522,7 +487,7 @@ impl Renderer {
fn create_rt_views(
device: &wgpu::Device,
size: (u16, u16),
size: (u32, u32),
mode: &RenderMode,
) -> Result<
(
@ -565,9 +530,10 @@ impl Renderer {
label: None,
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::Color,
// TODO: why is this not Color?
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: Some(levels),
level_count: None,
base_array_layer: 0,
array_layer_count: None,
})
@ -596,7 +562,7 @@ impl Renderer {
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
level_count: Some(levels),
level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
@ -620,7 +586,7 @@ impl Renderer {
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
level_count: Some(levels),
level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
@ -639,28 +605,20 @@ impl Renderer {
#[allow(clippy::type_complexity)]
fn create_shadow_views(
device: &wgpu::Device,
size: (u16, u16),
size: (u32, u32),
mode: &ShadowMapMode,
) -> Result<
(
wgpu::TextureView,
wgpu::Sampler,
wgpu::TextureView,
wgpu::Sampler,
),
RenderError,
> {
) -> Result<(Texture, Texture), RenderError> {
// (Attempt to) apply resolution factor to shadow map resolution.
let resolution_factor = mode.resolution.clamped(0.25, 4.0);
let max_texture_size = Self::max_texture_size_raw(device);
// Limit to max texture size, rather than erroring.
let size = Vec2::new(size.0, size.1).map(|e| {
let size = f32::from(e) * resolution_factor;
let size = e as f32 * resolution_factor;
// NOTE: We know 0 <= e since we clamped the resolution factor to be between
// 0.25 and 4.0.
if size <= f32::from(max_texture_size) {
size as u16
if size <= max_texture_size as f32 {
size as u32
} else {
max_texture_size
}
@ -669,7 +627,7 @@ impl Renderer {
let levels = 1;
// Limit to max texture size rather than erroring.
let two_size = size.map(|e| {
u16::checked_next_power_of_two(e)
u32::checked_next_power_of_two(e)
.filter(|&e| e <= max_texture_size)
.unwrap_or(max_texture_size)
});
@ -688,17 +646,17 @@ impl Renderer {
// diag_size would be 0 too). And it must be <= diag_size,
// since min_size <= max_size. Therefore, if diag_size fits in a
// u16, so does diag_cross_size.
(diag_size as u16, diag_cross_size as u16)
(diag_size as u32, diag_cross_size as u32)
} else {
// Limit to max texture resolution rather than error.
(max_texture_size as u16, max_texture_size as u16)
(max_texture_size as u32, max_texture_size as u32)
};
let diag_two_size = u16::checked_next_power_of_two(diag_size)
let diag_two_size = u32::checked_next_power_of_two(diag_size)
.filter(|&e| e <= max_texture_size)
// Limit to max texture resolution rather than error.
.unwrap_or(max_texture_size);
let point_shadow_tex = device.create_texture(&wgpu::TextureDescriptor {
let point_shadow_tex = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: diag_two_size / 4,
@ -710,20 +668,9 @@ impl Renderer {
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth24Plus,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::OUTPUT_ATTACHMENT,
});
};
let point_tgt_shadow_view = point_shadow_tex.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Depth24Plus),
dimension: Some(wgpu::TextureViewDimension::Cube),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
level_count: Some(levels),
base_array_layer: 0,
array_layer_count: None,
});
let directed_shadow_tex = device.create_texture(&wgpu::TextureDescriptor {
let directed_shadow_tex = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: diag_two_size,
@ -735,18 +682,18 @@ impl Renderer {
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth24Plus,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::OUTPUT_ATTACHMENT,
});
};
let directed_tgt_shadow_view = point_shadow_tex.create_view(&wgpu::TextureViewDescriptor {
let mut view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Depth24Plus),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
level_count: Some(levels),
level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
@ -760,57 +707,52 @@ impl Renderer {
..Default::default()
};
let point_shadow_tex_sampler = device.create_sampler(&sampler_info);
let directed_shadow_tex_sampler = device.create_sampler(&sampler_info);
let point_tgt_shadow =
Texture::new_raw(device, &point_shadow_tex, &view_info, &sampler_info);
view_info.dimension = Some(wgpu::TextureViewDimension::Cube);
let directed_shadow_tex =
Texture::new_raw(device, &directed_shadow_tex, &view_info, &sampler_info);
Ok((
point_tgt_shadow_view,
point_shadow_tex_sampler,
directed_tgt_shadow_view,
directed_shadow_tex_sampler,
))
Ok((point_tgt_shadow, directed_shadow_tex))
}
/// Get the resolution of the render target.
/// Note: the change after a resize can be delayed so
/// don't rely on this value being constant between resize events
pub fn get_resolution(&self) -> Vec2<u16> {
Vec2::new(
self.win_color_view.get_dimensions().0,
self.win_color_view.get_dimensions().1,
)
pub fn get_resolution(&self) -> Vec2<u32> {
let dims = self.window.inner_size();
Vec2::new(dims.width, dims.height)
}
/// Get the resolution of the shadow render target.
pub fn get_shadow_resolution(&self) -> (Vec2<u16>, Vec2<u16>) {
pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) {
if let Some(shadow_map) = &self.shadow_map {
let point_dims = shadow_map.point_depth_stencil_view.get_dimensions();
let directed_dims = shadow_map.directed_depth_stencil_view.get_dimensions();
let point_dims = shadow_map.point_depth_stencil.get_dimensions();
let directed_dims = shadow_map.directed_depth_stencil.get_dimensions();
(
Vec2::new(point_dims.0, point_dims.1),
Vec2::new(directed_dims.0, directed_dims.1),
Vec2::new(point_dims.width, point_dims.height),
Vec2::new(directed_dims.width, directed_dims.height),
)
} else {
(Vec2::new(1, 1), Vec2::new(1, 1))
}
}
/// Queue the clearing of the shadow targets ready for a new frame to be
/// rendered.
pub fn clear_shadows(&mut self) {
span!(_guard, "clear_shadows", "Renderer::clear_shadows");
if !self.mode.shadow.is_map() {
return;
}
if let Some(shadow_map) = self.shadow_map.as_mut() {
// let point_encoder = &mut shadow_map.point_encoder;
let point_encoder = &mut self.encoder;
point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0);
// let directed_encoder = &mut shadow_map.directed_encoder;
let directed_encoder = &mut self.encoder;
directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view, 1.0);
}
}
// /// Queue the clearing of the shadow targets ready for a new frame to be
// /// rendered.
// pub fn clear_shadows(&mut self) {
// span!(_guard, "clear_shadows", "Renderer::clear_shadows");
// if !self.mode.shadow.is_map() {
// return;
// }
// if let Some(shadow_map) = self.shadow_map.as_mut() {
// // let point_encoder = &mut shadow_map.point_encoder;
// let point_encoder = &mut self.encoder;
// point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0);
// // let directed_encoder = &mut shadow_map.directed_encoder;
// let directed_encoder = &mut self.encoder;
// directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view,
// 1.0); }
// }
/// NOTE: Supported by Vulkan (by default), DirectX 10+ (it seems--it's hard
/// to find proof of this, but Direct3D 10 apparently does it by
@ -839,15 +781,6 @@ impl Renderer {
// }
}
/// Queue the clearing of the depth target ready for a new frame to be
/// rendered.
pub fn clear(&mut self) {
span!(_guard, "clear", "Renderer::clear");
self.encoder.clear_depth(&self.tgt_depth_stencil_view, 1.0);
// self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0);
self.encoder.clear_depth(&self.win_depth_view, 1.0);
}
// /// Set up shadow rendering.
// pub fn start_shadows(&mut self) {
// if !self.mode.shadow.is_map() {
@ -879,8 +812,7 @@ impl Renderer {
/// items.
pub fn flush(&mut self) {
span!(_guard, "flush", "Renderer::flush");
self.encoder.flush(&mut self.device);
self.device.cleanup();
self.device.poll(wgpu::Maintain::Poll);
// If the shaders files were changed attempt to recreate the shaders
if self.shaders.reloaded() {
@ -891,9 +823,11 @@ impl Renderer {
/// Recreate the pipelines
fn recreate_pipelines(&mut self) {
match create_pipelines(
&mut self.factory,
&self.device,
&self.layouts,
&self.shaders.read(),
&self.mode,
&self.sc_desc,
self.shadow_map.is_some(),
) {
Ok((
@ -907,7 +841,7 @@ impl Renderer {
lod_terrain_pipeline,
clouds_pipeline,
postprocess_pipeline,
player_shadow_pipeline,
//player_shadow_pipeline,
point_shadow_pipeline,
terrain_directed_shadow_pipeline,
figure_directed_shadow_pipeline,
@ -922,7 +856,7 @@ impl Renderer {
self.lod_terrain_pipeline = lod_terrain_pipeline;
self.clouds_pipeline = clouds_pipeline;
self.postprocess_pipeline = postprocess_pipeline;
self.player_shadow_pipeline = player_shadow_pipeline;
//self.player_shadow_pipeline = player_shadow_pipeline;
if let (
Some(point_pipeline),
Some(terrain_directed_pipeline),
@ -948,14 +882,14 @@ impl Renderer {
&mut self,
vals: &[T],
) -> Result<Consts<T>, RenderError> {
let mut consts = Consts::new(&mut self.factory, vals.len());
consts.update(&mut self.encoder, vals, 0)?;
let mut consts = Consts::new(&self.device, vals.len() as u64);
consts.update(&self.device, &self.queue, vals, 0);
Ok(consts)
}
/// Update a set of constants with the provided values.
pub fn update_consts<T: Copy + bytemuck::Pod>(&mut self, consts: &mut Consts<T>, vals: &[T]) {
consts.update(&mut self.encoder, vals, 0)
consts.update(&self.device, &self.queue, vals, 0)
}
/// Create a new set of instances with the provided values.
@ -963,39 +897,31 @@ impl Renderer {
&mut self,
vals: &[T],
) -> Result<Instances<T>, RenderError> {
let mut instances = Instances::new(&mut self.factory, vals.len())?;
instances.update(&mut self.encoder, vals)?;
let mut instances = Instances::new(&self.device, vals.len() as u64);
instances.update(&self.device, &self.queue, vals, 0);
Ok(instances)
}
/// Create a new model from the provided mesh.
pub fn create_model<V: Vertex>(&mut self, mesh: &Mesh<V>) -> Result<Model<V>, RenderError> {
Ok(Model::new(&mut self.factory, mesh))
Ok(Model::new(&self.device, mesh))
}
/// Create a new dynamic model with the specified size.
pub fn create_dynamic_model<V: Vertex>(
&mut self,
size: usize,
) -> Result<Model<V>, RenderError> {
Model::new(&self.device, size)
pub fn create_dynamic_model<V: Vertex>(&mut self, size: u64) -> Model<V> {
Model::new_dynamic(&self.device, size)
}
/// Update a dynamic model with a mesh and a offset.
pub fn update_model<V: Vertex>(
&mut self,
model: &Model<V>,
mesh: &Mesh<V>,
offset: usize,
) -> Result<(), RenderError> {
model.update(&mut self.encoder, mesh, offset)
pub fn update_model<V: Vertex>(&mut self, model: &Model<V>, mesh: &Mesh<V>, offset: u64) {
model.update(&self.device, &self.queue, mesh, offset)
}
/// Return the maximum supported texture size.
pub fn max_texture_size(&self) -> u16 { Self::max_texture_size_raw(&self.factory) }
pub fn max_texture_size(&self) -> u32 { Self::max_texture_size_raw(&self.device) }
/// Return the maximum supported texture size from the factory.
fn max_texture_size_raw(device: &wgpu::Device) -> u16 {
fn max_texture_size_raw(_device: &wgpu::Device) -> u32 {
// This value is temporary as there are plans to include a way to get this in
// wgpu this is just a sane standard for now
8192
@ -1004,18 +930,19 @@ impl Renderer {
/// Create a new immutable texture from the provided image.
pub fn create_texture_with_data_raw(
&mut self,
texture_info: wgpu::TextureDescriptor,
sampler_info: wgpu::SamplerDescriptor,
texture_info: &wgpu::TextureDescriptor,
view_info: &wgpu::TextureViewDescriptor,
sampler_info: &wgpu::SamplerDescriptor,
bytes_per_row: u32,
data: &[u8],
) -> Texture {
let tex = Texture::new_raw(&self.device, texture_info, sampler_info);
let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info);
tex.update(
&self.device,
&self.queue,
[0; 2],
[texture_info.size.x, texture_info.size.y],
[texture_info.size.width, texture_info.size.height],
data,
bytes_per_row,
);
@ -1026,10 +953,11 @@ impl Renderer {
/// Create a new raw texture.
pub fn create_texture_raw(
&mut self,
texture_info: wgpu::TextureDescriptor,
sampler_info: wgpu::SamplerDescriptor,
texture_info: &wgpu::TextureDescriptor,
view_info: &wgpu::TextureViewDescriptor,
sampler_info: &wgpu::SamplerDescriptor,
) -> Texture {
Texture::new_raw(&self.device, texture_info, sampler_info)
Texture::new_raw(&self.device, texture_info, view_info, sampler_info)
}
/// Create a new texture from the provided image.
@ -1040,7 +968,7 @@ impl Renderer {
image: &image::DynamicImage,
filter_method: Option<FilterMode>,
address_mode: Option<AddressMode>,
) -> Texture {
) -> Result<Texture, RenderError> {
Texture::new(
&self.device,
&self.queue,
@ -1054,8 +982,8 @@ impl Renderer {
/// specified dimensions.
///
/// Currently only supports Rgba8Srgb
pub fn create_dynamic_texture(&mut self, dims: Vec2<u16>) -> Texture {
Texture::new_dynamic(&mut self.factory, dims.x, dims.y)
pub fn create_dynamic_texture(&mut self, dims: Vec2<u32>) -> Texture {
Texture::new_dynamic(&self.device, dims.x, dims.y)
}
/// Update a texture with the provided offset, size, and data.
@ -1064,8 +992,8 @@ impl Renderer {
pub fn update_texture(
&mut self,
texture: &Texture, /* <T> */
offset: [u16; 2],
size: [u16; 2],
offset: [u32; 2],
size: [u32; 2],
// TODO
// data: &[<<T as gfx::format::Formatted>::Surface as
// gfx::format::SurfaceTyped>::DataType], ) -> Result<(), RenderError>
@ -1078,7 +1006,14 @@ impl Renderer {
data: &[[u8; 4]],
bytes_per_row: u32,
) {
texture.update(&mut self.encoder, offset, size, data, bytes_per_row)
texture.update(
&self.device,
&self.queue,
offset,
size,
bytemuck::cast_slice(data),
bytes_per_row,
)
}
/// Creates a download buffer, downloads the win_color_view, and converts to
@ -1945,18 +1880,18 @@ fn create_pipelines(
options.set_optimization_level(OptimizationLevel::Performance);
options.set_include_callback(move |name, _, shader_name, _| {
Ok(ResolvedInclude {
resolved_name: name,
resolved_name: name.to_string(),
content: match name {
"constants.glsl" => constants,
"globals.glsl" => globals,
"shadows.glsl" => shadows,
"sky.glsl" => sky,
"light.glsl" => light,
"srgb.glsl" => srgb,
"random.glsl" => &random,
"lod.glsl" => &lod,
"anti-aliasing.glsl" => &anti_alias,
"cloud.glsl" => &cloud,
"globals.glsl" => *globals,
"shadows.glsl" => *shadows,
"sky.glsl" => *sky,
"light.glsl" => *light,
"srgb.glsl" => *srgb,
"random.glsl" => *random,
"lod.glsl" => *lod,
"anti-aliasing.glsl" => *anti_alias,
"cloud.glsl" => *cloud,
other => return Err(format!("Include {} is not defined", other)),
},
})
@ -1975,7 +1910,7 @@ fn create_pipelines(
let figure_vert_mod = create_shader_module(
device,
&mut compiler,
figure_vert,
&figure_vert,
ShaderKind::Vertex,
"figure-vert.glsl",
&options,
@ -1984,7 +1919,7 @@ fn create_pipelines(
let terrain_point_shadow_vert_mod = create_shader_module(
device,
&mut compiler,
terrain_point_shadow_vert,
&terrain_point_shadow_vert,
ShaderKind::Vertex,
"light-shadows-vert.glsl",
&options,
@ -1993,7 +1928,7 @@ fn create_pipelines(
let terrain_directed_shadow_vert_mod = create_shader_module(
device,
&mut compiler,
terrain_directed_shadow_vert,
&terrain_directed_shadow_vert,
ShaderKind::Vertex,
"light-shadows-directed-vert.glsl",
&options,
@ -2002,7 +1937,7 @@ fn create_pipelines(
let figure_directed_shadow_vert_mod = create_shader_module(
device,
&mut compiler,
figure_directed_shadow_vert,
&figure_directed_shadow_vert,
ShaderKind::Vertex,
"light-shadows-figure-vert.glsl",
&options,
@ -2022,24 +1957,24 @@ fn create_pipelines(
// Construct a pipeline for rendering skyboxes
let skybox_pipeline = skybox::SkyboxPipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.skybox_vert.read().0,
ShaderKind::Vertex,
"skybox-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.skybox_frag.read().0,
ShaderKind::Fragment,
"skybox-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
mode.aa,
);
@ -2047,55 +1982,57 @@ fn create_pipelines(
let figure_pipeline = figure::FigurePipeline::new(
device,
&figure_vert_mod,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.figure_frag.read().0,
ShaderKind::Fragment,
"figure-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
&layouts.figure,
mode.aa,
);
// Construct a pipeline for rendering terrain
let terrain_pipeline = terrain::TerrainPipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.terrain_vert.read().0,
ShaderKind::Vertex,
"terrain-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.terrain_frag.read().0,
ShaderKind::Fragment,
"terrain-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
&layouts.terrain,
mode.aa,
);
// Construct a pipeline for rendering fluids
let fluid_pipeline = fluid::FluidPipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.fluid_vert.read().0,
ShaderKind::Vertex,
"terrain-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
match mode.fluid {
@ -2105,105 +2042,109 @@ fn create_pipelines(
ShaderKind::Fragment,
"fluid-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
&layouts.fluid,
mode.aa,
);
// Construct a pipeline for rendering sprites
let sprite_pipeline = sprite::SpritePipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.sprite_vert.read().0,
ShaderKind::Vertex,
"sprite-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.sprite_frag.read().0,
ShaderKind::Fragment,
"sprite-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
&layouts.sprite,
&layouts.terrain,
mode.aa,
);
// Construct a pipeline for rendering particles
let particle_pipeline = particle::ParticlePipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.particle_vert.read().0,
ShaderKind::Vertex,
"particle-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.particle_frag.read().0,
ShaderKind::Fragment,
"particle-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
mode.aa,
);
// Construct a pipeline for rendering UI elements
let ui_pipeline = ui::UIPipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.ui_vert.read().0,
ShaderKind::Vertex,
"ui-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.ui_frag.read().0,
ShaderKind::Fragment,
"ui-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
&layouts.ui,
mode.aa,
);
// Construct a pipeline for rendering terrain
let lod_terrain_pipeline = lod_terrain::LodTerrainPipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.lod_terrain_vert.read().0,
ShaderKind::Vertex,
"lod-terrain-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.lod_terrain_frag.read().0,
ShaderKind::Fragment,
"lod-terrain-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
mode.aa,
);
@ -2221,24 +2162,25 @@ fn create_pipelines(
// Construct a pipeline for rendering our post-processing
let postprocess_pipeline = postprocess::PostProcessPipeline::new(
device,
create_shader_module(
&create_shader_module(
device,
&mut compiler,
shaders.postprocess_vert.read().0,
ShaderKind::Vertex,
"postprocess-vert.glsl",
&options,
),
create_shader_module(
)?,
&create_shader_module(
device,
&mut compiler,
shaders.postprocess_frag.read().0,
ShaderKind::Fragment,
"postprocess-frag.glsl",
&options,
),
)?,
sc_desc,
layouts,
&layouts.global,
&layouts.postprocess,
mode.aa,
);
@ -2368,5 +2310,9 @@ pub fn create_shader_module(
let spv = compiler.compile_into_spirv(source, kind, file_name, "main", Some(options))?;
Ok(device.create_shader_module(wgpu::ShaderModule::SpirV(Cow::Bowrrowed(spv.as_binary()))))
Ok(
device.create_shader_module(wgpu::ShaderModuleSource::SpirV(Cow::Borrowed(
spv.as_binary(),
))),
)
}

View File

@ -4,7 +4,8 @@ use wgpu::Extent3d;
/// Represents an image that has been uploaded to the GPU.
pub struct Texture {
pub tex: wgpu::TextureView,
pub tex: wgpu::Texture,
pub view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
size: Extent3d,
}
@ -49,7 +50,7 @@ impl Texture {
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&[buffer.as_slice()],
buffer.as_slice(),
wgpu::TextureDataLayout {
offset: 0,
bytes_per_row: image.width() * 4,
@ -73,21 +74,33 @@ impl Texture {
..Default::default()
};
let view = tex.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
Ok(Self {
tex,
view,
sampler: device.create_sampler(&sampler_info),
size,
})
}
pub fn new_dynamic(device: &wgpu::Device, width: u16, height: u16) -> Self {
pub fn new_dynamic(device: &wgpu::Device, width: u32, height: u32) -> Self {
let size = wgpu::Extent3d {
width,
height,
depth: 1,
};
let tex_info = device.create_texture(&wgpu::TextureDescriptor {
let tex_info = wgpu::TextureDescriptor {
label: None,
size,
mip_level_count: 1,
@ -95,7 +108,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED,
});
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
@ -108,19 +121,35 @@ impl Texture {
..Default::default()
};
Self::new_raw(device, tex_info, sampler_info)
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
Self::new_raw(device, &tex_info, &view_info, &sampler_info)
}
pub fn new_raw(
device: &wgpu::Device,
texture_info: wgpu::TextureDescriptor,
sampler_info: wgpu::SamplerDescriptor,
texture_info: &wgpu::TextureDescriptor,
view_info: &wgpu::TextureViewDescriptor,
sampler_info: &wgpu::SamplerDescriptor,
) -> Self {
Ok(Self {
tex: device.create_texture(&texture_info),
sampler: device.create_sampler(&sampler_info),
let tex = device.create_texture(texture_info);
let view = tex.create_view(view_info);
Self {
tex,
view,
sampler: device.create_sampler(sampler_info),
size: texture_info.size,
})
}
}
/// Update a texture with the given data (used for updating the glyph cache
@ -129,11 +158,11 @@ impl Texture {
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
offset: [u16; 2],
size: [u16; 2],
offset: [u32; 2],
size: [u32; 2],
data: &[u8],
bytes_per_row: u32,
) -> Result<(), RenderError> {
) {
// TODO: Only works for 2D images
queue.write_texture(
wgpu::TextureCopyViewBase {
@ -146,12 +175,10 @@ impl Texture {
},
},
data,
// TODO: I heard some rumors that there are other
// formats that are not Rgba8
wgpu::TextureDataLayout {
offset: 0,
bytes_per_row,
rows_per_image: self.size.y,
rows_per_image: self.size.height,
},
wgpu::Extent3d {
width: size[0],

View File

@ -168,19 +168,19 @@ fn handle_main_events_cleared(
if let Some(last) = states.last_mut() {
span!(guard, "Render");
let renderer = global_state.window.renderer_mut();
// Clear the shadow maps.
renderer.clear_shadows();
// Clear the screen
renderer.clear();
// // Clear the shadow maps.
// renderer.clear_shadows();
// // Clear the screen
// renderer.clear();
// Render the screen using the global renderer
last.render(renderer, &global_state.settings);
// Finish the frame.
global_state.window.renderer_mut().flush();
// Display the frame on the window.
global_state
.window
.swap_buffers()
.expect("Failed to swap window buffers!");
// global_state.window.renderer_mut().flush();
// // Display the frame on the window.
// global_state
// .window
// .swap_buffers()
// .expect("Failed to swap window buffers!");
drop(guard);
}

View File

@ -1,6 +1,6 @@
use super::{load::BodySpec, FigureModelEntry};
use crate::{
mesh::{greedy::GreedyMesh, Meshable},
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain},
render::{BoneMeshes, ColLightInfo, FigureModel, Mesh, Renderer, TerrainVertex},
scene::camera::CameraMode,
};
@ -476,11 +476,10 @@ where
offset: Vec3<f32>,
bone_idx: u8,
) -> BoneMeshes {
let (opaque, _, _, bounds) =
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
segment,
(greedy, opaque_mesh, offset, Vec3::one(), bone_idx),
);
let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain(
segment,
(greedy, opaque_mesh, offset, Vec3::one(), bone_idx),
);
(opaque, bounds)
}
@ -492,17 +491,16 @@ where
bone_idx: u8,
) -> BoneMeshes {
let lod_scale = 0.6;
let (opaque, _, _, bounds) =
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
segment.scaled_by(Vec3::broadcast(lod_scale)),
(
greedy,
opaque_mesh,
offset * lod_scale,
Vec3::one() / lod_scale,
bone_idx,
),
);
let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain(
segment.scaled_by(Vec3::broadcast(lod_scale)),
(
greedy,
opaque_mesh,
offset * lod_scale,
Vec3::one() / lod_scale,
bone_idx,
),
);
(opaque, bounds)
}
@ -514,17 +512,16 @@ where
bone_idx: u8,
) -> BoneMeshes {
let lod_scale = 0.3;
let (opaque, _, _, bounds) =
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
segment.scaled_by(Vec3::broadcast(lod_scale)),
(
greedy,
opaque_mesh,
offset * lod_scale,
Vec3::one() / lod_scale,
bone_idx,
),
);
let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain(
segment.scaled_by(Vec3::broadcast(lod_scale)),
(
greedy,
opaque_mesh,
offset * lod_scale,
Vec3::one() / lod_scale,
bone_idx,
),
);
(opaque, bounds)
}

View File

@ -5230,12 +5230,9 @@ impl FigureColLights {
span!(_guard, "create_figure", "FigureColLights::create_figure");
let atlas = &mut self.atlas;
let allocation = atlas
.allocate(guillotiere::Size::new(
i32::from(tex_size.x),
i32::from(tex_size.y),
))
.allocate(guillotiere::Size::new(tex_size.x as i32, tex_size.y as i32))
.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 = pipelines::shadow::create_col_lights(renderer, &(tex, tex_size));
let model_len = u32::try_from(opaque.vertices().len())
.expect("The model size for this figure does not fit in a u32!");
let model = renderer.create_model(&opaque)?;
@ -5262,8 +5259,7 @@ impl FigureColLights {
#[allow(clippy::unnecessary_wraps)]
fn make_atlas(renderer: &mut Renderer) -> Result<AtlasAllocator, RenderError> {
let max_texture_size = renderer.max_texture_size();
let atlas_size =
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions {
// TODO: Verify some good empirical constants.
small_size_threshold: 32,
@ -5286,7 +5282,7 @@ impl FigureColLights {
(0, 0),
gfx::format::Swizzle::new(),
gfx::texture::SamplerInfo::new(
gfx::texture::FilterMethod::Bilinear,
gfx::texture::FilterMetho>:Bilinear,
gfx::texture::WrapMode::Clamp,
),
)?;
@ -5467,18 +5463,16 @@ impl<S: Skeleton> FigureState<S> {
self.last_light,
self.last_glow,
);
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
renderer.update_consts(&mut self.locals, &[locals]);
let lantern_offset = anim::compute_matrices(&self.skeleton, mat, buf);
let new_bone_consts = figure_bone_data_from_anim(buf);
renderer
.update_consts(
&mut self.meta.bone_consts,
&new_bone_consts[0..S::BONE_COUNT],
)
.unwrap();
renderer.update_consts(
&mut self.meta.bone_consts,
&new_bone_consts[0..S::BONE_COUNT],
);
self.lantern_offset = lantern_offset;
let smoothing = (5.0 * dt).min(1.0);

View File

@ -26,7 +26,7 @@ impl Lod {
model: None,
data: LodData::new(
renderer,
client.world_data().chunk_size(),
client.world_data().chunk_size().as_(),
client.world_data().lod_base.raw(),
client.world_data().lod_alt.raw(),
client.world_data().lod_horizon.raw(),

View File

@ -584,9 +584,7 @@ impl Scene {
);
lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32);
lights.truncate(MAX_LIGHT_COUNT);
renderer
.update_consts(&mut self.data.lights, &lights)
.expect("Failed to update light constants");
renderer.update_consts(&mut self.data.lights, &lights);
// Update event lights
let dt = ecs.fetch::<DeltaTime>().0;
@ -623,9 +621,7 @@ impl Scene {
.collect::<Vec<_>>();
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32);
shadows.truncate(MAX_SHADOW_COUNT);
renderer
.update_consts(&mut self.data.shadows, &shadows)
.expect("Failed to update light constants");
renderer.update_consts(&mut self.data.shadows, &shadows);
// Remember to put the new loaded distance back in the scene.
self.loaded_distance = loaded_distance;
@ -636,48 +632,42 @@ impl Scene {
let focus_off = focus_pos.map(|e| e.trunc());
// Update global constants.
renderer
.update_consts(&mut self.data.globals, &[Globals::new(
view_mat,
proj_mat,
cam_pos,
focus_pos,
self.loaded_distance,
self.lod.get_data().tgt_detail as f32,
self.map_bounds,
time_of_day,
scene_data.state.get_time(),
renderer.get_resolution(),
Vec2::new(SHADOW_NEAR, SHADOW_FAR),
lights.len(),
shadows.len(),
NUM_DIRECTED_LIGHTS,
scene_data
.state
.terrain()
.get((cam_pos + focus_off).map(|e| e.floor() as i32))
.map(|b| b.kind())
.unwrap_or(BlockKind::Air),
self.select_pos.map(|e| e - focus_off.map(|e| e as i32)),
scene_data.gamma,
scene_data.exposure,
scene_data.ambiance,
self.camera.get_mode(),
scene_data.sprite_render_distance as f32 - 20.0,
)])
.expect("Failed to update global constants");
renderer
.update_consts(&mut self.clouds.locals, &[CloudsLocals::new(
proj_mat_inv,
view_mat_inv,
)])
.expect("Failed to update cloud locals");
renderer
.update_consts(&mut self.postprocess.locals, &[PostProcessLocals::new(
proj_mat_inv,
view_mat_inv,
)])
.expect("Failed to update post-process locals");
renderer.update_consts(&mut self.data.globals, &[Globals::new(
view_mat,
proj_mat,
cam_pos,
focus_pos,
self.loaded_distance,
self.lod.get_data().tgt_detail as f32,
self.map_bounds,
time_of_day,
scene_data.state.get_time(),
renderer.get_resolution().as_(),
Vec2::new(SHADOW_NEAR, SHADOW_FAR),
lights.len(),
shadows.len(),
NUM_DIRECTED_LIGHTS,
scene_data
.state
.terrain()
.get((cam_pos + focus_off).map(|e| e.floor() as i32))
.map(|b| b.kind())
.unwrap_or(BlockKind::Air),
self.select_pos.map(|e| e - focus_off.map(|e| e as i32)),
scene_data.gamma,
scene_data.exposure,
scene_data.ambiance,
self.camera.get_mode(),
scene_data.sprite_render_distance as f32 - 20.0,
)]);
renderer.update_consts(&mut self.clouds.locals, &[CloudsLocals::new(
proj_mat_inv,
view_mat_inv,
)]);
renderer.update_consts(&mut self.postprocess.locals, &[PostProcessLocals::new(
proj_mat_inv,
view_mat_inv,
)]);
// Maintain LoD.
self.lod.maintain(renderer);
@ -985,9 +975,7 @@ impl Scene {
})
}));
renderer
.update_consts(&mut self.data.shadow_mats, &shadow_mats)
.expect("Failed to update light constants");
renderer.update_consts(&mut self.data.shadow_mats, &shadow_mats);
}
// Remove unused figures.
@ -1028,10 +1016,10 @@ impl Scene {
// would instead have this as an extension.
if renderer.render_mode().shadow.is_map() && (is_daylight || !light_data.1.is_empty()) {
if is_daylight {
// Set up shadow mapping.
renderer.start_shadows();
}
// if is_daylight {
// // Set up shadow mapping.
// renderer.start_shadows();
// }
// Render terrain shadows.
self.terrain
@ -1041,10 +1029,10 @@ impl Scene {
self.figure_mgr
.render_shadows(renderer, state, tick, global, light_data, camera_data);
if is_daylight {
// Flush shadows.
renderer.flush_shadows();
}
// if is_daylight {
// // Flush shadows.
// renderer.flush_shadows();
// }
}
let lod = self.lod.get_data();

View File

@ -1,6 +1,6 @@
use super::{terrain::BlocksOfInterest, SceneData, Terrain};
use crate::{
mesh::{greedy::GreedyMesh, Meshable},
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_particle},
render::{
pipelines::particle::ParticleMode, GlobalModel, Instances, Light, LodData, Model,
ParticleInstance, ParticleVertex, Renderer,
@ -1220,14 +1220,12 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model<Particl
// NOTE: If we add texturing we may eventually try to share it among all
// particles in a single atlas.
let max_texture_size = renderer.max_texture_size();
let max_size =
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
let mut greedy = GreedyMesh::new(max_size);
let segment = Segment::from(&vox.read().0);
let segment_size = segment.size();
let mut mesh =
Meshable::<ParticleVertex, &mut GreedyMesh>::generate_mesh(segment, &mut greedy).0;
let mesh = generate_mesh_base_vol_particle(segment, &mut greedy).0;
// Center particle vertices around origin
for vert in mesh.vertices_mut() {
vert.pos[0] -= segment_size.x as f32 / 2.0;

View File

@ -1,5 +1,5 @@
use crate::{
mesh::{greedy::GreedyMesh, Meshable},
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain},
render::{
create_clouds_mesh, create_pp_mesh, create_skybox_mesh, BoneMeshes, CloudsLocals, Consts,
FigureModel, GlobalModel, Globals, Light, Mesh, Model, PostProcessLocals,
@ -51,10 +51,7 @@ fn generate_mesh<'a>(
bone_idx: u8,
) -> BoneMeshes {
let (opaque, _, /* shadow */ _, bounds) =
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
segment,
(greedy, mesh, offset, Vec3::one(), bone_idx),
);
generate_mesh_base_vol_terrain(segment, (greedy, mesh, offset, Vec3::one(), bone_idx));
(opaque /* , shadow */, bounds)
}
@ -152,7 +149,7 @@ impl Scene {
&alt_image,
&horizon_image,
1,
map_border.into(),
//map_border.into(),
),
map_bounds,
@ -271,7 +268,7 @@ impl Scene {
const SHADOW_NEAR: f32 = 1.0;
const SHADOW_FAR: f32 = 25.0;
if let Err(e) = renderer.update_consts(&mut self.data.globals, &[Globals::new(
renderer.update_consts(&mut self.data.globals, &[Globals::new(
view_mat,
proj_mat,
cam_pos,
@ -281,7 +278,7 @@ impl Scene {
self.map_bounds,
TIME,
scene_data.time,
renderer.get_resolution(),
renderer.get_resolution().as_(),
Vec2::new(SHADOW_NEAR, SHADOW_FAR),
0,
0,
@ -293,9 +290,7 @@ impl Scene {
scene_data.ambiance,
self.camera.get_mode(),
250.0,
)]) {
error!(?e, "Renderer failed to update");
}
)]);
self.figure_model_cache
.clean(&mut self.col_lights, scene_data.tick);

View File

@ -2,7 +2,11 @@ mod watcher;
pub use self::watcher::{BlocksOfInterest, Interaction};
use crate::{
mesh::{greedy::GreedyMesh, terrain::SUNLIGHT, Meshable},
mesh::{
greedy::GreedyMesh,
segment::generate_mesh_base_vol_sprite,
terrain::{generate_mesh, SUNLIGHT},
},
render::{
pipelines, ColLightInfo, Consts, FluidVertex, GlobalModel, Instances, Mesh, Model,
RenderError, Renderer, SpriteInstance, SpriteLocals, SpriteVertex, TerrainLocals,
@ -30,7 +34,6 @@ use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
use tracing::warn;
use treeculler::{BVol, Frustum, AABB};
use vek::*;
@ -174,17 +177,21 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
) -> MeshWorkerResponse {
span!(_guard, "mesh_worker");
let blocks_of_interest = BlocksOfInterest::from_chunk(&chunk);
let mesh;
let (light_map, glow_map) = if let Some((light_map, glow_map)) = &skip_remesh {
mesh = None;
(&**light_map, &**glow_map)
} else {
let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info, light_map, glow_map)) =
volume.generate_mesh((
range,
Vec2::new(max_texture_size, max_texture_size),
&blocks_of_interest,
));
generate_mesh(
&volume,
(
range,
Vec2::new(max_texture_size, max_texture_size),
&blocks_of_interest,
),
);
mesh = Some(MeshWorkerResponseMesh {
// TODO: Take sprite bounds into account somehow?
z_bounds: (bounds.min.z, bounds.max.z),
@ -198,6 +205,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
let mesh = mesh.as_ref().unwrap();
(&*mesh.light_map, &*mesh.glow_map)
};
MeshWorkerResponse {
pos,
// Extract sprite locations from volume
@ -363,8 +371,7 @@ impl SpriteRenderContext {
let sprite_config =
Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned();
let max_size =
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
let mut greedy = GreedyMesh::new(max_size);
let mut locals_buffer = [SpriteLocals::default(); 8];
let sprite_config_ = &sprite_config;
@ -400,14 +407,15 @@ impl SpriteRenderContext {
)
.unwrap_or(zero);
let max_model_size = Vec3::new(31.0, 31.0, 63.0);
let model_scale = max_model_size.map2(model_size, |max_sz: f32, cur_sz| {
let scale = max_sz / max_sz.max(cur_sz as f32);
if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz {
scale - 0.001
} else {
scale
}
});
let model_scale =
max_model_size.map2(model_size, |max_sz: f32, cur_sz| {
let scale = max_sz / max_sz.max(cur_sz as f32);
if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz {
scale - 0.001
} else {
scale
}
});
let sprite_mat: Mat4<f32> =
Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE);
move |greedy: &mut GreedyMesh| {
@ -421,14 +429,15 @@ impl SpriteRenderContext {
Vec3::broadcast(1.0)
} else {
lod_axes * lod_scale_orig
+ lod_axes
.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
+ lod_axes.map(|e| {
if e == 0.0 { 1.0 } else { 0.0 }
})
};
// Mesh generation exclusively acts using side effects; it
// has no
// Mesh generation exclusively acts using side effects;
// it has no
// interesting return value, but updates the mesh.
let mut opaque_mesh = Mesh::new();
Meshable::<SpriteVertex, &mut GreedyMesh>::generate_mesh(
generate_mesh_base_vol_sprite(
Segment::from(&model.read().0).scaled_by(lod_scale),
(greedy, &mut opaque_mesh, false),
);
@ -438,8 +447,9 @@ impl SpriteRenderContext {
sprite_mat * Mat4::scaling_3d(sprite_scale);
locals_buffer.iter_mut().enumerate().for_each(
|(ori, locals)| {
let sprite_mat = sprite_mat
.rotated_z(f32::consts::PI * 0.25 * ori as f32);
let sprite_mat = sprite_mat.rotated_z(
f32::consts::PI * 0.25 * ori as f32,
);
*locals = SpriteLocals::new(
sprite_mat,
sprite_scale,
@ -522,8 +532,7 @@ impl SpriteRenderContext {
})
.collect();
let sprite_col_lights =
pipelines::shadow::create_col_lights(renderer, &sprite_col_lights)
.expect("Failed to upload sprite color and light data to the GPU!");
pipelines::shadow::create_col_lights(renderer, &sprite_col_lights);
Self {
sprite_config: Arc::clone(&sprite_config),
@ -573,8 +582,7 @@ impl<V: RectRasterableVol> Terrain<V> {
) -> Result<(AtlasAllocator, Texture /* <ColLightFmt> */), RenderError> {
span!(_guard, "make_atlas", "Terrain::make_atlas");
let max_texture_size = renderer.max_texture_size();
let atlas_size =
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions {
// TODO: Verify some good empirical constants.
small_size_threshold: 128,
@ -582,7 +590,7 @@ impl<V: RectRasterableVol> Terrain<V> {
..guillotiere::AllocatorOptions::default()
});
let texture = renderer.create_texture_raw(
wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label: Some("Atlas texture"),
size: wgpu::Extent3d {
width: max_texture_size,
@ -595,7 +603,17 @@ impl<V: RectRasterableVol> Terrain<V> {
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED,
},
wgpu::SamplerDescriptor {
&wgpu::TextureViewDescriptor {
label: Some("Atlas texture view"),
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
dimension: Some(wgpu::TextureViewDimension::D1),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: 0,
array_layer_count: None,
},
&wgpu::SamplerDescriptor {
label: Some("Atlas sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
@ -605,7 +623,7 @@ impl<V: RectRasterableVol> Terrain<V> {
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
},
)?;
);
Ok((atlas, texture))
}
@ -955,7 +973,6 @@ impl<V: RectRasterableVol> Terrain<V> {
break;
}
// Find the area of the terrain we want. Because meshing needs to compute things
// like ambient occlusion and edge elision, we also need the borders
// of the chunk's neighbours too (hence the `- 1` and `+ 1`).
let aabr = Aabr {
@ -1022,7 +1039,7 @@ impl<V: RectRasterableVol> Terrain<V> {
skip_remesh,
started_tick,
volume,
max_texture_size,
max_texture_size as u16,
chunk,
aabb,
&sprite_data,
@ -1080,8 +1097,9 @@ impl<V: RectRasterableVol> Terrain<V> {
let col_lights = &mut self.col_lights;
let allocation = atlas
.allocate(guillotiere::Size::new(
i32::from(tex_size.x),
i32::from(tex_size.y),
tex_size.x as i32, /* TODO: adjust ColLightInfo to avoid the
* cast here? */
tex_size.y as i32,
))
.unwrap_or_else(|| {
// Atlas allocation failure: try allocating a new texture and atlas.
@ -1104,25 +1122,25 @@ impl<V: RectRasterableVol> Terrain<V> {
atlas
.allocate(guillotiere::Size::new(
i32::from(tex_size.x),
i32::from(tex_size.y),
tex_size.x as i32, /* TODO: adjust ColLightInfo to avoid
* the
* cast here? */
tex_size.y as i32,
))
.expect("Chunk data does not fit in a texture of maximum size.")
});
// NOTE: Cast is safe since the origin was a u16.
let atlas_offs = Vec2::new(
allocation.rectangle.min.x as u16,
allocation.rectangle.min.y as u16,
allocation.rectangle.min.x as u32,
allocation.rectangle.min.y as u32,
);
if let Err(err) = renderer.update_texture(
renderer.update_texture(
col_lights,
atlas_offs.into_array(),
tex_size.into_array(),
&tex,
) {
warn!("Failed to update texture: {:?}", err);
}
);
self.insert_chunk(response.pos, TerrainChunkData {
load_time,
@ -1152,8 +1170,8 @@ impl<V: RectRasterableVol> Terrain<V> {
)
.into_array(),
atlas_offs: Vec4::new(
i32::from(atlas_offs.x),
i32::from(atlas_offs.y),
atlas_offs.x as i32,
atlas_offs.y as i32,
0,
0,
)

View File

@ -518,7 +518,7 @@ impl KeyMouse {
}
pub struct Window {
renderer: Renderer,
renderer: Renderer<'static>,
window: winit::window::Window,
cursor_grabbed: bool,
pub pan_sensitivity: u32,
@ -574,7 +574,7 @@ impl Window {
// .map_err(|err| Error::BackendError(Box::new(err)))?
// .init_gfx::<WinColorFmt, WinDepthFmt>();
let window = win_builder.build(&event_loop)?;
let window = win_builder.build(&event_loop).unwrap();
let renderer = Renderer::new(&window, settings.graphics.render_mode.clone())?;
@ -658,7 +658,7 @@ impl Window {
pub fn renderer(&self) -> &Renderer { &self.renderer }
pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
pub fn renderer_mut(&mut self) -> &mut Renderer<'static> { &mut self.renderer }
pub fn resolve_deduplicated_events(&mut self, settings: &mut Settings) {
// Handle screenshots and toggling fullscreen
@ -938,9 +938,9 @@ impl Window {
match event {
WindowEvent::CloseRequested => self.events.push(Event::Close),
WindowEvent::Resized(physical) => {
let (mut color_view, mut depth_view) = self.renderer.win_views_mut();
self.window.resize(physical);
self.window.update_gfx(&mut color_view, &mut depth_view);
// let (mut color_view, mut depth_view) = self.renderer.win_views_mut();
// self.window.resize(physical);
// self.window.update_gfx(&mut color_view, &mut depth_view);
self.renderer.on_resize().unwrap();
// TODO: update users of this event with the fact that it is now the physical
// size
@ -1085,32 +1085,24 @@ impl Window {
/// Moves cursor by an offset
pub fn offset_cursor(&self, d: Vec2<f32>) {
if d != Vec2::zero() {
if let Err(err) =
self.window
.window()
.set_cursor_position(winit::dpi::LogicalPosition::new(
d.x as f64 + self.cursor_position.x,
d.y as f64 + self.cursor_position.y,
))
if let Err(err) = self
.window
.set_cursor_position(winit::dpi::LogicalPosition::new(
d.x as f64 + self.cursor_position.x,
d.y as f64 + self.cursor_position.y,
))
{
error!("Error setting cursor position: {:?}", err);
}
}
}
pub fn swap_buffers(&self) -> Result<(), Error> {
span!(_guard, "swap_buffers", "Window::swap_buffers");
self.window
.swap_buffers()
.map_err(|err| Error::BackendError(Box::new(err)))
}
pub fn is_cursor_grabbed(&self) -> bool { self.cursor_grabbed }
pub fn grab_cursor(&mut self, grab: bool) {
self.cursor_grabbed = grab;
self.window.window().set_cursor_visible(!grab);
let _ = self.window.window().set_cursor_grab(grab);
self.window.set_cursor_visible(!grab);
let _ = self.window.set_cursor_grab(grab);
}
/// Moves mouse cursor to center of screen
@ -1156,8 +1148,7 @@ impl Window {
// the correct resolution already, load that value, otherwise filter it
// in this iteration
let correct_res = correct_res.unwrap_or_else(|| {
let window = self.window.window();
window
self.window
.current_monitor()
.unwrap()
.video_modes()
@ -1309,7 +1300,6 @@ impl Window {
self
.window
.window()
.current_monitor().unwrap()
.video_modes()
// Prefer bit depth over refresh rate
@ -1321,7 +1311,7 @@ impl Window {
}
pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) {
let window = self.window.window();
let window = self.window;
self.fullscreen = fullscreen;
window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode {
FullscreenMode::Exclusive => {
@ -1343,20 +1333,17 @@ impl Window {
pub fn logical_size(&self) -> Vec2<f64> {
let (w, h) = self
.window
.window()
.inner_size()
.to_logical::<f64>(self.window.window().scale_factor())
.to_logical::<f64>(self.window.scale_factor())
.into();
Vec2::new(w, h)
}
pub fn set_size(&mut self, new_size: Vec2<u16>) {
self.window
.window()
.set_inner_size(winit::dpi::LogicalSize::new(
new_size.x as f64,
new_size.y as f64,
));
self.window.set_inner_size(winit::dpi::LogicalSize::new(
new_size.x as f64,
new_size.y as f64,
));
}
pub fn send_event(&mut self, event: Event) { self.events.push(event) }