Added translucent meshing pass, water reflections

This commit is contained in:
Joshua Barretto 2019-08-14 22:28:37 +01:00
parent 4a29900914
commit 42237a84e4
19 changed files with 270 additions and 163 deletions

View File

@ -22,9 +22,13 @@ void main() {
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm); vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm);
vec3 surf_color = f_col * light; vec3 surf_color = f_col * light;
float fog_level = fog(f_pos.xy, focus_pos.xy); float fog_level = fog(f_pos.xy, focus_pos.xy);
vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x); vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x);
vec3 color = mix(surf_color, fog_color, fog_level);
vec3 warped_norm = normalize(f_norm + (sin(f_pos.xyz + tick.x + 13.7) + sin(f_pos.zxy + tick.x + 19.3)) * 0.3);
vec3 reflect_color = get_sky_color(reflect(normalize(f_pos - cam_pos.xyz), warped_norm), time_of_day.x);
vec3 color = mix(surf_color + reflect_color * 0.5, fog_color, fog_level);
tgt_color = vec4(color, f_opac); tgt_color = vec4(color, f_opac);
} }

View File

@ -14,15 +14,16 @@ out vec3 f_pos;
flat out vec3 f_norm; flat out vec3 f_norm;
out vec3 f_col; out vec3 f_col;
out float f_light; out float f_light;
out float f_opac;
// First 3 normals are negative, next 3 are positive // First 3 normals are negative, next 3 are positive
vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) ); vec3 normals[6] = vec3[]( vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) );
void main() { void main() {
f_pos = vec3( f_pos = vec3(
float((v_pos_norm >> 0) & 0x00FFu), float((v_pos_norm >> 0) & 0x00FFu),
float((v_pos_norm >> 8) & 0x00FFu), float((v_pos_norm >> 8) & 0x00FFu),
float((v_pos_norm >> 16) & 0x1FFFu) float((v_pos_norm >> 16) & 0x1FFFu)
) + model_offs; ) + model_offs;
// TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction.
@ -35,15 +36,17 @@ void main() {
f_norm = normals[norm_axis + norm_dir]; f_norm = normals[norm_axis + norm_dir];
f_col = vec3( f_col = vec3(
float((v_col_light >> 8) & 0xFFu), float((v_col_light >> 8) & 0xFFu),
float((v_col_light >> 16) & 0xFFu), float((v_col_light >> 16) & 0xFFu),
float((v_col_light >> 24) & 0xFFu) float((v_col_light >> 24) & 0xFFu)
) / 200.0; ) / 200.0;
f_light = float(v_col_light & 0xFFu) / 255.0; f_light = float(v_col_light & 0xFFu) / 255.0;
f_opac = 0.1;
gl_Position = gl_Position =
proj_mat * proj_mat *
view_mat * view_mat *
vec4(f_pos, 1); vec4(f_pos, 1);
} }

View File

@ -1,47 +1,97 @@
use crate::vol::Vox; use crate::vol::Vox;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::ops::Deref;
use vek::*; use vek::*;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[repr(u8)]
pub enum BlockKind {
Air,
Normal,
Dense,
Water,
}
impl BlockKind {
pub fn is_air(&self) -> bool {
match self {
BlockKind::Air => true,
_ => false,
}
}
pub fn is_fluid(&self) -> bool {
match self {
BlockKind::Water => true,
_ => false,
}
}
pub fn is_opaque(&self) -> bool {
match self {
BlockKind::Air => false,
BlockKind::Water => false,
_ => true,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[repr(packed)]
pub struct Block { pub struct Block {
kind: u8, kind: BlockKind,
color: [u8; 3], color: [u8; 3],
} }
impl Block { impl Block {
pub fn new(kind: u8, color: Rgb<u8>) -> Self { pub fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
Self { Self {
kind, kind,
color: color.into_array(), color: color.into_array(),
} }
} }
pub fn get_color(self) -> Option<Rgb<u8>> { pub fn get_color(&self) -> Option<Rgb<u8>> {
if self.is_empty() { if !self.is_air() {
None
} else {
Some(self.color.into()) Some(self.color.into())
} else {
None
} }
} }
pub fn get_opacity(self) -> Option<f32> { pub fn kind(&self) -> BlockKind {
match self.kind { self.kind
0 => None, }
1 => Some(0.85), }
_ => Some(3.0),
} impl Deref for Block {
type Target = BlockKind;
fn deref(&self) -> &Self::Target {
&self.kind
} }
} }
impl Vox for Block { impl Vox for Block {
fn empty() -> Self { fn empty() -> Self {
Self { Self {
kind: 0, kind: BlockKind::Air,
color: [0; 3], color: [0; 3],
} }
} }
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.kind == 0 self.is_air()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_size() {
assert_eq!(std::mem::size_of::<BlockKind>(), 1);
assert_eq!(std::mem::size_of::<Block>(), 4);
} }
} }

View File

@ -4,7 +4,11 @@ pub mod chonk;
pub mod structure; pub mod structure;
// Reexports // Reexports
pub use self::{biome::BiomeKind, block::Block, structure::Structure}; pub use self::{
biome::BiomeKind,
block::{Block, BlockKind},
structure::Structure,
};
use crate::{vol::VolSize, volumes::vol_map_2d::VolMap2d}; use crate::{vol::VolSize, volumes::vol_map_2d::VolMap2d};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};

View File

@ -1,4 +1,4 @@
use super::Block; use super::{Block, BlockKind};
use crate::{ use crate::{
assets::{self, Asset}, assets::{self, Asset},
vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol}, vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol},
@ -103,7 +103,7 @@ impl Asset for Structure {
.get(index as usize) .get(index as usize)
.copied() .copied()
.unwrap_or_else(|| Rgb::broadcast(0)); .unwrap_or_else(|| Rgb::broadcast(0));
StructureBlock::Block(Block::new(1, color)) StructureBlock::Block(Block::new(BlockKind::Normal, color))
} }
}; };

