use crate::chunk::{Chunk, ChunkNeighbors, Face};
#[inline]
pub fn vertex_ao(side1: bool, side2: bool, corner: bool) -> u8 {
if side1 && side2 {
return 0;
}
3 - (side1 as u8 + side2 as u8 + corner as u8)
}
#[inline]
pub fn sample_block_opaque(
chunk: &Chunk,
neighbors: &ChunkNeighbors,
x: i32,
y: i32,
z: i32,
) -> bool {
let size = chunk.size() as i32;
if x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size {
return chunk.get(x as usize, y as usize, z as usize) != 0;
}
if x < 0 && y >= 0 && y < size && z >= 0 && z < size {
return neighbors.get_border_block(Face::NegX, z as usize, y as usize) != 0;
}
if x >= size && y >= 0 && y < size && z >= 0 && z < size {
return neighbors.get_border_block(Face::PosX, z as usize, y as usize) != 0;
}
if y < 0 && x >= 0 && x < size && z >= 0 && z < size {
return neighbors.get_border_block(Face::NegY, x as usize, z as usize) != 0;
}
if y >= size && x >= 0 && x < size && z >= 0 && z < size {
return neighbors.get_border_block(Face::PosY, x as usize, z as usize) != 0;
}
if z < 0 && x >= 0 && x < size && y >= 0 && y < size {
return neighbors.get_border_block(Face::NegZ, x as usize, y as usize) != 0;
}
if z >= size && x >= 0 && x < size && y >= 0 && y < size {
return neighbors.get_border_block(Face::PosZ, x as usize, y as usize) != 0;
}
false
}
pub fn face_ao(
chunk: &Chunk,
neighbors: &ChunkNeighbors,
x: i32,
y: i32,
z: i32,
face: Face,
) -> [f32; 4] {
let n = face.normal();
let (u_axis, v_axis) = face.tangent_axes();
let ox = x + n[0];
let oy = y + n[1];
let oz = z + n[2];
let mut u_dir = [0i32; 3];
let mut v_dir = [0i32; 3];
u_dir[u_axis] = 1;
v_dir[v_axis] = 1;
let sample = |du: i32, dv: i32| -> bool {
sample_block_opaque(
chunk,
neighbors,
ox + u_dir[0] * du + v_dir[0] * dv,
oy + u_dir[1] * du + v_dir[1] * dv,
oz + u_dir[2] * du + v_dir[2] * dv,
)
};
let neg_u = sample(-1, 0);
let pos_u = sample(1, 0);
let neg_v = sample(0, -1);
let pos_v = sample(0, 1);
let neg_u_neg_v = sample(-1, -1);
let pos_u_neg_v = sample(1, -1);
let pos_u_pos_v = sample(1, 1);
let neg_u_pos_v = sample(-1, 1);
let ao0 = vertex_ao(neg_u, neg_v, neg_u_neg_v);
let ao1 = vertex_ao(pos_u, neg_v, pos_u_neg_v);
let ao2 = vertex_ao(pos_u, pos_v, pos_u_pos_v);
let ao3 = vertex_ao(neg_u, pos_v, neg_u_pos_v);
const SCALE: f32 = 1.0 / 3.0;
[
ao0 as f32 * SCALE,
ao1 as f32 * SCALE,
ao2 as f32 * SCALE,
ao3 as f32 * SCALE,
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vertex_ao_no_occlusion() {
assert_eq!(vertex_ao(false, false, false), 3);
}
#[test]
fn vertex_ao_one_side() {
assert_eq!(vertex_ao(true, false, false), 2);
assert_eq!(vertex_ao(false, true, false), 2);
}
#[test]
fn vertex_ao_corner_only() {
assert_eq!(vertex_ao(false, false, true), 2);
}
#[test]
fn vertex_ao_one_side_and_corner() {
assert_eq!(vertex_ao(true, false, true), 1);
assert_eq!(vertex_ao(false, true, true), 1);
}
#[test]
fn vertex_ao_both_sides() {
assert_eq!(vertex_ao(true, true, false), 0);
assert_eq!(vertex_ao(true, true, true), 0);
}
#[test]
fn face_ao_no_neighbors() {
let mut chunk = Chunk::new_default();
chunk.set(5, 5, 5, 1);
let neighbors = ChunkNeighbors::empty(32);
let ao = face_ao(&chunk, &neighbors, 5, 5, 5, Face::PosY);
assert_eq!(ao, [1.0, 1.0, 1.0, 1.0]);
}
#[test]
fn face_ao_with_adjacent_blocks() {
let mut chunk = Chunk::new_default();
chunk.set(5, 5, 5, 1);
chunk.set(4, 6, 5, 1); chunk.set(5, 6, 4, 1); chunk.set(4, 6, 4, 1);
let neighbors = ChunkNeighbors::empty(32);
let ao = face_ao(&chunk, &neighbors, 5, 5, 5, Face::PosY);
assert_eq!(ao[0], 0.0);
assert!(ao[1] > 0.0);
assert!(ao[2] > 0.0);
assert!(ao[3] > 0.0);
}
}