veloren/voxygen/src/mesh/terrain.rs

323 lines
12 KiB
Rust
Raw Normal View History

2019-06-05 15:22:06 +00:00
use crate::{
mesh::{vol, Meshable},
render::{self, FluidPipeline, Mesh, TerrainPipeline},
2019-06-05 15:22:06 +00:00
};
2019-01-23 20:01:58 +00:00
use common::{
terrain::{Block, BlockKind},
vol::{ReadVol, RectRasterableVol, Vox},
2019-09-24 06:42:09 +00:00
volumes::vol_grid_2d::VolGrid2d,
2019-01-23 20:01:58 +00:00
};
use hashbrown::{HashMap, HashSet};
2019-06-05 15:22:06 +00:00
use std::fmt::Debug;
use vek::*;
2019-01-23 20:01:58 +00:00
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
const DIRS: [Vec2<i32>; 4] = [
Vec2 { x: 1, y: 0 },
Vec2 { x: 0, y: 1 },
Vec2 { x: -1, y: 0 },
Vec2 { x: 0, y: -1 },
];
const DIRS_3D: [Vec3<i32>; 6] = [
Vec3 { x: 1, y: 0, z: 0 },
Vec3 { x: 0, y: 1, z: 0 },
Vec3 { x: 0, y: 0, z: 1 },
Vec3 { x: -1, y: 0, z: 0 },
Vec3 { x: 0, y: -1, z: 0 },
Vec3 { x: 0, y: 0, z: -1 },
];
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
bounds: Aabb<i32>,
vol: &VolGrid2d<V>,
) -> impl Fn(Vec3<i32>) -> f32 {
let sunlight = 24;
let outer = Aabb {
min: bounds.min - sunlight,
max: bounds.max + sunlight,
};
let mut voids = HashMap::new();
let mut rays = vec![outer.size().d; outer.size().product() as usize];
for x in 0..outer.size().w {
for y in 0..outer.size().h {
let mut outside = true;
for z in (0..outer.size().d).rev() {
if vol
.get(outer.min + Vec3::new(x, y, z))
.map(|vox| vox.is_air())
.unwrap_or(true)
{
if !outside {
voids.insert(Vec3::new(x, y, z), None);
}
} else if outside {
rays[(outer.size().w * y + x) as usize] = z;
outside = false;
}
}
}
}
let mut opens = HashSet::new();
for (pos, l) in &mut voids {
for dir in &DIRS {
let col = Vec2::<i32>::from(*pos) + dir;
if pos.z
> *rays
.get(((outer.size().w * col.y) + col.x) as usize)
.unwrap_or(&0)
{
*l = Some(sunlight - 1);
opens.insert(*pos);
}
}
}
2019-09-24 00:29:24 +00:00
while opens.len() > 0 {
let mut new_opens = HashSet::new();
for open in &opens {
let parent_l = voids[open].unwrap_or(0);
for dir in &DIRS_3D {
let other = *open + *dir;
if !opens.contains(&other) {
if let Some(l) = voids.get_mut(&other) {
if l.unwrap_or(0) < parent_l - 1 {
new_opens.insert(other);
}
*l = Some(parent_l - 1);
}
}
}
}
opens = new_opens;
}
move |wpos| {
let pos = wpos - outer.min;
rays.get(((outer.size().w * pos.y) + pos.x) as usize)
.and_then(|ray| if pos.z > *ray { Some(1.0) } else { None })
.or_else(|| {
if let Some(Some(l)) = voids.get(&pos) {
Some(*l as f32 / sunlight as f32)
} else {
None
}
})
.unwrap_or(0.0)
}
}
fn block_shadow_density(kind: BlockKind) -> (f32, f32) {
2019-08-19 20:09:35 +00:00
// (density, cap)
match kind {
BlockKind::Normal => (0.085, 0.3),
BlockKind::Dense => (0.3, 0.0),
2019-08-16 14:58:14 +00:00
BlockKind::Water => (0.15, 0.0),
2019-08-19 20:09:35 +00:00
kind if kind.is_air() => (0.0, 0.0),
_ => (1.0, 0.0),
2019-01-23 20:01:58 +00:00
}
}
common: Rework volume API See the doc comments in `common/src/vol.rs` for more information on the API itself. The changes include: * Consistent `Err`/`Error` naming. * Types are named `...Error`. * `enum` variants are named `...Err`. * Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation to an upcoming change where a “map” in the game related sense will be added. * Add volume iterators. There are two types of them: * _Position_ iterators obtained from the trait `IntoPosIterator` using the method `fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `Vec3<i32>`. * _Volume_ iterators obtained from the trait `IntoVolIterator` using the method `fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `(Vec3<i32>, &Self::Vox)`. Those traits will usually be implemented by references to volume types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some type which usually implements several volume traits, such as `Chunk`). * _Position_ iterators iterate over the positions valid for that volume. * _Volume_ iterators do the same but return not only the position but also the voxel at that position, in each iteration. * Introduce trait `RectSizedVol` for the use case which we have with `Chonk`: A `Chonk` is sized only in x and y direction. * Introduce traits `RasterableVol`, `RectRasterableVol` * `RasterableVol` represents a volume that is compile-time sized and has its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen because such a volume can be used with `VolGrid3d`. * `RectRasterableVol` represents a volume that is compile-time sized at least in x and y direction and has its lower bound at `(0, 0, z)`. There's no requirement on he lower bound or size in z direction. The name `RectRasterableVol` was chosen because such a volume can be used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeline, FluidPipeline>
for VolGrid2d<V>
2019-08-19 20:09:35 +00:00
{
type Pipeline = TerrainPipeline;
type TranslucentPipeline = FluidPipeline;
type Supplement = Aabb<i32>;
fn generate_mesh(
&self,
range: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
2019-09-24 06:42:09 +00:00
let light = calc_light(range, self);
for x in range.min.x + 1..range.max.x - 1 {
for y in range.min.y + 1..range.max.y - 1 {
for z in (range.min.z..range.max.z).rev() {
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
if let Some(col) = block
.filter(|vox| vox.is_opaque())
.and_then(|vox| vox.get_color())
{
vol::push_vox_verts(
&mut opaque_mesh,
self,
pos,
offs,
2019-09-24 06:42:09 +00:00
&{
let mut cols = [[[None; 3]; 3]; 3];
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
cols[x][y][z] = self
.get(
pos + Vec3::new(x as i32, y as i32, z as i32)
- 1,
)
.ok()
.filter(|vox| vox.is_opaque())
.and_then(|vox| vox.get_color())
.map(|col| col.map(|e| e as f32 / 255.0));
}
}
}
cols
},
2019-06-19 14:55:26 +00:00
|pos, norm, col, ao, light| {
2019-07-04 12:02:26 +00:00
TerrainVertex::new(pos, norm, col, light * ao)
2019-06-19 14:55:26 +00:00
},
false,
&{
let mut ls = [[[0.0; 3]; 3]; 3];
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
ls[x][y][z] = light(
pos + Vec3::new(x as i32, y as i32, z as i32) - 1,
);
}
}
}
ls
},
|vox| !vox.is_opaque(),
|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,
2019-09-24 06:42:09 +00:00
&{
let mut cols = [[[None; 3]; 3]; 3];
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
cols[x][y][z] = self
.get(
pos + Vec3::new(x as i32, y as i32, z as i32)
- 1,
)
.ok()
.filter(|vox| vox.is_fluid())
.and_then(|vox| vox.get_color())
.map(|col| col.map(|e| e as f32 / 255.0));
}
}
}
cols
},
|pos, norm, col, ao, light| {
FluidVertex::new(pos, norm, col, light * ao, 0.3)
},
false,
&{
let mut ls = [[[0.0; 3]; 3]; 3];
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
ls[x][y][z] = light(
pos + Vec3::new(x as i32, y as i32, z as i32) - 1,
);
}
}
}
ls
},
|vox| vox.is_air(),
|vox| vox.is_opaque(),
);
}
}
}
}
(opaque_mesh, fluid_mesh)
}
}
2019-05-31 20:37:11 +00:00
/*
common: Rework volume API See the doc comments in `common/src/vol.rs` for more information on the API itself. The changes include: * Consistent `Err`/`Error` naming. * Types are named `...Error`. * `enum` variants are named `...Err`. * Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation to an upcoming change where a “map” in the game related sense will be added. * Add volume iterators. There are two types of them: * _Position_ iterators obtained from the trait `IntoPosIterator` using the method `fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `Vec3<i32>`. * _Volume_ iterators obtained from the trait `IntoVolIterator` using the method `fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `(Vec3<i32>, &Self::Vox)`. Those traits will usually be implemented by references to volume types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some type which usually implements several volume traits, such as `Chunk`). * _Position_ iterators iterate over the positions valid for that volume. * _Volume_ iterators do the same but return not only the position but also the voxel at that position, in each iteration. * Introduce trait `RectSizedVol` for the use case which we have with `Chonk`: A `Chonk` is sized only in x and y direction. * Introduce traits `RasterableVol`, `RectRasterableVol` * `RasterableVol` represents a volume that is compile-time sized and has its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen because such a volume can be used with `VolGrid3d`. * `RectRasterableVol` represents a volume that is compile-time sized at least in x and y direction and has its lower bound at `(0, 0, z)`. There's no requirement on he lower bound or size in z direction. The name `RectRasterableVol` was chosen because such a volume can be used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
impl<V: BaseVol<Vox = Block> + ReadVol + Debug> Meshable for VolGrid3d<V> {
type Pipeline = TerrainPipeline;
type Supplement = Aabb<i32>;
fn generate_mesh(&self, range: Self::Supplement) -> Mesh<Self::Pipeline> {
let mut mesh = Mesh::new();
let mut last_chunk_pos = self.pos_key(range.min);
let mut last_chunk = self.get_key(last_chunk_pos);
let size = range.max - range.min;
for x in 1..size.x - 1 {
for y in 1..size.y - 1 {
for z in 1..size.z - 1 {
let pos = Vec3::new(x, y, z);
let new_chunk_pos = self.pos_key(range.min + pos);
if last_chunk_pos != new_chunk_pos {
last_chunk = self.get_key(new_chunk_pos);
last_chunk_pos = new_chunk_pos;
}
let offs = pos.map(|e| e as f32 - 1.0);
if let Some(chunk) = last_chunk {
let chunk_pos = Self::chunk_offs(range.min + pos);
if let Some(col) = chunk.get(chunk_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,
range.min + pos,
offs,
col,
TerrainVertex::new,
false,
);
}
} else {
if let Some(col) = self
.get(range.min + 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,
range.min + pos,
offs,
col,
TerrainVertex::new,
false,
);
}
}
}
}
}
mesh
}
}
2019-05-31 20:37:11 +00:00
*/