View File

@ -6,7 +6,12 @@ use crate::render::{self, Mesh};
pub trait Meshable { pub trait Meshable {
type Pipeline: render::Pipeline; type Pipeline: render::Pipeline;
type TranslucentPipeline: render::Pipeline;
type Supplement; type Supplement;
fn generate_mesh(&self, supp: Self::Supplement) -> Mesh<Self::Pipeline>; // Generate meshes - one opaque, one translucent
fn generate_mesh(
&self,
supp: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>);
} }

View File

@ -5,7 +5,7 @@ use crate::{
use common::{ use common::{
figure::Segment, figure::Segment,
util::{linear_to_srgb, srgb_to_linear}, util::{linear_to_srgb, srgb_to_linear},
vol::{ReadVol, SizedVol}, vol::{ReadVol, SizedVol, Vox},
}; };
use vek::*; use vek::*;
@ -13,9 +13,13 @@ type FigureVertex = <FigurePipeline as render::Pipeline>::Vertex;
impl Meshable for Segment { impl Meshable for Segment {
type Pipeline = FigurePipeline; type Pipeline = FigurePipeline;
type TranslucentPipeline = FigurePipeline;
type Supplement = Vec3<f32>; type Supplement = Vec3<f32>;
fn generate_mesh(&self, offs: Self::Supplement) -> Mesh<Self::Pipeline> { fn generate_mesh(
&self,
offs: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut mesh = Mesh::new(); let mut mesh = Mesh::new();
for pos in self.iter_positions() { for pos in self.iter_positions() {
@ -38,10 +42,11 @@ impl Meshable for Segment {
}, },
true, true,
&[[[1.0; 3]; 3]; 3], &[[[1.0; 3]; 3]; 3],
|vox| vox.is_empty(),
); );
} }
} }
mesh (mesh, Mesh::new())
} }
} }

View File

