More fixes

This commit is contained in:
Capucho 2020-09-26 16:43:59 +01:00 committed by Avi Weinstock
parent 05d97e2f2f
commit 3502699732
33 changed files with 1037 additions and 1153 deletions

1
Cargo.lock generated
View File

@ -6063,6 +6063,7 @@ dependencies = [
name = "veloren-voxygen-anim" name = "veloren-voxygen-anim"
version = "0.9.0" version = "0.9.0"
dependencies = [ dependencies = [
"bytemuck",
"find_folder", "find_folder",
"lazy_static", "lazy_static",
"libloading 0.7.0", "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} notify = {version = "5.0.0-pre.2", optional = true}
tracing = {version = "0.1", optional = true} tracing = {version = "0.1", optional = true}
vek = {version = "=0.14.1", features = ["serde"]} vek = {version = "=0.14.1", features = ["serde"]}
bytemuck = { version="1.4", features=["derive"] }

View File

@ -75,16 +75,19 @@ pub use dyn_lib::init;
use std::ffi::CStr; use std::ffi::CStr;
use self::vek::*; use self::vek::*;
use bytemuck::{Pod, Zeroable};
type MatRaw = [[f32; 4]; 4]; 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; pub const MAX_BONE_COUNT: usize = 16;
fn make_bone(mat: Mat4<f32>) -> FigureBoneData { fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
let normal = mat.map_cols(Vec4::normalized); 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>; 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 common_base::span;
use vek::*; use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type TodoRect = ( type TodoRect = (
Vec3<i32>, Vec3<i32>,
Vec2<Vec3<u16>>, 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`. /// For an explanation of why we want this, see `SuspendedMesh`.
pub struct GreedyMesh<'a> { pub struct GreedyMesh<'a> {
atlas: guillotiere::SimpleAtlasAllocator, atlas: guillotiere::SimpleAtlasAllocator,
col_lights_size: Vec2<u16>, col_lights_size: Vec2<u32>,
max_size: guillotiere::Size, max_size: guillotiere::Size,
suspended: Vec<Box<SuspendedMesh<'a>>>, suspended: Vec<Box<SuspendedMesh<'a>>>,
} }
@ -123,7 +121,7 @@ impl<'a> GreedyMesh<'a> {
small_size_threshold, small_size_threshold,
large_size_threshold, large_size_threshold,
}); });
let col_lights_size = Vec2::new(1u16, 1u16); let col_lights_size = Vec2::new(1, 1);
Self { Self {
atlas, atlas,
col_lights_size, col_lights_size,
@ -178,7 +176,7 @@ impl<'a> GreedyMesh<'a> {
let cur_size = self.col_lights_size; let cur_size = self.col_lights_size;
let col_lights = vec![ let col_lights = vec![
TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254)); 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); let mut col_lights_info = (col_lights, cur_size);
self.suspended.into_iter().for_each(|cont| { 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>( fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FG, FO, FS, FP, FT>(
atlas: &mut guillotiere::SimpleAtlasAllocator, atlas: &mut guillotiere::SimpleAtlasAllocator,
col_lights_size: &mut Vec2<u16>, col_lights_size: &mut Vec2<u32>,
max_size: guillotiere::Size, max_size: guillotiere::Size,
GreedyConfig { GreedyConfig {
mut data, mut data,
@ -430,7 +428,7 @@ fn add_to_atlas(
norm: Vec3<i16>, norm: Vec3<i16>,
faces_forward: bool, faces_forward: bool,
max_size: guillotiere::Size, max_size: guillotiere::Size,
cur_size: &mut Vec2<u16>, cur_size: &mut Vec2<u32>,
) -> guillotiere::Rectangle { ) -> guillotiere::Rectangle {
// TODO: Check this conversion. // TODO: Check this conversion.
let atlas_rect; 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 // a u16 and we never grew the atlas, meaning all valid coordinates within the
// atlas also fit into a u16. // atlas also fit into a u16.
*cur_size = Vec2::new( *cur_size = Vec2::new(
cur_size.x.max(atlas_rect.max.x as u16), cur_size.x.max(atlas_rect.max.x as u32),
cur_size.y.max(atlas_rect.max.y as u16), cur_size.y.max(atlas_rect.max.y as u32),
); );
// NOTE: pos can be converted safely from usize to i32 because all legal block // 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_light: impl FnMut(&mut D, Vec3<i32>) -> f32,
mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32, mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32,
mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool, mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool,
mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> <<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)| { todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| {
// NOTE: Conversions are safe because width, height, and offset must be // NOTE: Conversions are safe because width, height, and offset must be
@ -520,7 +518,7 @@ fn draw_col_lights<D>(
let uv = uv.map(|e| e.map(i32::from)); let uv = uv.map(|e| e.map(i32::from));
let pos = pos + draw_delta; let pos = pos + draw_delta;
(0..height).for_each(|v| { (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) (0..width)
.zip(&mut col_lights[start..start + usize::from(width)]) .zip(&mut col_lights[start..start + usize::from(width)])
.for_each(|(u, col_light)| { .for_each(|(u, col_light)| {
@ -622,14 +620,14 @@ fn create_quad_greedy<M>(
push_quad(atlas_pos, dim, origin, draw_dim, norm, meta); 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>, atlas_pos: Vec2<u16>,
dim: Vec2<Vec2<u16>>, dim: Vec2<Vec2<u16>>,
origin: Vec3<f32>, origin: Vec3<f32>,
draw_dim: Vec2<Vec3<f32>>, draw_dim: Vec2<Vec3<f32>>,
norm: Vec3<f32>, norm: Vec3<f32>,
meta: &M, 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<O> {
Quad::new( Quad::new(
create_vertex(atlas_pos, origin, norm, meta), create_vertex(atlas_pos, origin, norm, meta),

View File

@ -2,25 +2,6 @@ pub mod greedy;
pub mod segment; pub mod segment;
pub mod terrain; pub mod terrain;
use crate::render::{self, Mesh}; use crate::render::Mesh;
pub type MeshGen<P, T, M> = ( pub type MeshGen<V, T, S, R> = (Mesh<V>, Mesh<T>, Mesh<S>, R);
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>;
}

View File

@ -1,12 +1,9 @@
use crate::{ use crate::{
mesh::{ mesh::{
greedy::{self, GreedyConfig, GreedyMesh}, greedy::{self, GreedyConfig, GreedyMesh},
MeshGen, Meshable, MeshGen,
},
render::{
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
TerrainPipeline,
}, },
render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex},
scene::math, scene::math,
}; };
use common::{ use common::{
@ -16,353 +13,315 @@ use common::{
use core::convert::TryFrom; use core::convert::TryFrom;
use vek::*; use vek::*;
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex; // /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex; // /// point).
type ParticleVertex = <ParticlePipeline as render::Pipeline>::Vertex; #[allow(clippy::or_fun_call)] // TODO: Pending review in #587
// TODO: this function name...
impl<'a: 'b, 'b, V: 'a> Meshable<FigurePipeline, &'b mut GreedyMesh<'a>> for V pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>(
where vol: V,
V: BaseVol<Vox = Cell> + ReadVol + SizedVol, (greedy, opaque_mesh, offs, scale, bone_idx): (
/* 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 = (
&'b mut GreedyMesh<'a>, &'b mut GreedyMesh<'a>,
&'b mut Mesh<Self::Pipeline>, &'b mut Mesh<TerrainVertex>,
Vec3<f32>, Vec3<f32>,
Vec3<f32>, Vec3<f32>,
u8, u8,
); ),
type TranslucentPipeline = FigurePipeline; ) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
#[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
where where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol, 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; assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
type Result = (); let max_size = greedy.max_size();
type ShadowPipeline = ShadowPipeline; // NOTE: Required because we steal two bits from the normal in the shadow uint
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool); // in order to store the bone index. The two bits are instead taken out
type TranslucentPipeline = SpritePipeline; // 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 let lower_bound = vol.lower_bound();
fn generate_mesh( let upper_bound = vol.upper_bound();
self, assert!(
(greedy, opaque_mesh, vertical_stripes): Self::Supplement, lower_bound.x <= upper_bound.x
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> { && lower_bound.y <= upper_bound.y
let max_size = greedy.max_size(); && lower_bound.z <= upper_bound.z
// 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 // NOTE: Figure sizes should be no more than 512 along each axis.
// of the atlas coordinates, which is why we "only" allow 1 << 15 per let greedy_size = upper_bound - lower_bound + 1;
// coordinate instead of 1 << 16. assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
assert!(max_size.width.max(max_size.height) < 1 << 16); // 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 get_light = |vol: &mut V, pos: Vec3<i32>| {
let upper_bound = self.upper_bound(); if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
assert!( 1.0
lower_bound.x <= upper_bound.x } else {
&& lower_bound.y <= upper_bound.y 0.0
&& lower_bound.z <= upper_bound.z }
); };
// Lower bound coordinates must fit in an i16 (which means upper bound let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
// coordinates fit as integers in a f23). let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
assert!( let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
i16::try_from(lower_bound.x).is_ok() should_draw_greedy(pos, delta, uv, |vox| {
&& i16::try_from(lower_bound.y).is_ok() vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
&& i16::try_from(lower_bound.z).is_ok(), })
"Sprite offsets should fit in i16", };
); let create_opaque = |atlas_pos, pos, norm| {
let greedy_size = upper_bound - lower_bound + 1; TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
// 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) = { greedy.push(GreedyConfig {
let (w, h, d) = (greedy_size + 2).into_tuple(); data: vol,
let flat = { draw_delta,
let vol = self; 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]; (Mesh::new(), Mesh::new(), Mesh::new(), bounds)
let mut i = 0; }
for x in -1..greedy_size.x + 1 {
for y in -1..greedy_size.y + 1 { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587
for z in -1..greedy_size.z + 1 { pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>(
let wpos = lower_bound + Vec3::new(x, y, z); vol: V,
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty()); (greedy, opaque_mesh, vertical_stripes): (
flat[i] = block; &'b mut GreedyMesh<'a>,
i += 1; &'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
} }
}; flat
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 { let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
data: flat, .get((x * h * d + y * d + z) as usize)
draw_delta, .copied()
greedy_size, {
greedy_size_cross, Some(b) => b,
get_light, None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
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(), ()) (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 where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol, 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; let max_size = greedy.max_size();
type Result = (); // NOTE: Required because we steal two bits from the normal in the shadow uint
type ShadowPipeline = ShadowPipeline; // in order to store the bone index. The two bits are instead taken out
type Supplement = &'b mut GreedyMesh<'a>; // of the atlas coordinates, which is why we "only" allow 1 << 15 per
type TranslucentPipeline = ParticlePipeline; // 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 let lower_bound = vol.lower_bound();
fn generate_mesh( let upper_bound = vol.upper_bound();
self, assert!(
greedy: Self::Supplement, lower_bound.x <= upper_bound.x
) -> MeshGen<ParticlePipeline, &'b mut GreedyMesh<'a>, Self> { && lower_bound.y <= upper_bound.y
let max_size = greedy.max_size(); && lower_bound.z <= upper_bound.z
// 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 let greedy_size = upper_bound - lower_bound + 1;
// of the atlas coordinates, which is why we "only" allow 1 << 15 per assert!(
// coordinate instead of 1 << 16. greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
assert!(max_size.width.max(max_size.height) < 1 << 16); "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 greedy_size_cross = greedy_size;
let upper_bound = self.upper_bound(); let draw_delta = lower_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 get_light = |vol: &mut V, pos: Vec3<i32>| {
let draw_delta = lower_bound; 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>| { let mut opaque_mesh = Mesh::new();
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { greedy.push(GreedyConfig {
1.0 data: vol,
} else { draw_delta,
0.0 greedy_size,
} greedy_size_cross,
}; get_light,
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0; get_glow,
let get_color = |vol: &mut V, pos: Vec3<i32>| { get_opacity,
vol.get(pos) should_draw,
.ok() push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
.and_then(|vox| vox.get_color()) opaque_mesh.push_quad(greedy::create_quad(
.unwrap_or(Rgb::zero()) atlas_origin,
}; dim,
let get_opacity = origin,
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty()); draw_dim,
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| { norm,
should_draw_greedy(pos, delta, uv, |vox| { meta,
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
}) ));
}; },
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(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(); (opaque_mesh, Mesh::new(), 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(), ())
}
} }
fn should_draw_greedy( fn should_draw_greedy(

View File

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

View File

@ -1,7 +1,6 @@
use bytemuck::Pod; use bytemuck::Pod;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
#[derive(Clone)]
pub struct Buffer<T: Copy + Pod> { pub struct Buffer<T: Copy + Pod> {
pub buf: wgpu::Buffer, pub buf: wgpu::Buffer,
// bytes // bytes
@ -10,7 +9,7 @@ pub struct Buffer<T: Copy + Pod> {
} }
impl<T: Copy + Pod> Buffer<T> { 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 { Self {
buf: device.create_buffer(&wgpu::BufferDescriptor { buf: device.create_buffer(&wgpu::BufferDescriptor {
label: None, 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 { pub fn new_with_data(device: &wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self {
let contents = data.as_bytes(); let contents = bytemuck::cast_slice(data);
Self { Self {
buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor { buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -37,15 +36,9 @@ impl<T: Copy + Pod> Buffer<T> {
} }
} }
pub fn update( pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
offset: usize,
) {
if !vals.is_empty() { 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 /// 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 /// information used in the rendering process that does not change throughout a
/// single render pass. /// single render pass.
#[derive(Clone)]
pub struct Consts<T: Copy + Pod> { pub struct Consts<T: Copy + Pod> {
buf: Buffer<T>, buf: Buffer<T>,
} }
impl<T: Copy + Pod> Consts<T> { impl<T: Copy + Pod> Consts<T> {
/// Create a new `Const<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 { Self {
buf: Buffer::new(device, len, wgpu::BufferUsage::UNIFORM), buf: Buffer::new(device, len, wgpu::BufferUsage::UNIFORM),
} }
} }
/// Update the GPU-side value represented by this constant handle. /// Update the GPU-side value represented by this constant handle.
pub fn update( pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
offset: usize,
) {
self.buf.update(device, queue, vals, offset) 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; use bytemuck::Pod;
/// Represents a mesh that has been sent to the GPU. /// Represents a mesh that has been sent to the GPU.
#[derive(Clone)]
pub struct Instances<T: Copy + Pod> { pub struct Instances<T: Copy + Pod> {
buf: Buffer<T>, buf: Buffer<T>,
} }
impl<T: Copy + Pod> Instances<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 { Self {
buf: Buffer::new(device, len, wgpu::BufferUsage::VERTEX), 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 count(&self) -> usize { self.buf.count() }
pub fn update( pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
offset: usize,
) -> Result<(), RenderError> {
self.buf.update(device, queue, vals, offset) 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; use std::ops::Range;
/// Represents a mesh that has been sent to the GPU. /// 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 { Self {
vbuf: Buffer::new(device, size, wgpu::BufferUsage::VERTEX), vbuf: Buffer::new(device, size, wgpu::BufferUsage::VERTEX),
} }
@ -45,10 +45,10 @@ impl<V: Vertex> Model<V> {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
mesh: &Mesh<V>, mesh: &Mesh<V>,
offset: usize, offset: u64,
) -> Result<(), RenderError> { ) {
self.buf.update(device, queue, mesh.vertices(), offset) 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, terrain::Vertex,
}; };
use crate::mesh::greedy::GreedyMesh; use crate::mesh::greedy::GreedyMesh;
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals { pub struct Locals {
model_mat: [[f32; 4]; 4], model_mat: [[f32; 4]; 4],
highlight_col: [f32; 4], highlight_col: [f32; 4],
@ -19,7 +19,7 @@ pub struct Locals {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct BoneData { pub struct BoneData {
bone_mat: [[f32; 4]; 4], bone_mat: [[f32; 4]; 4],
normals_mat: [[f32; 4]; 4], normals_mat: [[f32; 4]; 4],

View File

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

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts, Renderer, Texture}; use super::super::{AaMode, GlobalsLayouts, Renderer, Texture};
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex { pub struct Vertex {
pos: [f32; 2], pos: [f32; 2],
} }
@ -39,7 +39,7 @@ pub struct LodData {
impl LodData { impl LodData {
pub fn new( pub fn new(
renderer: &mut Renderer, renderer: &mut Renderer,
map_size: Vec2<u16>, map_size: Vec2<u32>,
lod_base: &[u32], lod_base: &[u32],
lod_alt: &[u32], lod_alt: &[u32],
lod_horizon: &[u32], lod_horizon: &[u32],
@ -72,28 +72,41 @@ impl LodData {
..Default::default() ..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( let map = renderer.create_texture_with_data_raw(
&texture_info, &texture_info,
&view_info,
&sampler_info, &sampler_info,
map_size.x * 4, map_size.x * 4,
[map_size.x, map_size.y], bytemuck::cast_slice(lod_base),
lod_base.as_bytes(),
); );
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( let alt = renderer.create_texture_with_data_raw(
&texture_info, &texture_info,
&view_info,
&sampler_info, &sampler_info,
map_size.x * 4, map_size.x * 4,
[map_size.x, map_size.y], bytemuck::cast_slice(lod_base),
lod_base.as_bytes(),
); );
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( let horizon = renderer.create_texture_with_data_raw(
&texture_info, &texture_info,
&view_info,
&sampler_info, &sampler_info,
map_size.x * 4, map_size.x * 4,
[map_size.x, map_size.y], bytemuck::cast_slice(lod_base),
lod_base.as_bytes(),
); );
Self { Self {

View File

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

View File

@ -1,9 +1,9 @@
use super::super::{AaMode, GlobalsLayouts}; use super::super::{AaMode, GlobalsLayouts};
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex { pub struct Vertex {
pos: [f32; 3], pos: [f32; 3],
// ____BBBBBBBBGGGGGGGGRRRRRRRR // ____BBBBBBBBGGGGGGGGRRRRRRRR
@ -79,7 +79,7 @@ impl ParticleMode {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance { pub struct Instance {
// created_at time, so we can calculate time relativity, needed for relative animation. // 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. // 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 super::super::{AaMode, GlobalsLayouts, Mesh, Tri};
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
@ -23,7 +23,7 @@ impl Locals {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex { pub struct Vertex {
pub pos: [f32; 2], pub pos: [f32; 2],
} }

View File

@ -2,11 +2,11 @@ use super::super::{
AaMode, ColLightInfo, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout, TerrainVertex, AaMode, ColLightInfo, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout, TerrainVertex,
Texture, Texture,
}; };
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals { pub struct Locals {
shadow_matrices: [[f32; 4]; 4], shadow_matrices: [[f32; 4]; 4],
texture_mats: [[f32; 4]; 4], texture_mats: [[f32; 4]; 4],
@ -80,12 +80,23 @@ pub fn create_col_lights(
..Default::default() ..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( renderer.create_texture_with_data_raw(
&texture_info, &texture_info,
&view_info,
&sampler_info, &sampler_info,
col_lights_size.x * 4, col_lights_size.x * 4,
[col_lights_size.x, col_lights_size.y], bytemuck::cast_slice(&col_lights),
col_lights.as_bytes(),
) )
} }

View File

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

View File

@ -1,10 +1,10 @@
use super::super::{AaMode, GlobalsLayouts, TerrainLayout}; use super::super::{AaMode, GlobalsLayouts, TerrainLayout};
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use core::fmt; use core::fmt;
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex { pub struct Vertex {
pos: [f32; 3], pos: [f32; 3],
// Because we try to restrict terrain sprite data to a 128×128 block // Because we try to restrict terrain sprite data to a 128×128 block
@ -70,7 +70,7 @@ impl Vertex {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance { pub struct Instance {
pos_ori: u32, pos_ori: u32,
inst_mat0: [f32; 4], inst_mat0: [f32; 4],
@ -122,7 +122,7 @@ impl Default for Instance {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals { pub struct Locals {
// Each matrix performs rotatation, translation, and scaling, relative to the sprite // Each matrix performs rotatation, translation, and scaling, relative to the sprite
// origin, for all sprite instances. The matrix will be in an array indexed by the // 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 super::super::{AaMode, GlobalsLayouts};
use bytemuck::Pod; use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex { pub struct Vertex {
pos_norm: u32, pos_norm: u32,
atlas_pos: u32, atlas_pos: u32,
@ -118,7 +118,7 @@ impl Vertex {
self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27); 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; use std::mem;
wgpu::VertexBufferDescriptor { wgpu::VertexBufferDescriptor {
stride: mem::size_of::<Self>() as wgpu::BufferAddress, stride: mem::size_of::<Self>() as wgpu::BufferAddress,
@ -129,7 +129,7 @@ impl Vertex {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Pod)] #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals { pub struct Locals {
model_offs: [f32; 3], model_offs: [f32; 3],
load_time: f32, load_time: f32,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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