mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
More fixes
This commit is contained in:
parent
05d97e2f2f
commit
3502699732
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6063,6 +6063,7 @@ dependencies = [
|
||||
name = "veloren-voxygen-anim"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"find_folder",
|
||||
"lazy_static",
|
||||
"libloading 0.7.0",
|
||||
|
@ -20,3 +20,4 @@ libloading = {version = "0.7", optional = true}
|
||||
notify = {version = "5.0.0-pre.2", optional = true}
|
||||
tracing = {version = "0.1", optional = true}
|
||||
vek = {version = "=0.14.1", features = ["serde"]}
|
||||
bytemuck = { version="1.4", features=["derive"] }
|
||||
|
@ -75,16 +75,19 @@ pub use dyn_lib::init;
|
||||
use std::ffi::CStr;
|
||||
|
||||
use self::vek::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
type MatRaw = [[f32; 4]; 4];
|
||||
|
||||
pub type FigureBoneData = (MatRaw, MatRaw);
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
|
||||
pub struct FigureBoneData(pub MatRaw, pub MatRaw);
|
||||
|
||||
pub const MAX_BONE_COUNT: usize = 16;
|
||||
|
||||
fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
|
||||
let normal = mat.map_cols(Vec4::normalized);
|
||||
(mat.into_col_arrays(), normal.into_col_arrays())
|
||||
FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
|
||||
}
|
||||
|
||||
pub type Bone = Transform<f32, f32, f32>;
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline};
|
||||
use crate::render::{mesh::Quad, ColLightInfo, TerrainVertex, Vertex};
|
||||
use common_base::span;
|
||||
use vek::*;
|
||||
|
||||
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
|
||||
|
||||
type TodoRect = (
|
||||
Vec3<i32>,
|
||||
Vec2<Vec3<u16>>,
|
||||
@ -84,7 +82,7 @@ pub type SuspendedMesh<'a> = dyn for<'r> FnOnce(&'r mut ColLightInfo) + 'a;
|
||||
/// For an explanation of why we want this, see `SuspendedMesh`.
|
||||
pub struct GreedyMesh<'a> {
|
||||
atlas: guillotiere::SimpleAtlasAllocator,
|
||||
col_lights_size: Vec2<u16>,
|
||||
col_lights_size: Vec2<u32>,
|
||||
max_size: guillotiere::Size,
|
||||
suspended: Vec<Box<SuspendedMesh<'a>>>,
|
||||
}
|
||||
@ -123,7 +121,7 @@ impl<'a> GreedyMesh<'a> {
|
||||
small_size_threshold,
|
||||
large_size_threshold,
|
||||
});
|
||||
let col_lights_size = Vec2::new(1u16, 1u16);
|
||||
let col_lights_size = Vec2::new(1, 1);
|
||||
Self {
|
||||
atlas,
|
||||
col_lights_size,
|
||||
@ -178,7 +176,7 @@ impl<'a> GreedyMesh<'a> {
|
||||
let cur_size = self.col_lights_size;
|
||||
let col_lights = vec![
|
||||
TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254));
|
||||
usize::from(cur_size.x) * usize::from(cur_size.y)
|
||||
cur_size.x as usize * cur_size.y as usize
|
||||
];
|
||||
let mut col_lights_info = (col_lights, cur_size);
|
||||
self.suspended.into_iter().for_each(|cont| {
|
||||
@ -192,7 +190,7 @@ impl<'a> GreedyMesh<'a> {
|
||||
|
||||
fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FG, FO, FS, FP, FT>(
|
||||
atlas: &mut guillotiere::SimpleAtlasAllocator,
|
||||
col_lights_size: &mut Vec2<u16>,
|
||||
col_lights_size: &mut Vec2<u32>,
|
||||
max_size: guillotiere::Size,
|
||||
GreedyConfig {
|
||||
mut data,
|
||||
@ -430,7 +428,7 @@ fn add_to_atlas(
|
||||
norm: Vec3<i16>,
|
||||
faces_forward: bool,
|
||||
max_size: guillotiere::Size,
|
||||
cur_size: &mut Vec2<u16>,
|
||||
cur_size: &mut Vec2<u32>,
|
||||
) -> guillotiere::Rectangle {
|
||||
// TODO: Check this conversion.
|
||||
let atlas_rect;
|
||||
@ -475,8 +473,8 @@ fn add_to_atlas(
|
||||
// a u16 and we never grew the atlas, meaning all valid coordinates within the
|
||||
// atlas also fit into a u16.
|
||||
*cur_size = Vec2::new(
|
||||
cur_size.x.max(atlas_rect.max.x as u16),
|
||||
cur_size.y.max(atlas_rect.max.y as u16),
|
||||
cur_size.x.max(atlas_rect.max.x as u32),
|
||||
cur_size.y.max(atlas_rect.max.y as u32),
|
||||
);
|
||||
|
||||
// NOTE: pos can be converted safely from usize to i32 because all legal block
|
||||
@ -507,7 +505,7 @@ fn draw_col_lights<D>(
|
||||
mut get_light: impl FnMut(&mut D, Vec3<i32>) -> f32,
|
||||
mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32,
|
||||
mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool,
|
||||
mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType,
|
||||
mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> [u8; 4],
|
||||
) {
|
||||
todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| {
|
||||
// NOTE: Conversions are safe because width, height, and offset must be
|
||||
@ -520,7 +518,7 @@ fn draw_col_lights<D>(
|
||||
let uv = uv.map(|e| e.map(i32::from));
|
||||
let pos = pos + draw_delta;
|
||||
(0..height).for_each(|v| {
|
||||
let start = usize::from(cur_size.x) * usize::from(top + v) + usize::from(left);
|
||||
let start = cur_size.x as usize * usize::from(top + v) + usize::from(left);
|
||||
(0..width)
|
||||
.zip(&mut col_lights[start..start + usize::from(width)])
|
||||
.for_each(|(u, col_light)| {
|
||||
@ -622,14 +620,14 @@ fn create_quad_greedy<M>(
|
||||
push_quad(atlas_pos, dim, origin, draw_dim, norm, meta);
|
||||
}
|
||||
|
||||
pub fn create_quad<O: render::Pipeline, M>(
|
||||
pub fn create_quad<O: Vertex, M>(
|
||||
atlas_pos: Vec2<u16>,
|
||||
dim: Vec2<Vec2<u16>>,
|
||||
origin: Vec3<f32>,
|
||||
draw_dim: Vec2<Vec3<f32>>,
|
||||
norm: Vec3<f32>,
|
||||
meta: &M,
|
||||
create_vertex: impl Fn(Vec2<u16>, Vec3<f32>, Vec3<f32>, &M) -> O::Vertex,
|
||||
create_vertex: impl Fn(Vec2<u16>, Vec3<f32>, Vec3<f32>, &M) -> O,
|
||||
) -> Quad<O> {
|
||||
Quad::new(
|
||||
create_vertex(atlas_pos, origin, norm, meta),
|
||||
|
@ -2,25 +2,6 @@ pub mod greedy;
|
||||
pub mod segment;
|
||||
pub mod terrain;
|
||||
|
||||
use crate::render::{self, Mesh};
|
||||
use crate::render::Mesh;
|
||||
|
||||
pub type MeshGen<P, T, M> = (
|
||||
Mesh<<M as Meshable<P, T>>::Pipeline>,
|
||||
Mesh<<M as Meshable<P, T>>::TranslucentPipeline>,
|
||||
Mesh<<M as Meshable<P, T>>::ShadowPipeline>,
|
||||
<M as Meshable<P, T>>::Result,
|
||||
);
|
||||
|
||||
/// FIXME: Remove this whole trait at some point. This "abstraction" is never
|
||||
/// abstracted over, and is organized completely differently from how we
|
||||
/// actually mesh things nowadays.
|
||||
pub trait Meshable<P: render::Pipeline, T> {
|
||||
type Pipeline: render::Pipeline;
|
||||
type TranslucentPipeline: render::Pipeline;
|
||||
type ShadowPipeline: render::Pipeline;
|
||||
type Supplement;
|
||||
type Result;
|
||||
|
||||
// Generate meshes - one opaque, one translucent, one shadow
|
||||
fn generate_mesh(self, supp: Self::Supplement) -> MeshGen<P, T, Self>;
|
||||
}
|
||||
pub type MeshGen<V, T, S, R> = (Mesh<V>, Mesh<T>, Mesh<S>, R);
|
||||
|
@ -1,12 +1,9 @@
|
||||
use crate::{
|
||||
mesh::{
|
||||
greedy::{self, GreedyConfig, GreedyMesh},
|
||||
MeshGen, Meshable,
|
||||
},
|
||||
render::{
|
||||
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
|
||||
TerrainPipeline,
|
||||
MeshGen,
|
||||
},
|
||||
render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex},
|
||||
scene::math,
|
||||
};
|
||||
use common::{
|
||||
@ -16,353 +13,315 @@ use common::{
|
||||
use core::convert::TryFrom;
|
||||
use vek::*;
|
||||
|
||||
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
|
||||
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
|
||||
type ParticleVertex = <ParticlePipeline as render::Pipeline>::Vertex;
|
||||
|
||||
impl<'a: 'b, 'b, V: 'a> Meshable<FigurePipeline, &'b mut GreedyMesh<'a>> for V
|
||||
where
|
||||
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
|
||||
/* TODO: Use VolIterator instead of manually iterating
|
||||
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
|
||||
* &'a V: BaseVol<Vox=Cell>, */
|
||||
{
|
||||
type Pipeline = TerrainPipeline;
|
||||
type Result = math::Aabb<f32>;
|
||||
type ShadowPipeline = ShadowPipeline;
|
||||
/// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
|
||||
/// point).
|
||||
type Supplement = (
|
||||
// /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
|
||||
// /// point).
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
// TODO: this function name...
|
||||
pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>(
|
||||
vol: V,
|
||||
(greedy, opaque_mesh, offs, scale, bone_idx): (
|
||||
&'b mut GreedyMesh<'a>,
|
||||
&'b mut Mesh<Self::Pipeline>,
|
||||
&'b mut Mesh<TerrainVertex>,
|
||||
Vec3<f32>,
|
||||
Vec3<f32>,
|
||||
u8,
|
||||
);
|
||||
type TranslucentPipeline = FigurePipeline;
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
fn generate_mesh(
|
||||
self,
|
||||
(greedy, opaque_mesh, offs, scale, bone_idx): Self::Supplement,
|
||||
) -> MeshGen<FigurePipeline, &'b mut GreedyMesh<'a>, Self> {
|
||||
assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
|
||||
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 15);
|
||||
|
||||
let lower_bound = self.lower_bound();
|
||||
let upper_bound = self.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
// NOTE: Figure sizes should be no more than 512 along each axis.
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = lower_bound;
|
||||
|
||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
|
||||
let get_opacity =
|
||||
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
|
||||
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy(pos, delta, uv, |vox| {
|
||||
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
|
||||
})
|
||||
};
|
||||
let create_opaque = |atlas_pos, pos, norm| {
|
||||
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
|
||||
};
|
||||
|
||||
greedy.push(GreedyConfig {
|
||||
data: self,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
|
||||
));
|
||||
},
|
||||
make_face_texel: |vol: &mut V, pos, light, _| {
|
||||
let cell = vol.get(pos).ok();
|
||||
let (glowy, shiny) = cell
|
||||
.map(|c| (c.is_glowy(), c.is_shiny()))
|
||||
.unwrap_or_default();
|
||||
let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero());
|
||||
TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
|
||||
},
|
||||
});
|
||||
let bounds = math::Aabb {
|
||||
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
|
||||
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
|
||||
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
|
||||
}
|
||||
.made_valid();
|
||||
|
||||
(Mesh::new(), Mesh::new(), Mesh::new(), bounds)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a: 'b, 'b, V: 'a> Meshable<SpritePipeline, &'b mut GreedyMesh<'a>> for V
|
||||
),
|
||||
) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
|
||||
where
|
||||
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
|
||||
/* TODO: Use VolIterator instead of manually iterating
|
||||
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
|
||||
* &'a V: BaseVol<Vox=Cell>, */
|
||||
{
|
||||
type Pipeline = SpritePipeline;
|
||||
type Result = ();
|
||||
type ShadowPipeline = ShadowPipeline;
|
||||
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool);
|
||||
type TranslucentPipeline = SpritePipeline;
|
||||
assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 15);
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
fn generate_mesh(
|
||||
self,
|
||||
(greedy, opaque_mesh, vertical_stripes): Self::Supplement,
|
||||
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> {
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||
let lower_bound = vol.lower_bound();
|
||||
let upper_bound = vol.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
// NOTE: Figure sizes should be no more than 512 along each axis.
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = lower_bound;
|
||||
|
||||
let lower_bound = self.lower_bound();
|
||||
let upper_bound = self.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
// Lower bound coordinates must fit in an i16 (which means upper bound
|
||||
// coordinates fit as integers in a f23).
|
||||
assert!(
|
||||
i16::try_from(lower_bound.x).is_ok()
|
||||
&& i16::try_from(lower_bound.y).is_ok()
|
||||
&& i16::try_from(lower_bound.z).is_ok(),
|
||||
"Sprite offsets should fit in i16",
|
||||
);
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
// TODO: Should this be 16, 16, 64?
|
||||
assert!(
|
||||
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
|
||||
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
|
||||
greedy_size - 1
|
||||
);
|
||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
|
||||
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
|
||||
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy(pos, delta, uv, |vox| {
|
||||
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
|
||||
})
|
||||
};
|
||||
let create_opaque = |atlas_pos, pos, norm| {
|
||||
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
|
||||
};
|
||||
|
||||
let (flat, flat_get) = {
|
||||
let (w, h, d) = (greedy_size + 2).into_tuple();
|
||||
let flat = {
|
||||
let vol = self;
|
||||
greedy.push(GreedyConfig {
|
||||
data: vol,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
|
||||
));
|
||||
},
|
||||
make_face_texel: |vol: &mut V, pos, light, _| {
|
||||
let cell = vol.get(pos).ok();
|
||||
let (glowy, shiny) = cell
|
||||
.map(|c| (c.is_glowy(), c.is_shiny()))
|
||||
.unwrap_or_default();
|
||||
let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero());
|
||||
TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
|
||||
},
|
||||
});
|
||||
let bounds = math::Aabb {
|
||||
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
|
||||
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
|
||||
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
|
||||
}
|
||||
.made_valid();
|
||||
|
||||
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
|
||||
let mut i = 0;
|
||||
for x in -1..greedy_size.x + 1 {
|
||||
for y in -1..greedy_size.y + 1 {
|
||||
for z in -1..greedy_size.z + 1 {
|
||||
let wpos = lower_bound + Vec3::new(x, y, z);
|
||||
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
|
||||
flat[i] = block;
|
||||
i += 1;
|
||||
}
|
||||
(Mesh::new(), Mesh::new(), Mesh::new(), bounds)
|
||||
}
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>(
|
||||
vol: V,
|
||||
(greedy, opaque_mesh, vertical_stripes): (
|
||||
&'b mut GreedyMesh<'a>,
|
||||
&'b mut Mesh<SpriteVertex>,
|
||||
bool,
|
||||
),
|
||||
) -> MeshGen<SpriteVertex, SpriteVertex, TerrainVertex, ()>
|
||||
where
|
||||
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
|
||||
{
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||
|
||||
let lower_bound = vol.lower_bound();
|
||||
let upper_bound = vol.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
// Lower bound coordinates must fit in an i16 (which means upper bound
|
||||
// coordinates fit as integers in a f23).
|
||||
assert!(
|
||||
i16::try_from(lower_bound.x).is_ok()
|
||||
&& i16::try_from(lower_bound.y).is_ok()
|
||||
&& i16::try_from(lower_bound.z).is_ok(),
|
||||
"Sprite offsets should fit in i16",
|
||||
);
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
// TODO: Should this be 16, 16, 64?
|
||||
assert!(
|
||||
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
|
||||
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
|
||||
greedy_size - 1
|
||||
);
|
||||
|
||||
let (flat, flat_get) = {
|
||||
let (w, h, d) = (greedy_size + 2).into_tuple();
|
||||
let flat = {
|
||||
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
|
||||
let mut i = 0;
|
||||
for x in -1..greedy_size.x + 1 {
|
||||
for y in -1..greedy_size.y + 1 {
|
||||
for z in -1..greedy_size.z + 1 {
|
||||
let wpos = lower_bound + Vec3::new(x, y, z);
|
||||
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
|
||||
flat[i] = block;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
flat
|
||||
};
|
||||
|
||||
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
|
||||
.get((x * h * d + y * d + z) as usize)
|
||||
.copied()
|
||||
{
|
||||
Some(b) => b,
|
||||
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
|
||||
};
|
||||
|
||||
(flat, flat_get)
|
||||
};
|
||||
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = Vec3::new(1, 1, 1);
|
||||
|
||||
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
|
||||
if flat_get(flat, pos).is_empty() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
|
||||
let get_color = move |flat: &mut _, pos: Vec3<i32>| {
|
||||
flat_get(flat, pos).get_color().unwrap_or(Rgb::zero())
|
||||
};
|
||||
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
|
||||
let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
|
||||
};
|
||||
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
|
||||
// position, pos + mesh_delta, is guaranteed to fit in an f32).
|
||||
let mesh_delta = lower_bound.as_::<f32>();
|
||||
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
|
||||
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
|
||||
flat
|
||||
};
|
||||
|
||||
greedy.push(GreedyConfig {
|
||||
data: flat,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
|
||||
));
|
||||
},
|
||||
make_face_texel: move |flat: &mut _, pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
|
||||
},
|
||||
});
|
||||
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
|
||||
.get((x * h * d + y * d + z) as usize)
|
||||
.copied()
|
||||
{
|
||||
Some(b) => b,
|
||||
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
|
||||
};
|
||||
|
||||
(Mesh::new(), Mesh::new(), Mesh::new(), ())
|
||||
}
|
||||
(flat, flat_get)
|
||||
};
|
||||
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = Vec3::new(1, 1, 1);
|
||||
|
||||
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
|
||||
if flat_get(flat, pos).is_empty() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
|
||||
let get_color =
|
||||
move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).get_color().unwrap_or(Rgb::zero());
|
||||
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
|
||||
let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
|
||||
};
|
||||
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
|
||||
// position, pos + mesh_delta, is guaranteed to fit in an f32).
|
||||
let mesh_delta = lower_bound.as_::<f32>();
|
||||
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
|
||||
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
|
||||
};
|
||||
|
||||
greedy.push(GreedyConfig {
|
||||
data: flat,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
|
||||
));
|
||||
},
|
||||
make_face_texel: move |flat: &mut _, pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
|
||||
},
|
||||
});
|
||||
|
||||
(Mesh::new(), Mesh::new(), Mesh::new(), ())
|
||||
}
|
||||
|
||||
impl<'a: 'b, 'b, V: 'a> Meshable<ParticlePipeline, &'b mut GreedyMesh<'a>> for V
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>(
|
||||
vol: V,
|
||||
greedy: &'b mut GreedyMesh<'a>,
|
||||
) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()>
|
||||
where
|
||||
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
|
||||
/* TODO: Use VolIterator instead of manually iterating
|
||||
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
|
||||
* &'a V: BaseVol<Vox=Cell>, */
|
||||
{
|
||||
type Pipeline = ParticlePipeline;
|
||||
type Result = ();
|
||||
type ShadowPipeline = ShadowPipeline;
|
||||
type Supplement = &'b mut GreedyMesh<'a>;
|
||||
type TranslucentPipeline = ParticlePipeline;
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
fn generate_mesh(
|
||||
self,
|
||||
greedy: Self::Supplement,
|
||||
) -> MeshGen<ParticlePipeline, &'b mut GreedyMesh<'a>, Self> {
|
||||
let max_size = greedy.max_size();
|
||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||
// in order to store the bone index. The two bits are instead taken out
|
||||
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
|
||||
// coordinate instead of 1 << 16.
|
||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||
let lower_bound = vol.lower_bound();
|
||||
let upper_bound = vol.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
assert!(
|
||||
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
|
||||
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
|
||||
greedy_size - 1
|
||||
);
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
|
||||
let lower_bound = self.lower_bound();
|
||||
let upper_bound = self.upper_bound();
|
||||
assert!(
|
||||
lower_bound.x <= upper_bound.x
|
||||
&& lower_bound.y <= upper_bound.y
|
||||
&& lower_bound.z <= upper_bound.z
|
||||
);
|
||||
let greedy_size = upper_bound - lower_bound + 1;
|
||||
assert!(
|
||||
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
|
||||
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
|
||||
greedy_size - 1
|
||||
);
|
||||
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||
// into u16 which is safe to cast to usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = lower_bound;
|
||||
|
||||
let greedy_size_cross = greedy_size;
|
||||
let draw_delta = lower_bound;
|
||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
|
||||
let get_color = |vol: &mut V, pos: Vec3<i32>| {
|
||||
vol.get(pos)
|
||||
.ok()
|
||||
.and_then(|vox| vox.get_color())
|
||||
.unwrap_or(Rgb::zero())
|
||||
};
|
||||
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
|
||||
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy(pos, delta, uv, |vox| {
|
||||
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
|
||||
})
|
||||
};
|
||||
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
|
||||
|
||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
|
||||
let get_color = |vol: &mut V, pos: Vec3<i32>| {
|
||||
vol.get(pos)
|
||||
.ok()
|
||||
.and_then(|vox| vox.get_color())
|
||||
.unwrap_or(Rgb::zero())
|
||||
};
|
||||
let get_opacity =
|
||||
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
|
||||
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
|
||||
should_draw_greedy(pos, delta, uv, |vox| {
|
||||
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
|
||||
})
|
||||
};
|
||||
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
greedy.push(GreedyConfig {
|
||||
data: vol,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
|
||||
));
|
||||
},
|
||||
make_face_texel: move |vol: &mut V, pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(vol, pos))
|
||||
},
|
||||
});
|
||||
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
greedy.push(GreedyConfig {
|
||||
data: self,
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
|
||||
));
|
||||
},
|
||||
make_face_texel: move |vol: &mut V, pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(vol, pos))
|
||||
},
|
||||
});
|
||||
|
||||
(opaque_mesh, Mesh::new(), Mesh::new(), ())
|
||||
}
|
||||
(opaque_mesh, Mesh::new(), Mesh::new(), ())
|
||||
}
|
||||
|
||||
fn should_draw_greedy(
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
mesh::{
|
||||
greedy::{self, GreedyConfig, GreedyMesh},
|
||||
MeshGen, Meshable,
|
||||
MeshGen,
|
||||
},
|
||||
render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline},
|
||||
render::{ColLightInfo, FluidVertex, Mesh, TerrainVertex},
|
||||
scene::terrain::BlocksOfInterest,
|
||||
};
|
||||
use common::{
|
||||
@ -17,9 +17,6 @@ use std::{collections::VecDeque, fmt::Debug, sync::Arc};
|
||||
use tracing::error;
|
||||
use vek::*;
|
||||
|
||||
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
|
||||
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum FaceKind {
|
||||
/// Opaque face that is facing something non-opaque; either
|
||||
@ -225,243 +222,234 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
|
||||
Meshable<TerrainPipeline, FluidPipeline> for &'a VolGrid2d<V>
|
||||
{
|
||||
type Pipeline = TerrainPipeline;
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Result = (
|
||||
#[allow(clippy::collapsible_if)]
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[allow(clippy::needless_range_loop)] // TODO: Pending review in #587
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>(
|
||||
vol: &'a VolGrid2d<V>,
|
||||
(range, max_texture_size): (Aabb<i32>, Vec2<u16>),
|
||||
(range, max_texture_size, _boi): (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest),
|
||||
) -> MeshGen<
|
||||
TerrainVertex,
|
||||
FluidVertex,
|
||||
TerrainVertex,
|
||||
(
|
||||
Aabb<f32>,
|
||||
ColLightInfo,
|
||||
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
|
||||
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
|
||||
),
|
||||
> {
|
||||
span!(
|
||||
_guard,
|
||||
"generate_mesh",
|
||||
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
|
||||
);
|
||||
type ShadowPipeline = ShadowPipeline;
|
||||
type Supplement = (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest);
|
||||
type TranslucentPipeline = FluidPipeline;
|
||||
|
||||
#[allow(clippy::collapsible_if)]
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[allow(clippy::needless_range_loop)] // TODO: Pending review in #587
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
fn generate_mesh(
|
||||
self,
|
||||
(range, max_texture_size, _boi): Self::Supplement,
|
||||
) -> MeshGen<TerrainPipeline, FluidPipeline, Self> {
|
||||
span!(
|
||||
_guard,
|
||||
"generate_mesh",
|
||||
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
|
||||
);
|
||||
// Find blocks that should glow
|
||||
// TODO: Search neighbouring chunks too!
|
||||
// let glow_blocks = boi.lights
|
||||
// .iter()
|
||||
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
|
||||
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
|
||||
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
|
||||
|
||||
// Find blocks that should glow
|
||||
// TODO: Search neighbouring chunks too!
|
||||
// let glow_blocks = boi.lights
|
||||
// .iter()
|
||||
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
|
||||
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
|
||||
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
|
||||
let mut glow_blocks = Vec::new();
|
||||
|
||||
let mut glow_blocks = Vec::new();
|
||||
|
||||
// TODO: This expensive, use BlocksOfInterest instead
|
||||
let mut volume = self.cached();
|
||||
for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST {
|
||||
for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST {
|
||||
for z in -1..range.size().d + 1 {
|
||||
let wpos = range.min + Vec3::new(x, y, z);
|
||||
volume
|
||||
.get(wpos)
|
||||
.ok()
|
||||
.and_then(|b| b.get_glow())
|
||||
.map(|glow| glow_blocks.push((wpos, glow)));
|
||||
}
|
||||
// TODO: This expensive, use BlocksOfInterest instead
|
||||
let mut volume = self.cached();
|
||||
for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST {
|
||||
for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST {
|
||||
for z in -1..range.size().d + 1 {
|
||||
let wpos = range.min + Vec3::new(x, y, z);
|
||||
volume
|
||||
.get(wpos)
|
||||
.ok()
|
||||
.and_then(|b| b.get_glow())
|
||||
.map(|glow| glow_blocks.push((wpos, glow)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
|
||||
let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty());
|
||||
let glow = calc_light(false, 0, range, self, glow_blocks.into_iter());
|
||||
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
|
||||
let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty());
|
||||
let glow = calc_light(false, 0, range, self, glow_blocks.into_iter());
|
||||
|
||||
let mut opaque_limits = None::<Limits>;
|
||||
let mut fluid_limits = None::<Limits>;
|
||||
let mut air_limits = None::<Limits>;
|
||||
let flat_get = {
|
||||
span!(_guard, "copy to flat array");
|
||||
let (w, h, d) = range.size().into_tuple();
|
||||
// z can range from -1..range.size().d + 1
|
||||
let d = d + 2;
|
||||
let flat = {
|
||||
let mut volume = self.cached();
|
||||
|
||||
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
|
||||
|
||||
// TODO: Once we can manage it sensibly, consider using something like
|
||||
// Option<Block> instead of just assuming air.
|
||||
let mut flat = vec![AIR; (w * h * d) as usize];
|
||||
let mut i = 0;
|
||||
for x in 0..range.size().w {
|
||||
for y in 0..range.size().h {
|
||||
for z in -1..range.size().d + 1 {
|
||||
let wpos = range.min + Vec3::new(x, y, z);
|
||||
let block = volume
|
||||
.get(wpos)
|
||||
.map(|b| *b)
|
||||
// TODO: Replace with None or some other more reasonable value,
|
||||
// since it's not clear this will work properly with liquid.
|
||||
.unwrap_or(AIR);
|
||||
if block.is_opaque() {
|
||||
opaque_limits = opaque_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
} else if block.is_liquid() {
|
||||
fluid_limits = fluid_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
} else {
|
||||
// Assume air
|
||||
air_limits = air_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
};
|
||||
flat[i] = block;
|
||||
i += 1;
|
||||
}
|
||||
let mut opaque_limits = None::<Limits>;
|
||||
let mut fluid_limits = None::<Limits>;
|
||||
let mut air_limits = None::<Limits>;
|
||||
let flat_get = {
|
||||
span!(_guard, "copy to flat array");
|
||||
let (w, h, d) = range.size().into_tuple();
|
||||
// z can range from -1..range.size().d + 1
|
||||
let d = d + 2;
|
||||
let flat = {
|
||||
let mut volume = vol.cached();
|
||||
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
|
||||
// TODO: Once we can manage it sensibly, consider using something like
|
||||
// Option<Block> instead of just assuming air.
|
||||
let mut flat = vec![AIR; (w * h * d) as usize];
|
||||
let mut i = 0;
|
||||
for x in 0..range.size().w {
|
||||
for y in 0..range.size().h {
|
||||
for z in -1..range.size().d + 1 {
|
||||
let wpos = range.min + Vec3::new(x, y, z);
|
||||
let block = volume
|
||||
.get(wpos)
|
||||
.map(|b| *b)
|
||||
// TODO: Replace with None or some other more reasonable value,
|
||||
// since it's not clear this will work properly with liquid.
|
||||
.unwrap_or(AIR);
|
||||
if block.is_opaque() {
|
||||
opaque_limits = opaque_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
} else if block.is_liquid() {
|
||||
fluid_limits = fluid_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
} else {
|
||||
// Assume air
|
||||
air_limits = air_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
};
|
||||
flat[i] = block;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
flat
|
||||
};
|
||||
|
||||
move |Vec3 { x, y, z }| {
|
||||
// z can range from -1..range.size().d + 1
|
||||
let z = z + 1;
|
||||
match flat.get((x * h * d + y * d + z) as usize).copied() {
|
||||
Some(b) => b,
|
||||
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
|
||||
}
|
||||
}
|
||||
flat
|
||||
};
|
||||
|
||||
// Constrain iterated area
|
||||
let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) {
|
||||
(Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque),
|
||||
(Some(air), Some(fluid), None) => air.intersection(fluid),
|
||||
(Some(air), None, Some(opaque)) => air.intersection(opaque),
|
||||
(None, Some(fluid), Some(opaque)) => fluid.intersection(opaque),
|
||||
// No interfaces (Note: if there are multiple fluid types this could change)
|
||||
(Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None,
|
||||
(None, None, None) => {
|
||||
error!("Impossible unless given an input AABB that has a height of zero");
|
||||
None
|
||||
},
|
||||
move |Vec3 { x, y, z }| {
|
||||
// z can range from -1..range.size().d + 1
|
||||
let z = z + 1;
|
||||
match flat.get((x * h * d + y * d + z) as usize).copied() {
|
||||
Some(b) => b,
|
||||
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
|
||||
}
|
||||
}
|
||||
.map_or((0, 0), |limits| {
|
||||
let (start, end) = limits.into_tuple();
|
||||
let start = start.max(0);
|
||||
let end = end.min(range.size().d - 1).max(start);
|
||||
(start, end)
|
||||
});
|
||||
};
|
||||
|
||||
let max_size =
|
||||
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
|
||||
assert!(z_end >= z_start);
|
||||
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
|
||||
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
|
||||
// + 14). FIXME: Make this function fallible, since the terrain
|
||||
// information might be dynamically generated which would make this hard
|
||||
// to enforce.
|
||||
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a f32.
|
||||
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
|
||||
let draw_delta = Vec3::new(1, 1, z_start);
|
||||
|
||||
let get_light = |_: &mut (), pos: Vec3<i32>| {
|
||||
if flat_get(pos).is_opaque() {
|
||||
0.0
|
||||
} else {
|
||||
light(pos + range.min)
|
||||
}
|
||||
};
|
||||
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
|
||||
let get_color =
|
||||
|_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
|
||||
let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque();
|
||||
let flat_get = |pos| flat_get(pos);
|
||||
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
|
||||
should_draw_greedy(pos, delta, flat_get)
|
||||
};
|
||||
// NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16.
|
||||
let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
|
||||
let create_opaque = |atlas_pos, pos, norm, meta| {
|
||||
TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta)
|
||||
};
|
||||
let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm);
|
||||
|
||||
let mut greedy = GreedyMesh::new(max_size);
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
let mut fluid_mesh = Mesh::new();
|
||||
greedy.push(GreedyConfig {
|
||||
data: (),
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
|
||||
FaceKind::Opaque(meta) => {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
|
||||
));
|
||||
},
|
||||
FaceKind::Fluid => {
|
||||
fluid_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
&(),
|
||||
|atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm),
|
||||
));
|
||||
},
|
||||
},
|
||||
make_face_texel: |data: &mut (), pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(data, pos))
|
||||
},
|
||||
});
|
||||
|
||||
let min_bounds = mesh_delta;
|
||||
let bounds = Aabb {
|
||||
min: min_bounds,
|
||||
max: max_bounds + min_bounds,
|
||||
};
|
||||
let (col_lights, col_lights_size) = greedy.finalize();
|
||||
|
||||
(
|
||||
opaque_mesh,
|
||||
fluid_mesh,
|
||||
Mesh::new(),
|
||||
(
|
||||
bounds,
|
||||
(col_lights, col_lights_size),
|
||||
Arc::new(light),
|
||||
Arc::new(glow),
|
||||
),
|
||||
)
|
||||
// Constrain iterated area
|
||||
let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) {
|
||||
(Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque),
|
||||
(Some(air), Some(fluid), None) => air.intersection(fluid),
|
||||
(Some(air), None, Some(opaque)) => air.intersection(opaque),
|
||||
(None, Some(fluid), Some(opaque)) => fluid.intersection(opaque),
|
||||
// No interfaces (Note: if there are multiple fluid types this could change)
|
||||
(Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None,
|
||||
(None, None, None) => {
|
||||
error!("Impossible unless given an input AABB that has a height of zero");
|
||||
None
|
||||
},
|
||||
}
|
||||
.map_or((0, 0), |limits| {
|
||||
let (start, end) = limits.into_tuple();
|
||||
let start = start.max(0);
|
||||
let end = end.min(range.size().d - 1).max(start);
|
||||
(start, end)
|
||||
});
|
||||
|
||||
let max_size =
|
||||
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
|
||||
assert!(z_end >= z_start);
|
||||
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
|
||||
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
|
||||
// + 14). FIXME: Make this function fallible, since the terrain
|
||||
// information might be dynamically generated which would make this hard
|
||||
// to enforce.
|
||||
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a f32.
|
||||
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
|
||||
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||
// which always fits into a usize.
|
||||
let greedy_size = greedy_size.as_::<usize>();
|
||||
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
|
||||
let draw_delta = Vec3::new(1, 1, z_start);
|
||||
|
||||
let get_light = |_: &mut (), pos: Vec3<i32>| {
|
||||
if flat_get(pos).is_opaque() {
|
||||
0.0
|
||||
} else {
|
||||
light(pos + range.min)
|
||||
}
|
||||
};
|
||||
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
|
||||
let get_color = |_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
|
||||
let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque();
|
||||
let flat_get = |pos| flat_get(pos);
|
||||
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
|
||||
should_draw_greedy(pos, delta, flat_get)
|
||||
};
|
||||
// NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16.
|
||||
let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
|
||||
let create_opaque =
|
||||
|atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
|
||||
let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm);
|
||||
|
||||
let mut greedy = GreedyMesh::new(max_size);
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
let mut fluid_mesh = Mesh::new();
|
||||
greedy.push(GreedyConfig {
|
||||
data: (),
|
||||
draw_delta,
|
||||
greedy_size,
|
||||
greedy_size_cross,
|
||||
get_light,
|
||||
get_glow,
|
||||
get_opacity,
|
||||
should_draw,
|
||||
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
|
||||
FaceKind::Opaque(meta) => {
|
||||
opaque_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
meta,
|
||||
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
|
||||
));
|
||||
},
|
||||
FaceKind::Fluid => {
|
||||
fluid_mesh.push_quad(greedy::create_quad(
|
||||
atlas_origin,
|
||||
dim,
|
||||
origin,
|
||||
draw_dim,
|
||||
norm,
|
||||
&(),
|
||||
|atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm),
|
||||
));
|
||||
},
|
||||
},
|
||||
make_face_texel: |data: &mut (), pos, light, glow| {
|
||||
TerrainVertex::make_col_light(light, glow, get_color(data, pos))
|
||||
},
|
||||
});
|
||||
|
||||
let min_bounds = mesh_delta;
|
||||
let bounds = Aabb {
|
||||
min: min_bounds,
|
||||
max: max_bounds + min_bounds,
|
||||
};
|
||||
let (col_lights, col_lights_size) = greedy.finalize();
|
||||
|
||||
(
|
||||
opaque_mesh,
|
||||
fluid_mesh,
|
||||
Mesh::new(),
|
||||
(
|
||||
bounds,
|
||||
(col_lights, col_lights_size),
|
||||
Arc::new(light),
|
||||
Arc::new(glow),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// NOTE: Make sure to reflect any changes to how meshing is performanced in
|
||||
|
@ -1,7 +1,6 @@
|
||||
use bytemuck::Pod;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Buffer<T: Copy + Pod> {
|
||||
pub buf: wgpu::Buffer,
|
||||
// bytes
|
||||
@ -10,7 +9,7 @@ pub struct Buffer<T: Copy + Pod> {
|
||||
}
|
||||
|
||||
impl<T: Copy + Pod> Buffer<T> {
|
||||
pub fn new(device: &mut wgpu::Device, cap: usize, usage: wgpu::BufferUsage) -> Self {
|
||||
pub fn new(device: &wgpu::Device, cap: u64, usage: wgpu::BufferUsage) -> Self {
|
||||
Self {
|
||||
buf: device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: None,
|
||||
@ -23,8 +22,8 @@ impl<T: Copy + Pod> Buffer<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_data(device: &mut wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self {
|
||||
let contents = data.as_bytes();
|
||||
pub fn new_with_data(device: &wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self {
|
||||
let contents = bytemuck::cast_slice(data);
|
||||
|
||||
Self {
|
||||
buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
@ -37,15 +36,9 @@ impl<T: Copy + Pod> Buffer<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
vals: &[T],
|
||||
offset: usize,
|
||||
) {
|
||||
pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
|
||||
if !vals.is_empty() {
|
||||
queue.write_buffer(&self.buf, offset, vals.as_bytes())
|
||||
queue.write_buffer(&self.buf, offset, bytemuck::cast_slice(vals))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,29 +4,22 @@ use bytemuck::Pod;
|
||||
/// A handle to a series of constants sitting on the GPU. This is used to hold
|
||||
/// information used in the rendering process that does not change throughout a
|
||||
/// single render pass.
|
||||
#[derive(Clone)]
|
||||
pub struct Consts<T: Copy + Pod> {
|
||||
buf: Buffer<T>,
|
||||
}
|
||||
|
||||
impl<T: Copy + Pod> Consts<T> {
|
||||
/// Create a new `Const<T>`.
|
||||
pub fn new(device: &mut wgpu::Device, len: usize) -> Self {
|
||||
pub fn new(device: &wgpu::Device, len: u64) -> Self {
|
||||
Self {
|
||||
buf: Buffer::new(device, len, wgpu::BufferUsage::UNIFORM),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the GPU-side value represented by this constant handle.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
vals: &[T],
|
||||
offset: usize,
|
||||
) {
|
||||
pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
|
||||
self.buf.update(device, queue, vals, offset)
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> &wgpu::Buffer { self.buf.buf }
|
||||
pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf }
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
use super::{buffer::Buffer, RenderError};
|
||||
use super::buffer::Buffer;
|
||||
use bytemuck::Pod;
|
||||
|
||||
/// Represents a mesh that has been sent to the GPU.
|
||||
#[derive(Clone)]
|
||||
pub struct Instances<T: Copy + Pod> {
|
||||
buf: Buffer<T>,
|
||||
}
|
||||
|
||||
impl<T: Copy + Pod> Instances<T> {
|
||||
pub fn new(device: &mut wgpu::Device, len: usize) -> Self {
|
||||
pub fn new(device: &wgpu::Device, len: u64) -> Self {
|
||||
Self {
|
||||
buf: Buffer::new(device, len, wgpu::BufferUsage::VERTEX),
|
||||
}
|
||||
@ -16,15 +15,9 @@ impl<T: Copy + Pod> Instances<T> {
|
||||
|
||||
pub fn count(&self) -> usize { self.buf.count() }
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
vals: &[T],
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, vals: &[T], offset: u64) {
|
||||
self.buf.update(device, queue, vals, offset)
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> &wgpu::Buffer { self.buf.buf }
|
||||
pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf }
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{buffer::Buffer, mesh::Mesh, RenderError, Vertex};
|
||||
use super::{buffer::Buffer, mesh::Mesh, Vertex};
|
||||
use std::ops::Range;
|
||||
|
||||
/// Represents a mesh that has been sent to the GPU.
|
||||
@ -24,7 +24,7 @@ impl<V: Vertex> Model<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic(device: &wgpu::Device, size: usize) -> Self {
|
||||
pub fn new_dynamic(device: &wgpu::Device, size: u64) -> Self {
|
||||
Self {
|
||||
vbuf: Buffer::new(device, size, wgpu::BufferUsage::VERTEX),
|
||||
}
|
||||
@ -45,10 +45,10 @@ impl<V: Vertex> Model<V> {
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
mesh: &Mesh<V>,
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
self.buf.update(device, queue, mesh.vertices(), offset)
|
||||
offset: u64,
|
||||
) {
|
||||
self.vbuf.update(device, queue, mesh.vertices(), offset)
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> &wgpu::Buffer { self.vbuf.buf }
|
||||
pub fn buf(&self) -> &wgpu::Buffer { &self.vbuf.buf }
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ use super::{
|
||||
terrain::Vertex,
|
||||
};
|
||||
use crate::mesh::greedy::GreedyMesh;
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Locals {
|
||||
model_mat: [[f32; 4]; 4],
|
||||
highlight_col: [f32; 4],
|
||||
@ -19,7 +19,7 @@ pub struct Locals {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct BoneData {
|
||||
bone_mat: [[f32; 4]; 4],
|
||||
normals_mat: [[f32; 4]; 4],
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::super::{AaMode, GlobalsLayouts};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pos_norm: u32,
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::super::{AaMode, GlobalsLayouts, Renderer, Texture};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pos: [f32; 2],
|
||||
}
|
||||
@ -39,7 +39,7 @@ pub struct LodData {
|
||||
impl LodData {
|
||||
pub fn new(
|
||||
renderer: &mut Renderer,
|
||||
map_size: Vec2<u16>,
|
||||
map_size: Vec2<u32>,
|
||||
lod_base: &[u32],
|
||||
lod_alt: &[u32],
|
||||
lod_horizon: &[u32],
|
||||
@ -72,28 +72,41 @@ impl LodData {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut view_info = wgpu::TextureViewDescriptor {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
};
|
||||
|
||||
let map = renderer.create_texture_with_data_raw(
|
||||
&texture_info,
|
||||
&view_info,
|
||||
&sampler_info,
|
||||
map_size.x * 4,
|
||||
[map_size.x, map_size.y],
|
||||
lod_base.as_bytes(),
|
||||
bytemuck::cast_slice(lod_base),
|
||||
);
|
||||
texture_info = wgpu::TextureFormat::Rg16Uint;
|
||||
texture_info.format = wgpu::TextureFormat::Rg16Uint;
|
||||
view_info.format = Some(wgpu::TextureFormat::Rg16Uint);
|
||||
let alt = renderer.create_texture_with_data_raw(
|
||||
&texture_info,
|
||||
&view_info,
|
||||
&sampler_info,
|
||||
map_size.x * 4,
|
||||
[map_size.x, map_size.y],
|
||||
lod_base.as_bytes(),
|
||||
bytemuck::cast_slice(lod_base),
|
||||
);
|
||||
texture_info = wgpu::TextureFormat::Rgba8Unorm;
|
||||
texture_info.format = wgpu::TextureFormat::Rgba8Unorm;
|
||||
view_info.format = Some(wgpu::TextureFormat::Rg16Uint);
|
||||
let horizon = renderer.create_texture_with_data_raw(
|
||||
&texture_info,
|
||||
&view_info,
|
||||
&sampler_info,
|
||||
map_size.x * 4,
|
||||
[map_size.x, map_size.y],
|
||||
lod_base.as_bytes(),
|
||||
bytemuck::cast_slice(lod_base),
|
||||
);
|
||||
|
||||
Self {
|
||||
|
@ -12,7 +12,7 @@ pub mod ui;
|
||||
|
||||
use super::Consts;
|
||||
use crate::scene::camera::CameraMode;
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use common::terrain::BlockKind;
|
||||
use vek::*;
|
||||
|
||||
@ -21,7 +21,7 @@ pub const MAX_FIGURE_SHADOW_COUNT: usize = 24;
|
||||
pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Globals {
|
||||
view_mat: [[f32; 4]; 4],
|
||||
proj_mat: [[f32; 4]; 4],
|
||||
@ -55,14 +55,14 @@ pub struct Globals {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Light {
|
||||
pos: [f32; 4],
|
||||
col: [f32; 4],
|
||||
pub pos: [f32; 4],
|
||||
pub col: [f32; 4],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Shadow {
|
||||
pos_radius: [f32; 4],
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::super::{AaMode, GlobalsLayouts};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pos: [f32; 3],
|
||||
// ____BBBBBBBBGGGGGGGGRRRRRRRR
|
||||
@ -79,7 +79,7 @@ impl ParticleMode {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Instance {
|
||||
// created_at time, so we can calculate time relativity, needed for relative animation.
|
||||
// can save 32 bits per instance, for particles that are not relatively animated.
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::super::{AaMode, GlobalsLayouts, Mesh, Tri};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
@ -23,7 +23,7 @@ impl Locals {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pub pos: [f32; 2],
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ use super::super::{
|
||||
AaMode, ColLightInfo, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout, TerrainVertex,
|
||||
Texture,
|
||||
};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Locals {
|
||||
shadow_matrices: [[f32; 4]; 4],
|
||||
texture_mats: [[f32; 4]; 4],
|
||||
@ -80,12 +80,23 @@ pub fn create_col_lights(
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let view_info = wgpu::TextureViewDescriptor {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
};
|
||||
|
||||
renderer.create_texture_with_data_raw(
|
||||
&texture_info,
|
||||
&view_info,
|
||||
&sampler_info,
|
||||
col_lights_size.x * 4,
|
||||
[col_lights_size.x, col_lights_size.y],
|
||||
col_lights.as_bytes(),
|
||||
bytemuck::cast_slice(&col_lights),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::super::{AaMode, GlobalsLayouts, Mesh, Quad};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pub pos: [f32; 3],
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use super::super::{AaMode, GlobalsLayouts, TerrainLayout};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use core::fmt;
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pos: [f32; 3],
|
||||
// Because we try to restrict terrain sprite data to a 128×128 block
|
||||
@ -70,7 +70,7 @@ impl Vertex {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Instance {
|
||||
pos_ori: u32,
|
||||
inst_mat0: [f32; 4],
|
||||
@ -122,7 +122,7 @@ impl Default for Instance {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Locals {
|
||||
// Each matrix performs rotatation, translation, and scaling, relative to the sprite
|
||||
// origin, for all sprite instances. The matrix will be in an array indexed by the
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::super::{AaMode, GlobalsLayouts};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pos_norm: u32,
|
||||
atlas_pos: u32,
|
||||
@ -118,7 +118,7 @@ impl Vertex {
|
||||
self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27);
|
||||
}
|
||||
|
||||
fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
|
||||
pub fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
|
||||
use std::mem;
|
||||
wgpu::VertexBufferDescriptor {
|
||||
stride: mem::size_of::<Self>() as wgpu::BufferAddress,
|
||||
@ -129,7 +129,7 @@ impl Vertex {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Locals {
|
||||
model_offs: [f32; 3],
|
||||
load_time: f32,
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::super::{AaMode, GlobalsLayouts, Quad, Tri};
|
||||
use bytemuck::Pod;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
pos: [f32; 2],
|
||||
uv: [f32; 2],
|
||||
@ -24,7 +24,7 @@ impl Vertex {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Locals {
|
||||
pos: [f32; 4],
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use vek::*;
|
||||
|
||||
/// A type representing data that can be converted to an immutable texture map
|
||||
/// of ColLight data (used for texture atlases created during greedy meshing).
|
||||
pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u16>);
|
||||
pub type ColLightInfo = (Vec<[u8; 4]>, Vec2<u32>);
|
||||
|
||||
/// Load from a GLSL file.
|
||||
pub struct Glsl(String);
|
||||
@ -150,11 +150,9 @@ impl assets::Compound for Shaders {
|
||||
pub struct ShadowMapRenderer {
|
||||
// directed_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
// point_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
directed_depth_stencil_view: wgpu::TextureView,
|
||||
directed_sampler: wgpu::Sampler,
|
||||
directed_depth_stencil: Texture,
|
||||
|
||||
point_depth_stencil_view: wgpu::TextureView,
|
||||
point_sampler: wgpu::Sampler,
|
||||
point_depth_stencil: Texture,
|
||||
|
||||
point_pipeline: shadow::ShadowPipeline,
|
||||
terrain_directed_pipeline: shadow::ShadowPipeline,
|
||||
@ -179,7 +177,9 @@ pub struct Layouts {
|
||||
/// rendering subsystem and contains any state necessary to interact with the
|
||||
/// GPU, along with pipeline state objects (PSOs) needed to renderer different
|
||||
/// kinds of models to the screen.
|
||||
pub struct Renderer {
|
||||
pub struct Renderer<'a> {
|
||||
window: &'a winit::window::Window,
|
||||
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
swap_chain: wgpu::SwapChain,
|
||||
@ -218,13 +218,10 @@ pub struct Renderer {
|
||||
mode: RenderMode,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
impl<'a> Renderer<'a> {
|
||||
/// Create a new `Renderer` from a variety of backend-specific components
|
||||
/// and the window targets.
|
||||
pub async fn new(
|
||||
window: &winit::window::Window,
|
||||
mode: RenderMode,
|
||||
) -> Result<Self, RenderError> {
|
||||
pub fn new(window: &'a winit::window::Window, mode: RenderMode) -> Result<Self, RenderError> {
|
||||
// Enable seamless cubemaps globally, where available--they are essentially a
|
||||
// strict improvement on regular cube maps.
|
||||
//
|
||||
@ -241,29 +238,27 @@ impl Renderer {
|
||||
#[allow(unsafe_code)]
|
||||
let surface = unsafe { instance.create_surface(window) };
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(wgpu::RequestAdapterOptionsBase {
|
||||
let adapter = futures::executor::block_on(instance.request_adapter(
|
||||
&wgpu::RequestAdapterOptionsBase {
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
compatible_surface: Some(surface),
|
||||
})
|
||||
.await
|
||||
.ok_or(RenderError::CouldNotFindAdapter)?;
|
||||
compatible_surface: Some(&surface),
|
||||
},
|
||||
))
|
||||
.ok_or(RenderError::CouldNotFindAdapter)?;
|
||||
|
||||
use wgpu::{Features, Limits};
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
wgpu::DeviceDescriptor {
|
||||
// TODO
|
||||
features: Features::DEPTH_CLAMPING | Features::ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
limits: Limits::default(),
|
||||
shader_validation: true,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let (device, queue) = futures::executor::block_on(adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
// TODO
|
||||
features: Features::DEPTH_CLAMPING | Features::ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
limits: Limits::default(),
|
||||
shader_validation: true,
|
||||
},
|
||||
None,
|
||||
))?;
|
||||
|
||||
let info = device.get_info();
|
||||
let info = adapter.get_info();
|
||||
info!(
|
||||
?info.name,
|
||||
?info.vendor,
|
||||
@ -276,8 +271,8 @@ impl Renderer {
|
||||
let sc_desc = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: dims.0,
|
||||
height: dims.1,
|
||||
width: dims.width,
|
||||
height: dims.height,
|
||||
present_mode: wgpu::PresentMode::Immediate,
|
||||
};
|
||||
|
||||
@ -285,7 +280,7 @@ impl Renderer {
|
||||
|
||||
let shadow_views = Self::create_shadow_views(
|
||||
&device,
|
||||
(dims.0, dims.1),
|
||||
(dims.width, dims.height),
|
||||
&ShadowMapMode::try_from(mode.shadow).unwrap_or_default(),
|
||||
)
|
||||
.map_err(|err| {
|
||||
@ -334,14 +329,10 @@ impl Renderer {
|
||||
point_shadow_pipeline,
|
||||
terrain_directed_shadow_pipeline,
|
||||
figure_directed_shadow_pipeline,
|
||||
) = create_pipelines(
|
||||
&device,
|
||||
&layouts & mode,
|
||||
shadow_views.is_some(),
|
||||
)?;
|
||||
) = create_pipelines(&device, &layouts, &mode, &sc_desc, shadow_views.is_some())?;
|
||||
|
||||
let (tgt_color_view, tgt_depth_stencil_view, tgt_color_pp_view, win_depth_view) =
|
||||
Self::create_rt_views(&device, (dims.0, dims.1), &mode)?;
|
||||
Self::create_rt_views(&device, (dims.width, dims.height), &mode)?;
|
||||
|
||||
let shadow_map = if let (
|
||||
Some(point_pipeline),
|
||||
@ -354,25 +345,16 @@ impl Renderer {
|
||||
figure_directed_shadow_pipeline,
|
||||
shadow_views,
|
||||
) {
|
||||
let (
|
||||
point_depth_stencil_view,
|
||||
point_res,
|
||||
point_sampler,
|
||||
directed_depth_stencil_view,
|
||||
directed_res,
|
||||
directed_sampler,
|
||||
) = shadow_views;
|
||||
let (point_depth_stencil, directed_depth_stencil) = shadow_views;
|
||||
|
||||
let layout = shadow::ShadowLayout::new(&device);
|
||||
|
||||
Some(ShadowMapRenderer {
|
||||
directed_depth_stencil_view,
|
||||
directed_sampler,
|
||||
directed_depth_stencil,
|
||||
|
||||
// point_encoder: factory.create_command_buffer().into(),
|
||||
// directed_encoder: factory.create_command_buffer().into(),
|
||||
point_depth_stencil_view,
|
||||
point_sampler,
|
||||
point_depth_stencil,
|
||||
|
||||
point_pipeline,
|
||||
terrain_directed_pipeline,
|
||||
@ -405,6 +387,8 @@ impl Renderer {
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
window,
|
||||
|
||||
device,
|
||||
queue,
|
||||
swap_chain,
|
||||
@ -472,43 +456,24 @@ impl Renderer {
|
||||
|
||||
/// Resize internal render targets to match window render target dimensions.
|
||||
pub fn on_resize(&mut self) -> Result<(), RenderError> {
|
||||
let dims = self.win_color_view.get_dimensions();
|
||||
let dims = self.window.inner_size();
|
||||
|
||||
// Avoid panics when creating texture with w,h of 0,0.
|
||||
if dims.0 != 0 && dims.1 != 0 {
|
||||
let (
|
||||
tgt_color_view,
|
||||
tgt_depth_stencil_view,
|
||||
tgt_color_pp_view,
|
||||
tgt_color_res,
|
||||
tgt_depth_res,
|
||||
tgt_color_res_pp,
|
||||
) = Self::create_rt_views(&mut self.factory, (dims.0, dims.1), &self.mode)?;
|
||||
self.tgt_color_res = tgt_color_res;
|
||||
self.tgt_depth_res = tgt_depth_res;
|
||||
self.tgt_color_res_pp = tgt_color_res_pp;
|
||||
if dims.width != 0 && dims.height != 0 {
|
||||
let (tgt_color_view, tgt_depth_stencil_view, tgt_color_pp_view, win_depth_view) =
|
||||
Self::create_rt_views(&mut self.device, (dims.width, dims.height), &self.mode)?;
|
||||
self.win_depth_view = win_depth_view;
|
||||
self.tgt_color_view = tgt_color_view;
|
||||
self.tgt_depth_stencil_view = tgt_depth_stencil_view;
|
||||
self.tgt_color_pp_view = tgt_color_pp_view;
|
||||
if let (Some(shadow_map), ShadowMode::Map(mode)) =
|
||||
(self.shadow_map.as_mut(), self.mode.shadow)
|
||||
{
|
||||
match Self::create_shadow_views(&mut self.factory, (dims.0, dims.1), &mode) {
|
||||
Ok((
|
||||
point_depth_stencil_view,
|
||||
point_res,
|
||||
point_sampler,
|
||||
directed_depth_stencil_view,
|
||||
directed_res,
|
||||
directed_sampler,
|
||||
)) => {
|
||||
shadow_map.point_depth_stencil_view = point_depth_stencil_view;
|
||||
shadow_map.point_res = point_res;
|
||||
shadow_map.point_sampler = point_sampler;
|
||||
|
||||
shadow_map.directed_depth_stencil_view = directed_depth_stencil_view;
|
||||
shadow_map.directed_res = directed_res;
|
||||
shadow_map.directed_sampler = directed_sampler;
|
||||
match Self::create_shadow_views(&mut self.device, (dims.width, dims.height), &mode)
|
||||
{
|
||||
Ok((point_depth_stencil, directed_depth_stencil)) => {
|
||||
shadow_map.point_depth_stencil = point_depth_stencil;
|
||||
shadow_map.directed_depth_stencil = directed_depth_stencil;
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Could not create shadow map views: {:?}", err);
|
||||
@ -522,7 +487,7 @@ impl Renderer {
|
||||
|
||||
fn create_rt_views(
|
||||
device: &wgpu::Device,
|
||||
size: (u16, u16),
|
||||
size: (u32, u32),
|
||||
mode: &RenderMode,
|
||||
) -> Result<
|
||||
(
|
||||
@ -565,9 +530,10 @@ impl Renderer {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::Color,
|
||||
// TODO: why is this not Color?
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
level_count: Some(levels),
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
})
|
||||
@ -596,7 +562,7 @@ impl Renderer {
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::DepthOnly,
|
||||
base_mip_level: 0,
|
||||
level_count: Some(levels),
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
@ -620,7 +586,7 @@ impl Renderer {
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::DepthOnly,
|
||||
base_mip_level: 0,
|
||||
level_count: Some(levels),
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
@ -639,28 +605,20 @@ impl Renderer {
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn create_shadow_views(
|
||||
device: &wgpu::Device,
|
||||
size: (u16, u16),
|
||||
size: (u32, u32),
|
||||
mode: &ShadowMapMode,
|
||||
) -> Result<
|
||||
(
|
||||
wgpu::TextureView,
|
||||
wgpu::Sampler,
|
||||
wgpu::TextureView,
|
||||
wgpu::Sampler,
|
||||
),
|
||||
RenderError,
|
||||
> {
|
||||
) -> Result<(Texture, Texture), RenderError> {
|
||||
// (Attempt to) apply resolution factor to shadow map resolution.
|
||||
let resolution_factor = mode.resolution.clamped(0.25, 4.0);
|
||||
|
||||
let max_texture_size = Self::max_texture_size_raw(device);
|
||||
// Limit to max texture size, rather than erroring.
|
||||
let size = Vec2::new(size.0, size.1).map(|e| {
|
||||
let size = f32::from(e) * resolution_factor;
|
||||
let size = e as f32 * resolution_factor;
|
||||
// NOTE: We know 0 <= e since we clamped the resolution factor to be between
|
||||
// 0.25 and 4.0.
|
||||
if size <= f32::from(max_texture_size) {
|
||||
size as u16
|
||||
if size <= max_texture_size as f32 {
|
||||
size as u32
|
||||
} else {
|
||||
max_texture_size
|
||||
}
|
||||
@ -669,7 +627,7 @@ impl Renderer {
|
||||
let levels = 1;
|
||||
// Limit to max texture size rather than erroring.
|
||||
let two_size = size.map(|e| {
|
||||
u16::checked_next_power_of_two(e)
|
||||
u32::checked_next_power_of_two(e)
|
||||
.filter(|&e| e <= max_texture_size)
|
||||
.unwrap_or(max_texture_size)
|
||||
});
|
||||
@ -688,17 +646,17 @@ impl Renderer {
|
||||
// diag_size would be 0 too). And it must be <= diag_size,
|
||||
// since min_size <= max_size. Therefore, if diag_size fits in a
|
||||
// u16, so does diag_cross_size.
|
||||
(diag_size as u16, diag_cross_size as u16)
|
||||
(diag_size as u32, diag_cross_size as u32)
|
||||
} else {
|
||||
// Limit to max texture resolution rather than error.
|
||||
(max_texture_size as u16, max_texture_size as u16)
|
||||
(max_texture_size as u32, max_texture_size as u32)
|
||||
};
|
||||
let diag_two_size = u16::checked_next_power_of_two(diag_size)
|
||||
let diag_two_size = u32::checked_next_power_of_two(diag_size)
|
||||
.filter(|&e| e <= max_texture_size)
|
||||
// Limit to max texture resolution rather than error.
|
||||
.unwrap_or(max_texture_size);
|
||||
|
||||
let point_shadow_tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
let point_shadow_tex = wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size: wgpu::Extent3d {
|
||||
width: diag_two_size / 4,
|
||||
@ -710,20 +668,9 @@ impl Renderer {
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Depth24Plus,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
});
|
||||
};
|
||||
|
||||
let point_tgt_shadow_view = point_shadow_tex.create_view(&wgpu::TextureViewDescriptor {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Depth24Plus),
|
||||
dimension: Some(wgpu::TextureViewDimension::Cube),
|
||||
aspect: wgpu::TextureAspect::DepthOnly,
|
||||
base_mip_level: 0,
|
||||
level_count: Some(levels),
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
|
||||
let directed_shadow_tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
let directed_shadow_tex = wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size: wgpu::Extent3d {
|
||||
width: diag_two_size,
|
||||
@ -735,18 +682,18 @@ impl Renderer {
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Depth24Plus,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
});
|
||||
};
|
||||
|
||||
let directed_tgt_shadow_view = point_shadow_tex.create_view(&wgpu::TextureViewDescriptor {
|
||||
let mut view_info = wgpu::TextureViewDescriptor {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Depth24Plus),
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::DepthOnly,
|
||||
base_mip_level: 0,
|
||||
level_count: Some(levels),
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
};
|
||||
|
||||
let sampler_info = wgpu::SamplerDescriptor {
|
||||
label: None,
|
||||
@ -760,57 +707,52 @@ impl Renderer {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let point_shadow_tex_sampler = device.create_sampler(&sampler_info);
|
||||
let directed_shadow_tex_sampler = device.create_sampler(&sampler_info);
|
||||
let point_tgt_shadow =
|
||||
Texture::new_raw(device, &point_shadow_tex, &view_info, &sampler_info);
|
||||
view_info.dimension = Some(wgpu::TextureViewDimension::Cube);
|
||||
let directed_shadow_tex =
|
||||
Texture::new_raw(device, &directed_shadow_tex, &view_info, &sampler_info);
|
||||
|
||||
Ok((
|
||||
point_tgt_shadow_view,
|
||||
point_shadow_tex_sampler,
|
||||
directed_tgt_shadow_view,
|
||||
directed_shadow_tex_sampler,
|
||||
))
|
||||
Ok((point_tgt_shadow, directed_shadow_tex))
|
||||
}
|
||||
|
||||
/// Get the resolution of the render target.
|
||||
/// Note: the change after a resize can be delayed so
|
||||
/// don't rely on this value being constant between resize events
|
||||
pub fn get_resolution(&self) -> Vec2<u16> {
|
||||
Vec2::new(
|
||||
self.win_color_view.get_dimensions().0,
|
||||
self.win_color_view.get_dimensions().1,
|
||||
)
|
||||
pub fn get_resolution(&self) -> Vec2<u32> {
|
||||
let dims = self.window.inner_size();
|
||||
|
||||
Vec2::new(dims.width, dims.height)
|
||||
}
|
||||
|
||||
/// Get the resolution of the shadow render target.
|
||||
pub fn get_shadow_resolution(&self) -> (Vec2<u16>, Vec2<u16>) {
|
||||
pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) {
|
||||
if let Some(shadow_map) = &self.shadow_map {
|
||||
let point_dims = shadow_map.point_depth_stencil_view.get_dimensions();
|
||||
let directed_dims = shadow_map.directed_depth_stencil_view.get_dimensions();
|
||||
let point_dims = shadow_map.point_depth_stencil.get_dimensions();
|
||||
let directed_dims = shadow_map.directed_depth_stencil.get_dimensions();
|
||||
(
|
||||
Vec2::new(point_dims.0, point_dims.1),
|
||||
Vec2::new(directed_dims.0, directed_dims.1),
|
||||
Vec2::new(point_dims.width, point_dims.height),
|
||||
Vec2::new(directed_dims.width, directed_dims.height),
|
||||
)
|
||||
} else {
|
||||
(Vec2::new(1, 1), Vec2::new(1, 1))
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue the clearing of the shadow targets ready for a new frame to be
|
||||
/// rendered.
|
||||
pub fn clear_shadows(&mut self) {
|
||||
span!(_guard, "clear_shadows", "Renderer::clear_shadows");
|
||||
if !self.mode.shadow.is_map() {
|
||||
return;
|
||||
}
|
||||
if let Some(shadow_map) = self.shadow_map.as_mut() {
|
||||
// let point_encoder = &mut shadow_map.point_encoder;
|
||||
let point_encoder = &mut self.encoder;
|
||||
point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0);
|
||||
// let directed_encoder = &mut shadow_map.directed_encoder;
|
||||
let directed_encoder = &mut self.encoder;
|
||||
directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view, 1.0);
|
||||
}
|
||||
}
|
||||
// /// Queue the clearing of the shadow targets ready for a new frame to be
|
||||
// /// rendered.
|
||||
// pub fn clear_shadows(&mut self) {
|
||||
// span!(_guard, "clear_shadows", "Renderer::clear_shadows");
|
||||
// if !self.mode.shadow.is_map() {
|
||||
// return;
|
||||
// }
|
||||
// if let Some(shadow_map) = self.shadow_map.as_mut() {
|
||||
// // let point_encoder = &mut shadow_map.point_encoder;
|
||||
// let point_encoder = &mut self.encoder;
|
||||
// point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0);
|
||||
// // let directed_encoder = &mut shadow_map.directed_encoder;
|
||||
// let directed_encoder = &mut self.encoder;
|
||||
// directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view,
|
||||
// 1.0); }
|
||||
// }
|
||||
|
||||
/// NOTE: Supported by Vulkan (by default), DirectX 10+ (it seems--it's hard
|
||||
/// to find proof of this, but Direct3D 10 apparently does it by
|
||||
@ -839,15 +781,6 @@ impl Renderer {
|
||||
// }
|
||||
}
|
||||
|
||||
/// Queue the clearing of the depth target ready for a new frame to be
|
||||
/// rendered.
|
||||
pub fn clear(&mut self) {
|
||||
span!(_guard, "clear", "Renderer::clear");
|
||||
self.encoder.clear_depth(&self.tgt_depth_stencil_view, 1.0);
|
||||
// self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0);
|
||||
self.encoder.clear_depth(&self.win_depth_view, 1.0);
|
||||
}
|
||||
|
||||
// /// Set up shadow rendering.
|
||||
// pub fn start_shadows(&mut self) {
|
||||
// if !self.mode.shadow.is_map() {
|
||||
@ -879,8 +812,7 @@ impl Renderer {
|
||||
/// items.
|
||||
pub fn flush(&mut self) {
|
||||
span!(_guard, "flush", "Renderer::flush");
|
||||
self.encoder.flush(&mut self.device);
|
||||
self.device.cleanup();
|
||||
self.device.poll(wgpu::Maintain::Poll);
|
||||
|
||||
// If the shaders files were changed attempt to recreate the shaders
|
||||
if self.shaders.reloaded() {
|
||||
@ -891,9 +823,11 @@ impl Renderer {
|
||||
/// Recreate the pipelines
|
||||
fn recreate_pipelines(&mut self) {
|
||||
match create_pipelines(
|
||||
&mut self.factory,
|
||||
&self.device,
|
||||
&self.layouts,
|
||||
&self.shaders.read(),
|
||||
&self.mode,
|
||||
&self.sc_desc,
|
||||
self.shadow_map.is_some(),
|
||||
) {
|
||||
Ok((
|
||||
@ -907,7 +841,7 @@ impl Renderer {
|
||||
lod_terrain_pipeline,
|
||||
clouds_pipeline,
|
||||
postprocess_pipeline,
|
||||
player_shadow_pipeline,
|
||||
//player_shadow_pipeline,
|
||||
point_shadow_pipeline,
|
||||
terrain_directed_shadow_pipeline,
|
||||
figure_directed_shadow_pipeline,
|
||||
@ -922,7 +856,7 @@ impl Renderer {
|
||||
self.lod_terrain_pipeline = lod_terrain_pipeline;
|
||||
self.clouds_pipeline = clouds_pipeline;
|
||||
self.postprocess_pipeline = postprocess_pipeline;
|
||||
self.player_shadow_pipeline = player_shadow_pipeline;
|
||||
//self.player_shadow_pipeline = player_shadow_pipeline;
|
||||
if let (
|
||||
Some(point_pipeline),
|
||||
Some(terrain_directed_pipeline),
|
||||
@ -948,14 +882,14 @@ impl Renderer {
|
||||
&mut self,
|
||||
vals: &[T],
|
||||
) -> Result<Consts<T>, RenderError> {
|
||||
let mut consts = Consts::new(&mut self.factory, vals.len());
|
||||
consts.update(&mut self.encoder, vals, 0)?;
|
||||
let mut consts = Consts::new(&self.device, vals.len() as u64);
|
||||
consts.update(&self.device, &self.queue, vals, 0);
|
||||
Ok(consts)
|
||||
}
|
||||
|
||||
/// Update a set of constants with the provided values.
|
||||
pub fn update_consts<T: Copy + bytemuck::Pod>(&mut self, consts: &mut Consts<T>, vals: &[T]) {
|
||||
consts.update(&mut self.encoder, vals, 0)
|
||||
consts.update(&self.device, &self.queue, vals, 0)
|
||||
}
|
||||
|
||||
/// Create a new set of instances with the provided values.
|
||||
@ -963,39 +897,31 @@ impl Renderer {
|
||||
&mut self,
|
||||
vals: &[T],
|
||||
) -> Result<Instances<T>, RenderError> {
|
||||
let mut instances = Instances::new(&mut self.factory, vals.len())?;
|
||||
instances.update(&mut self.encoder, vals)?;
|
||||
let mut instances = Instances::new(&self.device, vals.len() as u64);
|
||||
instances.update(&self.device, &self.queue, vals, 0);
|
||||
Ok(instances)
|
||||
}
|
||||
|
||||
/// Create a new model from the provided mesh.
|
||||
pub fn create_model<V: Vertex>(&mut self, mesh: &Mesh<V>) -> Result<Model<V>, RenderError> {
|
||||
Ok(Model::new(&mut self.factory, mesh))
|
||||
Ok(Model::new(&self.device, mesh))
|
||||
}
|
||||
|
||||
/// Create a new dynamic model with the specified size.
|
||||
pub fn create_dynamic_model<V: Vertex>(
|
||||
&mut self,
|
||||
size: usize,
|
||||
) -> Result<Model<V>, RenderError> {
|
||||
Model::new(&self.device, size)
|
||||
pub fn create_dynamic_model<V: Vertex>(&mut self, size: u64) -> Model<V> {
|
||||
Model::new_dynamic(&self.device, size)
|
||||
}
|
||||
|
||||
/// Update a dynamic model with a mesh and a offset.
|
||||
pub fn update_model<V: Vertex>(
|
||||
&mut self,
|
||||
model: &Model<V>,
|
||||
mesh: &Mesh<V>,
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
model.update(&mut self.encoder, mesh, offset)
|
||||
pub fn update_model<V: Vertex>(&mut self, model: &Model<V>, mesh: &Mesh<V>, offset: u64) {
|
||||
model.update(&self.device, &self.queue, mesh, offset)
|
||||
}
|
||||
|
||||
/// Return the maximum supported texture size.
|
||||
pub fn max_texture_size(&self) -> u16 { Self::max_texture_size_raw(&self.factory) }
|
||||
pub fn max_texture_size(&self) -> u32 { Self::max_texture_size_raw(&self.device) }
|
||||
|
||||
/// Return the maximum supported texture size from the factory.
|
||||
fn max_texture_size_raw(device: &wgpu::Device) -> u16 {
|
||||
fn max_texture_size_raw(_device: &wgpu::Device) -> u32 {
|
||||
// This value is temporary as there are plans to include a way to get this in
|
||||
// wgpu this is just a sane standard for now
|
||||
8192
|
||||
@ -1004,18 +930,19 @@ impl Renderer {
|
||||
/// Create a new immutable texture from the provided image.
|
||||
pub fn create_texture_with_data_raw(
|
||||
&mut self,
|
||||
texture_info: wgpu::TextureDescriptor,
|
||||
sampler_info: wgpu::SamplerDescriptor,
|
||||
texture_info: &wgpu::TextureDescriptor,
|
||||
view_info: &wgpu::TextureViewDescriptor,
|
||||
sampler_info: &wgpu::SamplerDescriptor,
|
||||
bytes_per_row: u32,
|
||||
data: &[u8],
|
||||
) -> Texture {
|
||||
let tex = Texture::new_raw(&self.device, texture_info, sampler_info);
|
||||
let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info);
|
||||
|
||||
tex.update(
|
||||
&self.device,
|
||||
&self.queue,
|
||||
[0; 2],
|
||||
[texture_info.size.x, texture_info.size.y],
|
||||
[texture_info.size.width, texture_info.size.height],
|
||||
data,
|
||||
bytes_per_row,
|
||||
);
|
||||
@ -1026,10 +953,11 @@ impl Renderer {
|
||||
/// Create a new raw texture.
|
||||
pub fn create_texture_raw(
|
||||
&mut self,
|
||||
texture_info: wgpu::TextureDescriptor,
|
||||
sampler_info: wgpu::SamplerDescriptor,
|
||||
texture_info: &wgpu::TextureDescriptor,
|
||||
view_info: &wgpu::TextureViewDescriptor,
|
||||
sampler_info: &wgpu::SamplerDescriptor,
|
||||
) -> Texture {
|
||||
Texture::new_raw(&self.device, texture_info, sampler_info)
|
||||
Texture::new_raw(&self.device, texture_info, view_info, sampler_info)
|
||||
}
|
||||
|
||||
/// Create a new texture from the provided image.
|
||||
@ -1040,7 +968,7 @@ impl Renderer {
|
||||
image: &image::DynamicImage,
|
||||
filter_method: Option<FilterMode>,
|
||||
address_mode: Option<AddressMode>,
|
||||
) -> Texture {
|
||||
) -> Result<Texture, RenderError> {
|
||||
Texture::new(
|
||||
&self.device,
|
||||
&self.queue,
|
||||
@ -1054,8 +982,8 @@ impl Renderer {
|
||||
/// specified dimensions.
|
||||
///
|
||||
/// Currently only supports Rgba8Srgb
|
||||
pub fn create_dynamic_texture(&mut self, dims: Vec2<u16>) -> Texture {
|
||||
Texture::new_dynamic(&mut self.factory, dims.x, dims.y)
|
||||
pub fn create_dynamic_texture(&mut self, dims: Vec2<u32>) -> Texture {
|
||||
Texture::new_dynamic(&self.device, dims.x, dims.y)
|
||||
}
|
||||
|
||||
/// Update a texture with the provided offset, size, and data.
|
||||
@ -1064,8 +992,8 @@ impl Renderer {
|
||||
pub fn update_texture(
|
||||
&mut self,
|
||||
texture: &Texture, /* <T> */
|
||||
offset: [u16; 2],
|
||||
size: [u16; 2],
|
||||
offset: [u32; 2],
|
||||
size: [u32; 2],
|
||||
// TODO
|
||||
// data: &[<<T as gfx::format::Formatted>::Surface as
|
||||
// gfx::format::SurfaceTyped>::DataType], ) -> Result<(), RenderError>
|
||||
@ -1078,7 +1006,14 @@ impl Renderer {
|
||||
data: &[[u8; 4]],
|
||||
bytes_per_row: u32,
|
||||
) {
|
||||
texture.update(&mut self.encoder, offset, size, data, bytes_per_row)
|
||||
texture.update(
|
||||
&self.device,
|
||||
&self.queue,
|
||||
offset,
|
||||
size,
|
||||
bytemuck::cast_slice(data),
|
||||
bytes_per_row,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a download buffer, downloads the win_color_view, and converts to
|
||||
@ -1945,18 +1880,18 @@ fn create_pipelines(
|
||||
options.set_optimization_level(OptimizationLevel::Performance);
|
||||
options.set_include_callback(move |name, _, shader_name, _| {
|
||||
Ok(ResolvedInclude {
|
||||
resolved_name: name,
|
||||
resolved_name: name.to_string(),
|
||||
content: match name {
|
||||
"constants.glsl" => constants,
|
||||
"globals.glsl" => globals,
|
||||
"shadows.glsl" => shadows,
|
||||
"sky.glsl" => sky,
|
||||
"light.glsl" => light,
|
||||
"srgb.glsl" => srgb,
|
||||
"random.glsl" => &random,
|
||||
"lod.glsl" => &lod,
|
||||
"anti-aliasing.glsl" => &anti_alias,
|
||||
"cloud.glsl" => &cloud,
|
||||
"globals.glsl" => *globals,
|
||||
"shadows.glsl" => *shadows,
|
||||
"sky.glsl" => *sky,
|
||||
"light.glsl" => *light,
|
||||
"srgb.glsl" => *srgb,
|
||||
"random.glsl" => *random,
|
||||
"lod.glsl" => *lod,
|
||||
"anti-aliasing.glsl" => *anti_alias,
|
||||
"cloud.glsl" => *cloud,
|
||||
other => return Err(format!("Include {} is not defined", other)),
|
||||
},
|
||||
})
|
||||
@ -1975,7 +1910,7 @@ fn create_pipelines(
|
||||
let figure_vert_mod = create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
figure_vert,
|
||||
&figure_vert,
|
||||
ShaderKind::Vertex,
|
||||
"figure-vert.glsl",
|
||||
&options,
|
||||
@ -1984,7 +1919,7 @@ fn create_pipelines(
|
||||
let terrain_point_shadow_vert_mod = create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
terrain_point_shadow_vert,
|
||||
&terrain_point_shadow_vert,
|
||||
ShaderKind::Vertex,
|
||||
"light-shadows-vert.glsl",
|
||||
&options,
|
||||
@ -1993,7 +1928,7 @@ fn create_pipelines(
|
||||
let terrain_directed_shadow_vert_mod = create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
terrain_directed_shadow_vert,
|
||||
&terrain_directed_shadow_vert,
|
||||
ShaderKind::Vertex,
|
||||
"light-shadows-directed-vert.glsl",
|
||||
&options,
|
||||
@ -2002,7 +1937,7 @@ fn create_pipelines(
|
||||
let figure_directed_shadow_vert_mod = create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
figure_directed_shadow_vert,
|
||||
&figure_directed_shadow_vert,
|
||||
ShaderKind::Vertex,
|
||||
"light-shadows-figure-vert.glsl",
|
||||
&options,
|
||||
@ -2022,24 +1957,24 @@ fn create_pipelines(
|
||||
// Construct a pipeline for rendering skyboxes
|
||||
let skybox_pipeline = skybox::SkyboxPipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.skybox_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"skybox-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.skybox_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"skybox-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
@ -2047,55 +1982,57 @@ fn create_pipelines(
|
||||
let figure_pipeline = figure::FigurePipeline::new(
|
||||
device,
|
||||
&figure_vert_mod,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.figure_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"figure-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
&layouts.figure,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
// Construct a pipeline for rendering terrain
|
||||
let terrain_pipeline = terrain::TerrainPipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.terrain_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"terrain-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.terrain_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"terrain-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
&layouts.terrain,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
// Construct a pipeline for rendering fluids
|
||||
let fluid_pipeline = fluid::FluidPipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.fluid_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"terrain-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
match mode.fluid {
|
||||
@ -2105,105 +2042,109 @@ fn create_pipelines(
|
||||
ShaderKind::Fragment,
|
||||
"fluid-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
&layouts.fluid,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
// Construct a pipeline for rendering sprites
|
||||
let sprite_pipeline = sprite::SpritePipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.sprite_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"sprite-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.sprite_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"sprite-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
&layouts.sprite,
|
||||
&layouts.terrain,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
// Construct a pipeline for rendering particles
|
||||
let particle_pipeline = particle::ParticlePipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.particle_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"particle-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.particle_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"particle-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
// Construct a pipeline for rendering UI elements
|
||||
let ui_pipeline = ui::UIPipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.ui_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"ui-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.ui_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"ui-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
&layouts.ui,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
// Construct a pipeline for rendering terrain
|
||||
let lod_terrain_pipeline = lod_terrain::LodTerrainPipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.lod_terrain_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"lod-terrain-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.lod_terrain_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"lod-terrain-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
@ -2221,24 +2162,25 @@ fn create_pipelines(
|
||||
// Construct a pipeline for rendering our post-processing
|
||||
let postprocess_pipeline = postprocess::PostProcessPipeline::new(
|
||||
device,
|
||||
create_shader_module(
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.postprocess_vert.read().0,
|
||||
ShaderKind::Vertex,
|
||||
"postprocess-vert.glsl",
|
||||
&options,
|
||||
),
|
||||
create_shader_module(
|
||||
)?,
|
||||
&create_shader_module(
|
||||
device,
|
||||
&mut compiler,
|
||||
shaders.postprocess_frag.read().0,
|
||||
ShaderKind::Fragment,
|
||||
"postprocess-frag.glsl",
|
||||
&options,
|
||||
),
|
||||
)?,
|
||||
sc_desc,
|
||||
layouts,
|
||||
&layouts.global,
|
||||
&layouts.postprocess,
|
||||
mode.aa,
|
||||
);
|
||||
|
||||
@ -2368,5 +2310,9 @@ pub fn create_shader_module(
|
||||
|
||||
let spv = compiler.compile_into_spirv(source, kind, file_name, "main", Some(options))?;
|
||||
|
||||
Ok(device.create_shader_module(wgpu::ShaderModule::SpirV(Cow::Bowrrowed(spv.as_binary()))))
|
||||
Ok(
|
||||
device.create_shader_module(wgpu::ShaderModuleSource::SpirV(Cow::Borrowed(
|
||||
spv.as_binary(),
|
||||
))),
|
||||
)
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use wgpu::Extent3d;
|
||||
|
||||
/// Represents an image that has been uploaded to the GPU.
|
||||
pub struct Texture {
|
||||
pub tex: wgpu::TextureView,
|
||||
pub tex: wgpu::Texture,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
size: Extent3d,
|
||||
}
|
||||
@ -49,7 +50,7 @@ impl Texture {
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
},
|
||||
&[buffer.as_slice()],
|
||||
buffer.as_slice(),
|
||||
wgpu::TextureDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: image.width() * 4,
|
||||
@ -73,21 +74,33 @@ impl Texture {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
tex,
|
||||
view,
|
||||
sampler: device.create_sampler(&sampler_info),
|
||||
size,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_dynamic(device: &wgpu::Device, width: u16, height: u16) -> Self {
|
||||
pub fn new_dynamic(device: &wgpu::Device, width: u32, height: u32) -> Self {
|
||||
let size = wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
};
|
||||
|
||||
let tex_info = device.create_texture(&wgpu::TextureDescriptor {
|
||||
let tex_info = wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
@ -95,7 +108,7 @@ impl Texture {
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED,
|
||||
});
|
||||
};
|
||||
|
||||
let sampler_info = wgpu::SamplerDescriptor {
|
||||
label: None,
|
||||
@ -108,19 +121,35 @@ impl Texture {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Self::new_raw(device, tex_info, sampler_info)
|
||||
let view_info = wgpu::TextureViewDescriptor {
|
||||
label: None,
|
||||
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
};
|
||||
|
||||
Self::new_raw(device, &tex_info, &view_info, &sampler_info)
|
||||
}
|
||||
|
||||
pub fn new_raw(
|
||||
device: &wgpu::Device,
|
||||
texture_info: wgpu::TextureDescriptor,
|
||||
sampler_info: wgpu::SamplerDescriptor,
|
||||
texture_info: &wgpu::TextureDescriptor,
|
||||
view_info: &wgpu::TextureViewDescriptor,
|
||||
sampler_info: &wgpu::SamplerDescriptor,
|
||||
) -> Self {
|
||||
Ok(Self {
|
||||
tex: device.create_texture(&texture_info),
|
||||
sampler: device.create_sampler(&sampler_info),
|
||||
let tex = device.create_texture(texture_info);
|
||||
let view = tex.create_view(view_info);
|
||||
|
||||
Self {
|
||||
tex,
|
||||
view,
|
||||
sampler: device.create_sampler(sampler_info),
|
||||
size: texture_info.size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a texture with the given data (used for updating the glyph cache
|
||||
@ -129,11 +158,11 @@ impl Texture {
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
offset: [u16; 2],
|
||||
size: [u16; 2],
|
||||
offset: [u32; 2],
|
||||
size: [u32; 2],
|
||||
data: &[u8],
|
||||
bytes_per_row: u32,
|
||||
) -> Result<(), RenderError> {
|
||||
) {
|
||||
// TODO: Only works for 2D images
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyViewBase {
|
||||
@ -146,12 +175,10 @@ impl Texture {
|
||||
},
|
||||
},
|
||||
data,
|
||||
// TODO: I heard some rumors that there are other
|
||||
// formats that are not Rgba8
|
||||
wgpu::TextureDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row,
|
||||
rows_per_image: self.size.y,
|
||||
rows_per_image: self.size.height,
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width: size[0],
|
||||
|
@ -168,19 +168,19 @@ fn handle_main_events_cleared(
|
||||
if let Some(last) = states.last_mut() {
|
||||
span!(guard, "Render");
|
||||
let renderer = global_state.window.renderer_mut();
|
||||
// Clear the shadow maps.
|
||||
renderer.clear_shadows();
|
||||
// Clear the screen
|
||||
renderer.clear();
|
||||
// // Clear the shadow maps.
|
||||
// renderer.clear_shadows();
|
||||
// // Clear the screen
|
||||
// renderer.clear();
|
||||
// Render the screen using the global renderer
|
||||
last.render(renderer, &global_state.settings);
|
||||
// Finish the frame.
|
||||
global_state.window.renderer_mut().flush();
|
||||
// Display the frame on the window.
|
||||
global_state
|
||||
.window
|
||||
.swap_buffers()
|
||||
.expect("Failed to swap window buffers!");
|
||||
// global_state.window.renderer_mut().flush();
|
||||
// // Display the frame on the window.
|
||||
// global_state
|
||||
// .window
|
||||
// .swap_buffers()
|
||||
// .expect("Failed to swap window buffers!");
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{load::BodySpec, FigureModelEntry};
|
||||
use crate::{
|
||||
mesh::{greedy::GreedyMesh, Meshable},
|
||||
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain},
|
||||
render::{BoneMeshes, ColLightInfo, FigureModel, Mesh, Renderer, TerrainVertex},
|
||||
scene::camera::CameraMode,
|
||||
};
|
||||
@ -476,11 +476,10 @@ where
|
||||
offset: Vec3<f32>,
|
||||
bone_idx: u8,
|
||||
) -> BoneMeshes {
|
||||
let (opaque, _, _, bounds) =
|
||||
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
|
||||
segment,
|
||||
(greedy, opaque_mesh, offset, Vec3::one(), bone_idx),
|
||||
);
|
||||
let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain(
|
||||
segment,
|
||||
(greedy, opaque_mesh, offset, Vec3::one(), bone_idx),
|
||||
);
|
||||
(opaque, bounds)
|
||||
}
|
||||
|
||||
@ -492,17 +491,16 @@ where
|
||||
bone_idx: u8,
|
||||
) -> BoneMeshes {
|
||||
let lod_scale = 0.6;
|
||||
let (opaque, _, _, bounds) =
|
||||
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
|
||||
segment.scaled_by(Vec3::broadcast(lod_scale)),
|
||||
(
|
||||
greedy,
|
||||
opaque_mesh,
|
||||
offset * lod_scale,
|
||||
Vec3::one() / lod_scale,
|
||||
bone_idx,
|
||||
),
|
||||
);
|
||||
let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain(
|
||||
segment.scaled_by(Vec3::broadcast(lod_scale)),
|
||||
(
|
||||
greedy,
|
||||
opaque_mesh,
|
||||
offset * lod_scale,
|
||||
Vec3::one() / lod_scale,
|
||||
bone_idx,
|
||||
),
|
||||
);
|
||||
(opaque, bounds)
|
||||
}
|
||||
|
||||
@ -514,17 +512,16 @@ where
|
||||
bone_idx: u8,
|
||||
) -> BoneMeshes {
|
||||
let lod_scale = 0.3;
|
||||
let (opaque, _, _, bounds) =
|
||||
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
|
||||
segment.scaled_by(Vec3::broadcast(lod_scale)),
|
||||
(
|
||||
greedy,
|
||||
opaque_mesh,
|
||||
offset * lod_scale,
|
||||
Vec3::one() / lod_scale,
|
||||
bone_idx,
|
||||
),
|
||||
);
|
||||
let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain(
|
||||
segment.scaled_by(Vec3::broadcast(lod_scale)),
|
||||
(
|
||||
greedy,
|
||||
opaque_mesh,
|
||||
offset * lod_scale,
|
||||
Vec3::one() / lod_scale,
|
||||
bone_idx,
|
||||
),
|
||||
);
|
||||
(opaque, bounds)
|
||||
}
|
||||
|
||||
|
@ -5230,12 +5230,9 @@ impl FigureColLights {
|
||||
span!(_guard, "create_figure", "FigureColLights::create_figure");
|
||||
let atlas = &mut self.atlas;
|
||||
let allocation = atlas
|
||||
.allocate(guillotiere::Size::new(
|
||||
i32::from(tex_size.x),
|
||||
i32::from(tex_size.y),
|
||||
))
|
||||
.allocate(guillotiere::Size::new(tex_size.x as i32, tex_size.y as i32))
|
||||
.expect("Not yet implemented: allocate new atlas on allocation failure.");
|
||||
let col_lights = pipelines::shadow::create_col_lights(renderer, &(tex, tex_size))?;
|
||||
let col_lights = pipelines::shadow::create_col_lights(renderer, &(tex, tex_size));
|
||||
let model_len = u32::try_from(opaque.vertices().len())
|
||||
.expect("The model size for this figure does not fit in a u32!");
|
||||
let model = renderer.create_model(&opaque)?;
|
||||
@ -5262,8 +5259,7 @@ impl FigureColLights {
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn make_atlas(renderer: &mut Renderer) -> Result<AtlasAllocator, RenderError> {
|
||||
let max_texture_size = renderer.max_texture_size();
|
||||
let atlas_size =
|
||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||
let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
|
||||
let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions {
|
||||
// TODO: Verify some good empirical constants.
|
||||
small_size_threshold: 32,
|
||||
@ -5286,7 +5282,7 @@ impl FigureColLights {
|
||||
(0, 0),
|
||||
gfx::format::Swizzle::new(),
|
||||
gfx::texture::SamplerInfo::new(
|
||||
gfx::texture::FilterMethod::Bilinear,
|
||||
gfx::texture::FilterMetho>:Bilinear,
|
||||
gfx::texture::WrapMode::Clamp,
|
||||
),
|
||||
)?;
|
||||
@ -5467,18 +5463,16 @@ impl<S: Skeleton> FigureState<S> {
|
||||
self.last_light,
|
||||
self.last_glow,
|
||||
);
|
||||
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
||||
renderer.update_consts(&mut self.locals, &[locals]);
|
||||
|
||||
let lantern_offset = anim::compute_matrices(&self.skeleton, mat, buf);
|
||||
|
||||
let new_bone_consts = figure_bone_data_from_anim(buf);
|
||||
|
||||
renderer
|
||||
.update_consts(
|
||||
&mut self.meta.bone_consts,
|
||||
&new_bone_consts[0..S::BONE_COUNT],
|
||||
)
|
||||
.unwrap();
|
||||
renderer.update_consts(
|
||||
&mut self.meta.bone_consts,
|
||||
&new_bone_consts[0..S::BONE_COUNT],
|
||||
);
|
||||
self.lantern_offset = lantern_offset;
|
||||
|
||||
let smoothing = (5.0 * dt).min(1.0);
|
||||
|
@ -26,7 +26,7 @@ impl Lod {
|
||||
model: None,
|
||||
data: LodData::new(
|
||||
renderer,
|
||||
client.world_data().chunk_size(),
|
||||
client.world_data().chunk_size().as_(),
|
||||
client.world_data().lod_base.raw(),
|
||||
client.world_data().lod_alt.raw(),
|
||||
client.world_data().lod_horizon.raw(),
|
||||
|
@ -584,9 +584,7 @@ impl Scene {
|
||||
);
|
||||
lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32);
|
||||
lights.truncate(MAX_LIGHT_COUNT);
|
||||
renderer
|
||||
.update_consts(&mut self.data.lights, &lights)
|
||||
.expect("Failed to update light constants");
|
||||
renderer.update_consts(&mut self.data.lights, &lights);
|
||||
|
||||
// Update event lights
|
||||
let dt = ecs.fetch::<DeltaTime>().0;
|
||||
@ -623,9 +621,7 @@ impl Scene {
|
||||
.collect::<Vec<_>>();
|
||||
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32);
|
||||
shadows.truncate(MAX_SHADOW_COUNT);
|
||||
renderer
|
||||
.update_consts(&mut self.data.shadows, &shadows)
|
||||
.expect("Failed to update light constants");
|
||||
renderer.update_consts(&mut self.data.shadows, &shadows);
|
||||
|
||||
// Remember to put the new loaded distance back in the scene.
|
||||
self.loaded_distance = loaded_distance;
|
||||
@ -636,48 +632,42 @@ impl Scene {
|
||||
let focus_off = focus_pos.map(|e| e.trunc());
|
||||
|
||||
// Update global constants.
|
||||
renderer
|
||||
.update_consts(&mut self.data.globals, &[Globals::new(
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
focus_pos,
|
||||
self.loaded_distance,
|
||||
self.lod.get_data().tgt_detail as f32,
|
||||
self.map_bounds,
|
||||
time_of_day,
|
||||
scene_data.state.get_time(),
|
||||
renderer.get_resolution(),
|
||||
Vec2::new(SHADOW_NEAR, SHADOW_FAR),
|
||||
lights.len(),
|
||||
shadows.len(),
|
||||
NUM_DIRECTED_LIGHTS,
|
||||
scene_data
|
||||
.state
|
||||
.terrain()
|
||||
.get((cam_pos + focus_off).map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind())
|
||||
.unwrap_or(BlockKind::Air),
|
||||
self.select_pos.map(|e| e - focus_off.map(|e| e as i32)),
|
||||
scene_data.gamma,
|
||||
scene_data.exposure,
|
||||
scene_data.ambiance,
|
||||
self.camera.get_mode(),
|
||||
scene_data.sprite_render_distance as f32 - 20.0,
|
||||
)])
|
||||
.expect("Failed to update global constants");
|
||||
renderer
|
||||
.update_consts(&mut self.clouds.locals, &[CloudsLocals::new(
|
||||
proj_mat_inv,
|
||||
view_mat_inv,
|
||||
)])
|
||||
.expect("Failed to update cloud locals");
|
||||
renderer
|
||||
.update_consts(&mut self.postprocess.locals, &[PostProcessLocals::new(
|
||||
proj_mat_inv,
|
||||
view_mat_inv,
|
||||
)])
|
||||
.expect("Failed to update post-process locals");
|
||||
renderer.update_consts(&mut self.data.globals, &[Globals::new(
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
focus_pos,
|
||||
self.loaded_distance,
|
||||
self.lod.get_data().tgt_detail as f32,
|
||||
self.map_bounds,
|
||||
time_of_day,
|
||||
scene_data.state.get_time(),
|
||||
renderer.get_resolution().as_(),
|
||||
Vec2::new(SHADOW_NEAR, SHADOW_FAR),
|
||||
lights.len(),
|
||||
shadows.len(),
|
||||
NUM_DIRECTED_LIGHTS,
|
||||
scene_data
|
||||
.state
|
||||
.terrain()
|
||||
.get((cam_pos + focus_off).map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind())
|
||||
.unwrap_or(BlockKind::Air),
|
||||
self.select_pos.map(|e| e - focus_off.map(|e| e as i32)),
|
||||
scene_data.gamma,
|
||||
scene_data.exposure,
|
||||
scene_data.ambiance,
|
||||
self.camera.get_mode(),
|
||||
scene_data.sprite_render_distance as f32 - 20.0,
|
||||
)]);
|
||||
renderer.update_consts(&mut self.clouds.locals, &[CloudsLocals::new(
|
||||
proj_mat_inv,
|
||||
view_mat_inv,
|
||||
)]);
|
||||
renderer.update_consts(&mut self.postprocess.locals, &[PostProcessLocals::new(
|
||||
proj_mat_inv,
|
||||
view_mat_inv,
|
||||
)]);
|
||||
|
||||
// Maintain LoD.
|
||||
self.lod.maintain(renderer);
|
||||
@ -985,9 +975,7 @@ impl Scene {
|
||||
})
|
||||
}));
|
||||
|
||||
renderer
|
||||
.update_consts(&mut self.data.shadow_mats, &shadow_mats)
|
||||
.expect("Failed to update light constants");
|
||||
renderer.update_consts(&mut self.data.shadow_mats, &shadow_mats);
|
||||
}
|
||||
|
||||
// Remove unused figures.
|
||||
@ -1028,10 +1016,10 @@ impl Scene {
|
||||
|
||||
// would instead have this as an extension.
|
||||
if renderer.render_mode().shadow.is_map() && (is_daylight || !light_data.1.is_empty()) {
|
||||
if is_daylight {
|
||||
// Set up shadow mapping.
|
||||
renderer.start_shadows();
|
||||
}
|
||||
// if is_daylight {
|
||||
// // Set up shadow mapping.
|
||||
// renderer.start_shadows();
|
||||
// }
|
||||
|
||||
// Render terrain shadows.
|
||||
self.terrain
|
||||
@ -1041,10 +1029,10 @@ impl Scene {
|
||||
self.figure_mgr
|
||||
.render_shadows(renderer, state, tick, global, light_data, camera_data);
|
||||
|
||||
if is_daylight {
|
||||
// Flush shadows.
|
||||
renderer.flush_shadows();
|
||||
}
|
||||
// if is_daylight {
|
||||
// // Flush shadows.
|
||||
// renderer.flush_shadows();
|
||||
// }
|
||||
}
|
||||
let lod = self.lod.get_data();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{terrain::BlocksOfInterest, SceneData, Terrain};
|
||||
use crate::{
|
||||
mesh::{greedy::GreedyMesh, Meshable},
|
||||
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_particle},
|
||||
render::{
|
||||
pipelines::particle::ParticleMode, GlobalModel, Instances, Light, LodData, Model,
|
||||
ParticleInstance, ParticleVertex, Renderer,
|
||||
@ -1220,14 +1220,12 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model<Particl
|
||||
// NOTE: If we add texturing we may eventually try to share it among all
|
||||
// particles in a single atlas.
|
||||
let max_texture_size = renderer.max_texture_size();
|
||||
let max_size =
|
||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||
let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
|
||||
let mut greedy = GreedyMesh::new(max_size);
|
||||
|
||||
let segment = Segment::from(&vox.read().0);
|
||||
let segment_size = segment.size();
|
||||
let mut mesh =
|
||||
Meshable::<ParticleVertex, &mut GreedyMesh>::generate_mesh(segment, &mut greedy).0;
|
||||
let mesh = generate_mesh_base_vol_particle(segment, &mut greedy).0;
|
||||
// Center particle vertices around origin
|
||||
for vert in mesh.vertices_mut() {
|
||||
vert.pos[0] -= segment_size.x as f32 / 2.0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
mesh::{greedy::GreedyMesh, Meshable},
|
||||
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain},
|
||||
render::{
|
||||
create_clouds_mesh, create_pp_mesh, create_skybox_mesh, BoneMeshes, CloudsLocals, Consts,
|
||||
FigureModel, GlobalModel, Globals, Light, Mesh, Model, PostProcessLocals,
|
||||
@ -51,10 +51,7 @@ fn generate_mesh<'a>(
|
||||
bone_idx: u8,
|
||||
) -> BoneMeshes {
|
||||
let (opaque, _, /* shadow */ _, bounds) =
|
||||
Meshable::<TerrainVertex, &mut GreedyMesh>::generate_mesh(
|
||||
segment,
|
||||
(greedy, mesh, offset, Vec3::one(), bone_idx),
|
||||
);
|
||||
generate_mesh_base_vol_terrain(segment, (greedy, mesh, offset, Vec3::one(), bone_idx));
|
||||
(opaque /* , shadow */, bounds)
|
||||
}
|
||||
|
||||
@ -152,7 +149,7 @@ impl Scene {
|
||||
&alt_image,
|
||||
&horizon_image,
|
||||
1,
|
||||
map_border.into(),
|
||||
//map_border.into(),
|
||||
),
|
||||
map_bounds,
|
||||
|
||||
@ -271,7 +268,7 @@ impl Scene {
|
||||
const SHADOW_NEAR: f32 = 1.0;
|
||||
const SHADOW_FAR: f32 = 25.0;
|
||||
|
||||
if let Err(e) = renderer.update_consts(&mut self.data.globals, &[Globals::new(
|
||||
renderer.update_consts(&mut self.data.globals, &[Globals::new(
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
@ -281,7 +278,7 @@ impl Scene {
|
||||
self.map_bounds,
|
||||
TIME,
|
||||
scene_data.time,
|
||||
renderer.get_resolution(),
|
||||
renderer.get_resolution().as_(),
|
||||
Vec2::new(SHADOW_NEAR, SHADOW_FAR),
|
||||
0,
|
||||
0,
|
||||
@ -293,9 +290,7 @@ impl Scene {
|
||||
scene_data.ambiance,
|
||||
self.camera.get_mode(),
|
||||
250.0,
|
||||
)]) {
|
||||
error!(?e, "Renderer failed to update");
|
||||
}
|
||||
)]);
|
||||
|
||||
self.figure_model_cache
|
||||
.clean(&mut self.col_lights, scene_data.tick);
|
||||
|
@ -2,7 +2,11 @@ mod watcher;
|
||||
pub use self::watcher::{BlocksOfInterest, Interaction};
|
||||
|
||||
use crate::{
|
||||
mesh::{greedy::GreedyMesh, terrain::SUNLIGHT, Meshable},
|
||||
mesh::{
|
||||
greedy::GreedyMesh,
|
||||
segment::generate_mesh_base_vol_sprite,
|
||||
terrain::{generate_mesh, SUNLIGHT},
|
||||
},
|
||||
render::{
|
||||
pipelines, ColLightInfo, Consts, FluidVertex, GlobalModel, Instances, Mesh, Model,
|
||||
RenderError, Renderer, SpriteInstance, SpriteLocals, SpriteVertex, TerrainLocals,
|
||||
@ -30,7 +34,6 @@ use std::sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
};
|
||||
use tracing::warn;
|
||||
use treeculler::{BVol, Frustum, AABB};
|
||||
use vek::*;
|
||||
|
||||
@ -174,17 +177,21 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
|
||||
) -> MeshWorkerResponse {
|
||||
span!(_guard, "mesh_worker");
|
||||
let blocks_of_interest = BlocksOfInterest::from_chunk(&chunk);
|
||||
|
||||
let mesh;
|
||||
let (light_map, glow_map) = if let Some((light_map, glow_map)) = &skip_remesh {
|
||||
mesh = None;
|
||||
(&**light_map, &**glow_map)
|
||||
} else {
|
||||
let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info, light_map, glow_map)) =
|
||||
volume.generate_mesh((
|
||||
range,
|
||||
Vec2::new(max_texture_size, max_texture_size),
|
||||
&blocks_of_interest,
|
||||
));
|
||||
generate_mesh(
|
||||
&volume,
|
||||
(
|
||||
range,
|
||||
Vec2::new(max_texture_size, max_texture_size),
|
||||
&blocks_of_interest,
|
||||
),
|
||||
);
|
||||
mesh = Some(MeshWorkerResponseMesh {
|
||||
// TODO: Take sprite bounds into account somehow?
|
||||
z_bounds: (bounds.min.z, bounds.max.z),
|
||||
@ -198,6 +205,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
|
||||
let mesh = mesh.as_ref().unwrap();
|
||||
(&*mesh.light_map, &*mesh.glow_map)
|
||||
};
|
||||
|
||||
MeshWorkerResponse {
|
||||
pos,
|
||||
// Extract sprite locations from volume
|
||||
@ -363,8 +371,7 @@ impl SpriteRenderContext {
|
||||
let sprite_config =
|
||||
Arc::<SpriteSpec>::load_expect("voxygen.voxel.sprite_manifest").cloned();
|
||||
|
||||
let max_size =
|
||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||
let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
|
||||
let mut greedy = GreedyMesh::new(max_size);
|
||||
let mut locals_buffer = [SpriteLocals::default(); 8];
|
||||
let sprite_config_ = &sprite_config;
|
||||
@ -400,14 +407,15 @@ impl SpriteRenderContext {
|
||||
)
|
||||
.unwrap_or(zero);
|
||||
let max_model_size = Vec3::new(31.0, 31.0, 63.0);
|
||||
let model_scale = max_model_size.map2(model_size, |max_sz: f32, cur_sz| {
|
||||
let scale = max_sz / max_sz.max(cur_sz as f32);
|
||||
if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz {
|
||||
scale - 0.001
|
||||
} else {
|
||||
scale
|
||||
}
|
||||
});
|
||||
let model_scale =
|
||||
max_model_size.map2(model_size, |max_sz: f32, cur_sz| {
|
||||
let scale = max_sz / max_sz.max(cur_sz as f32);
|
||||
if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz {
|
||||
scale - 0.001
|
||||
} else {
|
||||
scale
|
||||
}
|
||||
});
|
||||
let sprite_mat: Mat4<f32> =
|
||||
Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE);
|
||||
move |greedy: &mut GreedyMesh| {
|
||||
@ -421,14 +429,15 @@ impl SpriteRenderContext {
|
||||
Vec3::broadcast(1.0)
|
||||
} else {
|
||||
lod_axes * lod_scale_orig
|
||||
+ lod_axes
|
||||
.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
|
||||
+ lod_axes.map(|e| {
|
||||
if e == 0.0 { 1.0 } else { 0.0 }
|
||||
})
|
||||
};
|
||||
// Mesh generation exclusively acts using side effects; it
|
||||
// has no
|
||||
// Mesh generation exclusively acts using side effects;
|
||||
// it has no
|
||||
// interesting return value, but updates the mesh.
|
||||
let mut opaque_mesh = Mesh::new();
|
||||
Meshable::<SpriteVertex, &mut GreedyMesh>::generate_mesh(
|
||||
generate_mesh_base_vol_sprite(
|
||||
Segment::from(&model.read().0).scaled_by(lod_scale),
|
||||
(greedy, &mut opaque_mesh, false),
|
||||
);
|
||||
@ -438,8 +447,9 @@ impl SpriteRenderContext {
|
||||
sprite_mat * Mat4::scaling_3d(sprite_scale);
|
||||
locals_buffer.iter_mut().enumerate().for_each(
|
||||
|(ori, locals)| {
|
||||
let sprite_mat = sprite_mat
|
||||
.rotated_z(f32::consts::PI * 0.25 * ori as f32);
|
||||
let sprite_mat = sprite_mat.rotated_z(
|
||||
f32::consts::PI * 0.25 * ori as f32,
|
||||
);
|
||||
*locals = SpriteLocals::new(
|
||||
sprite_mat,
|
||||
sprite_scale,
|
||||
@ -522,8 +532,7 @@ impl SpriteRenderContext {
|
||||
})
|
||||
.collect();
|
||||
let sprite_col_lights =
|
||||
pipelines::shadow::create_col_lights(renderer, &sprite_col_lights)
|
||||
.expect("Failed to upload sprite color and light data to the GPU!");
|
||||
pipelines::shadow::create_col_lights(renderer, &sprite_col_lights);
|
||||
|
||||
Self {
|
||||
sprite_config: Arc::clone(&sprite_config),
|
||||
@ -573,8 +582,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
) -> Result<(AtlasAllocator, Texture /* <ColLightFmt> */), RenderError> {
|
||||
span!(_guard, "make_atlas", "Terrain::make_atlas");
|
||||
let max_texture_size = renderer.max_texture_size();
|
||||
let atlas_size =
|
||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||
let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32);
|
||||
let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions {
|
||||
// TODO: Verify some good empirical constants.
|
||||
small_size_threshold: 128,
|
||||
@ -582,7 +590,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
..guillotiere::AllocatorOptions::default()
|
||||
});
|
||||
let texture = renderer.create_texture_raw(
|
||||
wgpu::TextureDescriptor {
|
||||
&wgpu::TextureDescriptor {
|
||||
label: Some("Atlas texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: max_texture_size,
|
||||
@ -595,7 +603,17 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED,
|
||||
},
|
||||
wgpu::SamplerDescriptor {
|
||||
&wgpu::TextureViewDescriptor {
|
||||
label: Some("Atlas texture view"),
|
||||
format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
|
||||
dimension: Some(wgpu::TextureViewDimension::D1),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
},
|
||||
&wgpu::SamplerDescriptor {
|
||||
label: Some("Atlas sampler"),
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
@ -605,7 +623,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
);
|
||||
Ok((atlas, texture))
|
||||
}
|
||||
|
||||
@ -955,7 +973,6 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the area of the terrain we want. Because meshing needs to compute things
|
||||
// like ambient occlusion and edge elision, we also need the borders
|
||||
// of the chunk's neighbours too (hence the `- 1` and `+ 1`).
|
||||
let aabr = Aabr {
|
||||
@ -1022,7 +1039,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
skip_remesh,
|
||||
started_tick,
|
||||
volume,
|
||||
max_texture_size,
|
||||
max_texture_size as u16,
|
||||
chunk,
|
||||
aabb,
|
||||
&sprite_data,
|
||||
@ -1080,8 +1097,9 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
let col_lights = &mut self.col_lights;
|
||||
let allocation = atlas
|
||||
.allocate(guillotiere::Size::new(
|
||||
i32::from(tex_size.x),
|
||||
i32::from(tex_size.y),
|
||||
tex_size.x as i32, /* TODO: adjust ColLightInfo to avoid the
|
||||
* cast here? */
|
||||
tex_size.y as i32,
|
||||
))
|
||||
.unwrap_or_else(|| {
|
||||
// Atlas allocation failure: try allocating a new texture and atlas.
|
||||
@ -1104,25 +1122,25 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
|
||||
atlas
|
||||
.allocate(guillotiere::Size::new(
|
||||
i32::from(tex_size.x),
|
||||
i32::from(tex_size.y),
|
||||
tex_size.x as i32, /* TODO: adjust ColLightInfo to avoid
|
||||
* the
|
||||
* cast here? */
|
||||
tex_size.y as i32,
|
||||
))
|
||||
.expect("Chunk data does not fit in a texture of maximum size.")
|
||||
});
|
||||
|
||||
// NOTE: Cast is safe since the origin was a u16.
|
||||
let atlas_offs = Vec2::new(
|
||||
allocation.rectangle.min.x as u16,
|
||||
allocation.rectangle.min.y as u16,
|
||||
allocation.rectangle.min.x as u32,
|
||||
allocation.rectangle.min.y as u32,
|
||||
);
|
||||
if let Err(err) = renderer.update_texture(
|
||||
renderer.update_texture(
|
||||
col_lights,
|
||||
atlas_offs.into_array(),
|
||||
tex_size.into_array(),
|
||||
&tex,
|
||||
) {
|
||||
warn!("Failed to update texture: {:?}", err);
|
||||
}
|
||||
);
|
||||
|
||||
self.insert_chunk(response.pos, TerrainChunkData {
|
||||
load_time,
|
||||
@ -1152,8 +1170,8 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
)
|
||||
.into_array(),
|
||||
atlas_offs: Vec4::new(
|
||||
i32::from(atlas_offs.x),
|
||||
i32::from(atlas_offs.y),
|
||||
atlas_offs.x as i32,
|
||||
atlas_offs.y as i32,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
@ -518,7 +518,7 @@ impl KeyMouse {
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
renderer: Renderer,
|
||||
renderer: Renderer<'static>,
|
||||
window: winit::window::Window,
|
||||
cursor_grabbed: bool,
|
||||
pub pan_sensitivity: u32,
|
||||
@ -574,7 +574,7 @@ impl Window {
|
||||
// .map_err(|err| Error::BackendError(Box::new(err)))?
|
||||
// .init_gfx::<WinColorFmt, WinDepthFmt>();
|
||||
|
||||
let window = win_builder.build(&event_loop)?;
|
||||
let window = win_builder.build(&event_loop).unwrap();
|
||||
|
||||
let renderer = Renderer::new(&window, settings.graphics.render_mode.clone())?;
|
||||
|
||||
@ -658,7 +658,7 @@ impl Window {
|
||||
|
||||
pub fn renderer(&self) -> &Renderer { &self.renderer }
|
||||
|
||||
pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
|
||||
pub fn renderer_mut(&mut self) -> &mut Renderer<'static> { &mut self.renderer }
|
||||
|
||||
pub fn resolve_deduplicated_events(&mut self, settings: &mut Settings) {
|
||||
// Handle screenshots and toggling fullscreen
|
||||
@ -938,9 +938,9 @@ impl Window {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => self.events.push(Event::Close),
|
||||
WindowEvent::Resized(physical) => {
|
||||
let (mut color_view, mut depth_view) = self.renderer.win_views_mut();
|
||||
self.window.resize(physical);
|
||||
self.window.update_gfx(&mut color_view, &mut depth_view);
|
||||
// let (mut color_view, mut depth_view) = self.renderer.win_views_mut();
|
||||
// self.window.resize(physical);
|
||||
// self.window.update_gfx(&mut color_view, &mut depth_view);
|
||||
self.renderer.on_resize().unwrap();
|
||||
// TODO: update users of this event with the fact that it is now the physical
|
||||
// size
|
||||
@ -1084,32 +1084,24 @@ impl Window {
|
||||
/// Moves cursor by an offset
|
||||
pub fn offset_cursor(&self, d: Vec2<f32>) {
|
||||
if d != Vec2::zero() {
|
||||
if let Err(err) =
|
||||
self.window
|
||||
.window()
|
||||
.set_cursor_position(winit::dpi::LogicalPosition::new(
|
||||
d.x as f64 + self.cursor_position.x,
|
||||
d.y as f64 + self.cursor_position.y,
|
||||
))
|
||||
if let Err(err) = self
|
||||
.window
|
||||
.set_cursor_position(winit::dpi::LogicalPosition::new(
|
||||
d.x as f64 + self.cursor_position.x,
|
||||
d.y as f64 + self.cursor_position.y,
|
||||
))
|
||||
{
|
||||
error!("Error setting cursor position: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) -> Result<(), Error> {
|
||||
span!(_guard, "swap_buffers", "Window::swap_buffers");
|
||||
self.window
|
||||
.swap_buffers()
|
||||
.map_err(|err| Error::BackendError(Box::new(err)))
|
||||
}
|
||||
|
||||
pub fn is_cursor_grabbed(&self) -> bool { self.cursor_grabbed }
|
||||
|
||||
pub fn grab_cursor(&mut self, grab: bool) {
|
||||
self.cursor_grabbed = grab;
|
||||
self.window.window().set_cursor_visible(!grab);
|
||||
let _ = self.window.window().set_cursor_grab(grab);
|
||||
self.window.set_cursor_visible(!grab);
|
||||
let _ = self.window.set_cursor_grab(grab);
|
||||
}
|
||||
|
||||
/// Moves mouse cursor to center of screen
|
||||
@ -1155,8 +1147,7 @@ impl Window {
|
||||
// the correct resolution already, load that value, otherwise filter it
|
||||
// in this iteration
|
||||
let correct_res = correct_res.unwrap_or_else(|| {
|
||||
let window = self.window.window();
|
||||
window
|
||||
self.window
|
||||
.current_monitor()
|
||||
.unwrap()
|
||||
.video_modes()
|
||||
@ -1308,7 +1299,6 @@ impl Window {
|
||||
|
||||
self
|
||||
.window
|
||||
.window()
|
||||
.current_monitor().unwrap()
|
||||
.video_modes()
|
||||
// Prefer bit depth over refresh rate
|
||||
@ -1320,7 +1310,7 @@ impl Window {
|
||||
}
|
||||
|
||||
pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) {
|
||||
let window = self.window.window();
|
||||
let window = self.window;
|
||||
self.fullscreen = fullscreen;
|
||||
window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode {
|
||||
FullscreenMode::Exclusive => {
|
||||
@ -1342,20 +1332,17 @@ impl Window {
|
||||
pub fn logical_size(&self) -> Vec2<f64> {
|
||||
let (w, h) = self
|
||||
.window
|
||||
.window()
|
||||
.inner_size()
|
||||
.to_logical::<f64>(self.window.window().scale_factor())
|
||||
.to_logical::<f64>(self.window.scale_factor())
|
||||
.into();
|
||||
Vec2::new(w, h)
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, new_size: Vec2<u16>) {
|
||||
self.window
|
||||
.window()
|
||||
.set_inner_size(winit::dpi::LogicalSize::new(
|
||||
new_size.x as f64,
|
||||
new_size.y as f64,
|
||||
));
|
||||
self.window.set_inner_size(winit::dpi::LogicalSize::new(
|
||||
new_size.x as f64,
|
||||
new_size.y as f64,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn send_event(&mut self, event: Event) { self.events.push(event) }
|
||||
|
Loading…
Reference in New Issue
Block a user