@ -1,9 +1,9 @@
use crate::{ use crate::{
mesh::{vol, Meshable}, mesh::{vol, Meshable},
render::{self, Mesh, TerrainPipeline}, render::{self, FluidPipeline, Mesh, TerrainPipeline},
}; };
use common::{ use common::{
terrain::Block, terrain::{Block, BlockKind},
vol::{BaseVol, ReadVol, VolSize}, vol::{BaseVol, ReadVol, VolSize},
volumes::vol_map_2d::VolMap2d, volumes::vol_map_2d::VolMap2d,
}; };
@ -11,43 +11,28 @@ use std::fmt::Debug;
use vek::*; use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex; type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
/* fn block_shadow_density(kind: BlockKind) -> Option<f32> {
impl<M> Meshable for Dyna<Block, M> { match kind {
type Pipeline = TerrainPipeline; BlockKind::Air => None,
type Supplement = (); BlockKind::Normal => Some(0.85),
BlockKind::Dense => Some(3.0),
fn generate_mesh(&self, _: Self::Supplement) -> Mesh<Self::Pipeline> { BlockKind::Water => Some(0.01),
let mut mesh = Mesh::new();
for pos in self
.iter_positions()
.filter(|pos| pos.map(|e| e >= 1).reduce_and())
.filter(|pos| {
pos.map2(self.get_size(), |e, sz| e < sz as i32 - 1)
.reduce_and()
})
{
let offs = pos.map(|e| e as f32 - 1.0);
if let Some(col) = self.get(pos).ok().and_then(|vox| vox.get_color()) {
let col = col.map(|e| e as f32 / 255.0);
vol::push_vox_verts(&mut mesh, self, pos, offs, col, TerrainVertex::new, true);
}
}
mesh
} }
} }
*/
impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for VolMap2d<V, S> { impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for VolMap2d<V, S> {
type Pipeline = TerrainPipeline; type Pipeline = TerrainPipeline;
type TranslucentPipeline = FluidPipeline;
type Supplement = Aabb<i32>; type Supplement = Aabb<i32>;
fn generate_mesh(&self, range: Self::Supplement) -> Mesh<Self::Pipeline> { fn generate_mesh(
let mut mesh = Mesh::new(); &self,
range: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
for x in range.min.x + 1..range.max.x - 1 { for x in range.min.x + 1..range.max.x - 1 {
for y in range.min.y + 1..range.max.y - 1 { for y in range.min.y + 1..range.max.y - 1 {
@ -55,16 +40,19 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
for z in (range.min.z..range.max.z).rev() { for z in (range.min.z..range.max.z).rev() {
let pos = Vec3::new(x, y, z); let pos = Vec3::new(x, y, z);
let offs = (pos - (range.min + 1) * Vec3::new(1, 1, 0)).map(|e| e as f32);
let block = self.get(pos).ok();
// Create mesh polygons // Create mesh polygons
if let Some(col) = self.get(pos).ok().and_then(|vox| vox.get_color()) { if let Some(col) = block
.filter(|vox| vox.is_opaque())
.and_then(|vox| vox.get_color())
{
let col = col.map(|e| e as f32 / 255.0); let col = col.map(|e| e as f32 / 255.0);
let offs = (pos - range.min * Vec3::new(1, 1, 0)).map(|e| e as f32)
- Vec3::new(1.0, 1.0, 0.0);
vol::push_vox_verts( vol::push_vox_verts(
&mut mesh, &mut opaque_mesh,
self, self,
pos, pos,
offs, offs,
@ -74,6 +62,26 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
}, },
false, false,
&neighbour_light, &neighbour_light,
|vox| !vox.is_opaque(),
);
} else if let Some(col) = block
.filter(|vox| vox.is_fluid())
.and_then(|vox| vox.get_color())
{
let col = col.map(|e| e as f32 / 255.0);
vol::push_vox_verts(
&mut fluid_mesh,
self,
pos,
offs,
col,
|pos, norm, col, ao, light| {
FluidVertex::new(pos, norm, col, light * ao, 0.3)
},
false,
&neighbour_light,
|vox| vox.is_air(),
); );
} }
@ -84,13 +92,13 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
// Accumulate shade under opaque blocks // Accumulate shade under opaque blocks
for i in 0..3 { for i in 0..3 {
for j in 0..3 { for j in 0..3 {
neighbour_light[0][i][j] = if let Some(opacity) = self neighbour_light[0][i][j] = if let Some(density) = self
.get(pos + Vec3::new(i as i32 - 1, j as i32 - 1, -1)) .get(pos + Vec3::new(i as i32 - 1, j as i32 - 1, -1))
.ok() .ok()
.and_then(|vox| vox.get_opacity()) .and_then(|vox| block_shadow_density(vox.kind()))
{ {
(neighbour_light[0][i][j] * (1.0 - opacity * 0.1)) (neighbour_light[0][i][j] * (1.0 - density * 0.1))
.max(1.0 - opacity) .max(1.0 - density)
} else { } else {
(neighbour_light[0][i][j] * 1.025).min(1.0) (neighbour_light[0][i][j] * 1.025).min(1.0)
}; };
@ -108,7 +116,8 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone> Meshable for
} }
} }
} }
mesh
(opaque_mesh, fluid_mesh)
} }
} }

View File

@ -88,26 +88,23 @@ fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P
} }
} }
pub fn push_vox_verts< pub fn push_vox_verts<V: ReadVol, P: Pipeline>(
V: ReadVol,
P: Pipeline,
F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex,
>(
mesh: &mut Mesh<P>, mesh: &mut Mesh<P>,
vol: &V, vol: &V,
pos: Vec3<i32>, pos: Vec3<i32>,
offs: Vec3<f32>, offs: Vec3<f32>,
col: Rgb<f32>, col: Rgb<f32>,
vcons: F, vcons: impl Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex,
error_makes_face: bool, error_makes_face: bool,
darknesses: &[[[f32; 3]; 3]; 3], darknesses: &[[[f32; 3]; 3]; 3],
should_add: impl Fn(&V::Vox) -> bool,
) { ) {
let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z());
// -x // -x
if vol if vol
.get(pos - Vec3::unit_x()) .get(pos - Vec3::unit_x())
.map(|v| v.is_empty()) .map(|v| should_add(v))
.unwrap_or(error_makes_face) .unwrap_or(error_makes_face)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
@ -123,7 +120,7 @@ pub fn push_vox_verts<
// +x // +x
if vol if vol
.get(pos + Vec3::unit_x()) .get(pos + Vec3::unit_x())
.map(|v| v.is_empty()) .map(|v| should_add(v))
.unwrap_or(error_makes_face) .unwrap_or(error_makes_face)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
@ -139,7 +136,7 @@ pub fn push_vox_verts<
// -y // -y
if vol if vol
.get(pos - Vec3::unit_y()) .get(pos - Vec3::unit_y())
.map(|v| v.is_empty()) .map(|v| should_add(v))
.unwrap_or(error_makes_face) .unwrap_or(error_makes_face)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
@ -155,7 +152,7 @@ pub fn push_vox_verts<
// +y // +y
if vol if vol
.get(pos + Vec3::unit_y()) .get(pos + Vec3::unit_y())
.map(|v| v.is_empty()) .map(|v| should_add(v))
.unwrap_or(error_makes_face) .unwrap_or(error_makes_face)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
@ -171,7 +168,7 @@ pub fn push_vox_verts<
// -z // -z
if vol if vol
.get(pos - Vec3::unit_z()) .get(pos - Vec3::unit_z())
.map(|v| v.is_empty()) .map(|v| should_add(v))
.unwrap_or(error_makes_face) .unwrap_or(error_makes_face)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
@ -187,7 +184,7 @@ pub fn push_vox_verts<
// +z // +z
if vol if vol
.get(pos + Vec3::unit_z()) .get(pos + Vec3::unit_z())
.map(|v| v.is_empty()) .map(|v| should_add(v))
.unwrap_or(error_makes_face) .unwrap_or(error_makes_face)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(

View File

@ -13,6 +13,7 @@ pub use self::{
model::{DynamicModel, Model}, model::{DynamicModel, Model},
pipelines::{ pipelines::{
figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals}, figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals},
fluid::FluidPipeline,
postprocess::{ postprocess::{
create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline, create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline,
}, },
@ -22,7 +23,6 @@ pub use self::{
create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals, create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals,
Mode as UiMode, UiPipeline, Mode as UiMode, UiPipeline,
}, },
fluid::{Locals as FluidLocals, FluidPipeline},
Globals, Light, Globals, Light,
}, },
renderer::{Renderer, TgtColorFmt, TgtDepthFmt, WinColorFmt, WinDepthFmt}, renderer::{Renderer, TgtColorFmt, TgtDepthFmt, WinColorFmt, WinDepthFmt},

View File

@ -1,16 +1,16 @@
use super::{ use super::{
super::{Pipeline, TgtColorFmt, TgtDepthFmt}, super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthFmt},
Globals, Light, Globals, Light,
}; };
use gfx::{ use gfx::{
self, self,
gfx_constant_struct_meta,
// Macros // Macros
gfx_defines, gfx_defines,
gfx_impl_struct_meta, gfx_impl_struct_meta,
gfx_pipeline, gfx_pipeline,
gfx_pipeline_inner, gfx_pipeline_inner,
gfx_vertex_struct_meta, gfx_vertex_struct_meta,
state::ColorMask,
}; };
use std::ops::Mul; use std::ops::Mul;
use vek::*; use vek::*;
@ -21,18 +21,14 @@ gfx_defines! {
col_light: u32 = "v_col_light", col_light: u32 = "v_col_light",
} }
constant Locals {
model_offs: [f32; 3] = "model_offs",
}
pipeline pipe { pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (), vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals", locals: gfx::ConstantBuffer<TerrainLocals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals", globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights", lights: gfx::ConstantBuffer<Light> = "u_lights",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color", tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE, tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
} }
} }
@ -57,16 +53,8 @@ impl Vertex {
| ((col.r.mul(200.0) as u32) & 0xFF) << 8 | ((col.r.mul(200.0) as u32) & 0xFF) << 8
| ((col.g.mul(200.0) as u32) & 0xFF) << 16 | ((col.g.mul(200.0) as u32) & 0xFF) << 16
| ((col.b.mul(200.0) as u32) & 0xFF) << 24 | ((col.b.mul(200.0) as u32) & 0xFF) << 24
| ((light.mul(255.0) as u32) & 0xFF) << 0 | ((light.mul(255.0) as u32) & 0xFF) << 0,
| ((opac.mul(0.4) as u32) & 0xFF) << 0, //| ((opac.mul(0.4) as u32) & 0xFF) << 0,
}
}
}
impl Locals {
pub fn default() -> Self {
Self {
model_offs: [0.0; 3],
} }
} }
} }

View File

@ -1,9 +1,9 @@
pub mod figure; pub mod figure;
pub mod fluid;
pub mod postprocess; pub mod postprocess;
pub mod skybox; pub mod skybox;
pub mod terrain; pub mod terrain;
pub mod ui; pub mod ui;
pub mod fluid;
use super::util::arr_to_mat; use super::util::arr_to_mat;
use gfx::{ use gfx::{

View File

@ -3,7 +3,7 @@ use super::{
gfx_backend, gfx_backend,
mesh::Mesh, mesh::Mesh,
model::{DynamicModel, Model}, model::{DynamicModel, Model},
pipelines::{figure, postprocess, skybox, terrain, ui, Globals, Light, fluid}, pipelines::{figure, fluid, postprocess, skybox, terrain, ui, Globals, Light},
texture::Texture, texture::Texture,
Pipeline, RenderError, Pipeline, RenderError,
}; };
@ -64,6 +64,7 @@ pub struct Renderer {
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>, skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>, figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>, terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>,
fluid_pipeline: GfxPipeline<fluid::pipe::Init<'static>>,
ui_pipeline: GfxPipeline<ui::pipe::Init<'static>>, ui_pipeline: GfxPipeline<ui::pipe::Init<'static>>,
postprocess_pipeline: GfxPipeline<postprocess::pipe::Init<'static>>, postprocess_pipeline: GfxPipeline<postprocess::pipe::Init<'static>>,
@ -80,18 +81,9 @@ impl Renderer {
) -> Result<Self, RenderError> { ) -> Result<Self, RenderError> {
let mut shader_reload_indicator = ReloadIndicator::new(); let mut shader_reload_indicator = ReloadIndicator::new();
let (skybox_pipeline, figure_pipeline, terrain_pipeline, ui_pipeline, postprocess_pipeline) = let (skybox_pipeline, figure_pipeline, terrain_pipeline, fluid_pipeline, ui_pipeline, postprocess_pipeline) =
create_pipelines(&mut factory, &mut shader_reload_indicator)?; create_pipelines(&mut factory, &mut shader_reload_indicator)?;
// Construct a pipeline for rendering fluids
let fluid_pipeline = create_pipeline(
&mut factory,
fluid::pipe::new(),
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/fluid.vert")),
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/fluid.frag")),
&include_ctx,
)?;
let dims = win_color_view.get_dimensions(); let dims = win_color_view.get_dimensions();
let (tgt_color_view, tgt_depth_view, tgt_color_res) = let (tgt_color_view, tgt_depth_view, tgt_color_res) =
Self::create_rt_views(&mut factory, (dims.0, dims.1))?; Self::create_rt_views(&mut factory, (dims.0, dims.1))?;
@ -115,6 +107,7 @@ impl Renderer {
skybox_pipeline, skybox_pipeline,
figure_pipeline, figure_pipeline,
terrain_pipeline, terrain_pipeline,
fluid_pipeline,
ui_pipeline, ui_pipeline,
postprocess_pipeline, postprocess_pipeline,
@ -200,13 +193,15 @@ impl Renderer {
Ok(( Ok((
skybox_pipeline, skybox_pipeline,
figure_pipeline, figure_pipeline,
terrain_pipline, terrain_pipeline,
fluid_pipeline,
ui_pipeline, ui_pipeline,
postprocess_pipeline, postprocess_pipeline,
)) => { )) => {
self.skybox_pipeline = skybox_pipeline; self.skybox_pipeline = skybox_pipeline;
self.figure_pipeline = figure_pipeline; self.figure_pipeline = figure_pipeline;
self.terrain_pipeline = terrain_pipline; self.terrain_pipeline = terrain_pipeline;
self.fluid_pipeline = fluid_pipeline;
self.ui_pipeline = ui_pipeline; self.ui_pipeline = ui_pipeline;
self.postprocess_pipeline = postprocess_pipeline; self.postprocess_pipeline = postprocess_pipeline;
} }
@ -407,6 +402,28 @@ impl Renderer {
); );
} }
/// Queue the rendering of the provided terrain chunk model in the upcoming frame.
pub fn render_fluid_chunk(
&mut self,
model: &Model<fluid::FluidPipeline>,
globals: &Consts<Globals>,
locals: &Consts<terrain::Locals>,
lights: &Consts<Light>,
) {
self.encoder.draw(
&model.slice,
&self.fluid_pipeline.pso,
&fluid::pipe::Data {
vbuf: model.vbuf.clone(),
locals: locals.buf.clone(),
globals: globals.buf.clone(),
lights: lights.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},
);
}
/// Queue the rendering of the provided UI element in the upcoming frame. /// Queue the rendering of the provided UI element in the upcoming frame.
pub fn render_ui_element( pub fn render_ui_element(
&mut self, &mut self,
@ -471,6 +488,7 @@ fn create_pipelines(
GfxPipeline<skybox::pipe::Init<'static>>, GfxPipeline<skybox::pipe::Init<'static>>,
GfxPipeline<figure::pipe::Init<'static>>, GfxPipeline<figure::pipe::Init<'static>>,
GfxPipeline<terrain::pipe::Init<'static>>, GfxPipeline<terrain::pipe::Init<'static>>,
GfxPipeline<fluid::pipe::Init<'static>>,
GfxPipeline<ui::pipe::Init<'static>>, GfxPipeline<ui::pipe::Init<'static>>,
GfxPipeline<postprocess::pipe::Init<'static>>, GfxPipeline<postprocess::pipe::Init<'static>>,
), ),
@ -528,6 +546,17 @@ fn create_pipelines(
&include_ctx, &include_ctx,
)?; )?;
// Construct a pipeline for rendering fluids
let fluid_pipeline = create_pipeline(
factory,
fluid::pipe::new(),
&assets::load_watched::<String>("voxygen.shaders.fluid-vert", shader_reload_indicator)
.unwrap(),
&assets::load_watched::<String>("voxygen.shaders.fluid-frag", shader_reload_indicator)
.unwrap(),
&include_ctx,
)?;
// Construct a pipeline for rendering UI elements // Construct a pipeline for rendering UI elements
let ui_pipeline = create_pipeline( let ui_pipeline = create_pipeline(
factory, factory,
@ -560,6 +589,7 @@ fn create_pipelines(
skybox_pipeline, skybox_pipeline,
figure_pipeline, figure_pipeline,
terrain_pipeline, terrain_pipeline,
fluid_pipeline,
ui_pipeline, ui_pipeline,
postprocess_pipeline, postprocess_pipeline,
)) ))

View File

@ -165,6 +165,7 @@ impl FigureModelCache {
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat(); let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
Segment::from(assets::load_expect::<DotVoxData>(full_specifier.as_str()).as_ref()) Segment::from(assets::load_expect::<DotVoxData>(full_specifier.as_str()).as_ref())
.generate_mesh(position) .generate_mesh(position)
.0
} }
fn load_head(race: humanoid::Race, body_type: humanoid::BodyType) -> Mesh<FigurePipeline> { fn load_head(race: humanoid::Race, body_type: humanoid::BodyType) -> Mesh<FigurePipeline> {

View File

@ -221,9 +221,9 @@ impl Scene {
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
// Render terrain and figures. // Render terrain and figures.
self.terrain.render(renderer, &self.globals, &self.lights);
self.figure_mgr self.figure_mgr
.render(renderer, client, &self.globals, &self.lights, &self.camera); .render(renderer, client, &self.globals, &self.lights, &self.camera);
self.terrain.render(renderer, &self.globals, &self.lights);
renderer.render_post_process( renderer.render_post_process(
&self.postprocess.model, &self.postprocess.model,

View File

@ -1,6 +1,9 @@
use crate::{ use crate::{
mesh::Meshable, mesh::Meshable,
render::{Consts, Globals, Light, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline}, render::{
Consts, FluidPipeline, Globals, Light, Mesh, Model, Renderer, TerrainLocals,
TerrainPipeline,
},
}; };
use client::Client; use client::Client;
use common::{ use common::{
@ -16,7 +19,8 @@ use vek::*;
struct TerrainChunk { struct TerrainChunk {
// GPU data // GPU data
model: Model<TerrainPipeline>, opaque_model: Model<TerrainPipeline>,
fluid_model: Model<FluidPipeline>,
locals: Consts<TerrainLocals>, locals: Consts<TerrainLocals>,
visible: bool, visible: bool,
z_bounds: (f32, f32), z_bounds: (f32, f32),
@ -32,7 +36,8 @@ struct ChunkMeshState {
struct MeshWorkerResponse { struct MeshWorkerResponse {
pos: Vec2<i32>, pos: Vec2<i32>,
z_bounds: (f32, f32), z_bounds: (f32, f32),
mesh: Mesh<TerrainPipeline>, opaque_mesh: Mesh<TerrainPipeline>,
fluid_mesh: Mesh<FluidPipeline>,
started_tick: u64, started_tick: u64,
} }
@ -44,10 +49,12 @@ fn mesh_worker(
volume: <TerrainMap as SampleVol<Aabr<i32>>>::Sample, volume: <TerrainMap as SampleVol<Aabr<i32>>>::Sample,
range: Aabb<i32>, range: Aabb<i32>,
) -> MeshWorkerResponse { ) -> MeshWorkerResponse {
let (opaque_mesh, fluid_mesh) = volume.generate_mesh(range);
MeshWorkerResponse { MeshWorkerResponse {
pos, pos,
z_bounds, z_bounds,
mesh: volume.generate_mesh(range), opaque_mesh,
fluid_mesh,
started_tick, started_tick,
} }
} }
@ -262,8 +269,11 @@ impl Terrain {
self.chunks.insert( self.chunks.insert(
response.pos, response.pos,
TerrainChunk { TerrainChunk {
model: renderer opaque_model: renderer
.create_model(&response.mesh) .create_model(&response.opaque_mesh)
.expect("Failed to upload chunk mesh to the GPU!"),
fluid_model: renderer
.create_model(&response.fluid_mesh)
.expect("Failed to upload chunk mesh to the GPU!"), .expect("Failed to upload chunk mesh to the GPU!"),
locals: renderer locals: renderer
.create_consts(&[TerrainLocals { .create_consts(&[TerrainLocals {
@ -334,9 +344,17 @@ impl Terrain {
globals: &Consts<Globals>, globals: &Consts<Globals>,
lights: &Consts<Light>, lights: &Consts<Light>,
) { ) {
// Opaque
for (_pos, chunk) in &self.chunks { for (_pos, chunk) in &self.chunks {
if chunk.visible { if chunk.visible {
renderer.render_terrain_chunk(&chunk.model, globals, &chunk.locals, lights); renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights);
}
}
// Translucent
for (_pos, chunk) in &self.chunks {
if chunk.visible {
renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights);
} }
} }
} }

View File

@ -8,7 +8,13 @@ use crate::{
}; };
use client::{self, Client}; use client::{self, Client};
use common::{ use common::{
clock::Clock, comp, comp::Pos, comp::Vel, msg::ClientState, terrain::Block, vol::ReadVol, clock::Clock,
comp,
comp::Pos,
comp::Vel,
msg::ClientState,
terrain::{Block, BlockKind},
vol::ReadVol,
}; };
use log::error; use log::error;
use specs::Join; use specs::Join;
@ -39,7 +45,7 @@ impl SessionState {
key_state: KeyState::new(), key_state: KeyState::new(),
controller: comp::Controller::default(), controller: comp::Controller::default(),
hud: Hud::new(global_state), hud: Hud::new(global_state),
selected_block: Block::new(1, Rgb::broadcast(255)), selected_block: Block::new(BlockKind::Normal, Rgb::broadcast(255)),
} }
} }
} }

View File

@ -6,7 +6,7 @@ use crate::{
World, CONFIG, World, CONFIG,
}; };
use common::{ use common::{
terrain::{structure::StructureBlock, Block, Structure}, terrain::{structure::StructureBlock, Block, BlockKind, Structure},
util::saturate_srgb, util::saturate_srgb,
vol::{ReadVol, Vox}, vol::{ReadVol, Vox},
}; };
@ -221,8 +221,7 @@ impl<'a> BlockGen<'a> {
// let sand = Block::new(1, Rgb::new(180, 150, 50)); // let sand = Block::new(1, Rgb::new(180, 150, 50));
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130)); // let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
//let water = Block::new(1, Rgb::new(100, 150, 255)); let water = Block::new(BlockKind::Water, Rgb::new(0, 24, 255));
let water = Block::new(1, Rgb::new(0, 24, 255));
let grass_depth = 1.5 + 2.0 * chaos; let grass_depth = 1.5 + 2.0 * chaos;
let block = if (wposf.z as f32) < height - grass_depth { let block = if (wposf.z as f32) < height - grass_depth {
@ -234,9 +233,9 @@ impl<'a> BlockGen<'a> {
// Underground // Underground
if (wposf.z as f32) > alt - 32.0 * chaos { if (wposf.z as f32) > alt - 32.0 * chaos {
Some(Block::new(1, col)) Some(Block::new(BlockKind::Normal, col))
} else { } else {
Some(Block::new(2, col)) Some(Block::new(BlockKind::Dense, col))
} }
} else if (wposf.z as f32) < height { } else if (wposf.z as f32) < height {
let col = Lerp::lerp( let col = Lerp::lerp(
@ -248,7 +247,7 @@ impl<'a> BlockGen<'a> {
); );
// Surface // Surface
Some(Block::new( Some(Block::new(
1, BlockKind::Normal,
saturate_srgb(col, 0.45).map(|e| (e * 255.0) as u8), saturate_srgb(col, 0.45).map(|e| (e * 255.0) as u8),
)) ))
} else if (wposf.z as f32) < water_height { } else if (wposf.z as f32) < water_height {
@ -284,7 +283,7 @@ impl<'a> BlockGen<'a> {
let field2 = RandomField::new(world.sim().seed + 2); let field2 = RandomField::new(world.sim().seed + 2);
Some(Block::new( Some(Block::new(
1, BlockKind::Normal,
stone_col stone_col
- Rgb::new( - Rgb::new(
field0.get(wpos) as u8 % 16, field0.get(wpos) as u8 % 16,
@ -415,7 +414,7 @@ impl StructureInfo {
.map(|e: i32| (e.abs() / 2) * 2) .map(|e: i32| (e.abs() / 2) * 2)
.reduce_max() .reduce_max()
{ {
Some(Block::new(2, Rgb::new(203, 170, 146))) Some(Block::new(BlockKind::Dense, Rgb::new(203, 170, 146)))
} else { } else {
None None
} }
@ -452,39 +451,27 @@ fn block_from_structure(
match sblock { match sblock {
StructureBlock::TemperateLeaves => Some(Block::new( StructureBlock::TemperateLeaves => Some(Block::new(
1, BlockKind::Normal,
Lerp::lerp( Lerp::lerp(Rgb::new(0.0, 132.0, 94.0), Rgb::new(142.0, 181.0, 0.0), lerp)
Rgb::new(0.0, 132.0, 94.0), .map(|e| e as u8),
Rgb::new(142.0, 181.0, 0.0),
lerp,
)
.map(|e| e as u8),
)), )),
StructureBlock::PineLeaves => Some(Block::new( StructureBlock::PineLeaves => Some(Block::new(
1, BlockKind::Normal,
Lerp::lerp( Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp)
Rgb::new(0.0, 108.0, 113.0), .map(|e| e as u8),
Rgb::new(30.0, 156.0, 10.0),
lerp,
)
.map(|e| e as u8),
)), )),
StructureBlock::PalmLeaves => Some(Block::new( StructureBlock::PalmLeaves => Some(Block::new(
1, BlockKind::Normal,
Lerp::lerp( Lerp::lerp(Rgb::new(0.0, 108.0, 113.0), Rgb::new(30.0, 156.0, 10.0), lerp)
Rgb::new(15.0, 156.0, 70.0), .map(|e| e as u8),
Rgb::new(40.0, 222.0, 0.0),
lerp,
)
.map(|e| e as u8),
)), )),
StructureBlock::Acacia => Some(Block::new( StructureBlock::Acacia => Some(Block::new(
1, BlockKind::Normal,
Lerp::lerp(Rgb::new(35.0, 156.0, 0.0), Rgb::new(62.0, 208.0, 0.0), lerp) Lerp::lerp(Rgb::new(35.0, 156.0, 0.0), Rgb::new(62.0, 208.0, 0.0), lerp)
.map(|e| e as u8), .map(|e| e as u8),
)), )),
StructureBlock::Fruit => Some(Block::new( StructureBlock::Fruit => Some(Block::new(
1, BlockKind::Normal,
Lerp::lerp(Rgb::new(237.0, 0.0, 0.0), Rgb::new(200.0, 237.0, 0.0), lerp) Lerp::lerp(Rgb::new(237.0, 0.0, 0.0), Rgb::new(200.0, 237.0, 0.0), lerp)
.map(|e| e as u8), .map(|e| e as u8),
)), )),

View File

@ -16,7 +16,7 @@ use crate::{
util::{Sampler, SamplerMut}, util::{Sampler, SamplerMut},
}; };
use common::{ use common::{
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
vol::{ReadVol, VolSize, Vox, WriteVol}, vol::{ReadVol, VolSize, Vox, WriteVol},
}; };
use rand::Rng; use rand::Rng;
@ -59,8 +59,8 @@ impl World {
pub fn generate_chunk(&self, chunk_pos: Vec2<i32>) -> (TerrainChunk, ChunkSupplement) { pub fn generate_chunk(&self, chunk_pos: Vec2<i32>) -> (TerrainChunk, ChunkSupplement) {
let air = Block::empty(); let air = Block::empty();
let stone = Block::new(2, Rgb::new(200, 220, 255)); let stone = Block::new(BlockKind::Dense, Rgb::new(200, 220, 255));
let water = Block::new(5, Rgb::new(100, 150, 255)); let water = Block::new(BlockKind::Water, Rgb::new(100, 150, 255));
let chunk_size2d = Vec2::from(TerrainChunkSize::SIZE); let chunk_size2d = Vec2::from(TerrainChunkSize::SIZE);
let (base_z, sim_chunk) = match self let (base_z, sim_chunk) = match self