messy state
This commit is contained in:
317
src/meshing/binary_greedy_mesher.rs
Normal file
317
src/meshing/binary_greedy_mesher.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
use crate::chunk::chunk::CHUNK_SIZE;
|
||||
use crate::chunk::{Chunk, ChunkMesh};
|
||||
use crate::editor::voxel_registry::VoxelRegistry;
|
||||
use crate::meshing::Mesher;
|
||||
use godot::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BinaryGreedyMesher;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct GreedyQuad {
|
||||
x: u32,
|
||||
y: u32,
|
||||
w: u32,
|
||||
h: u32,
|
||||
}
|
||||
|
||||
impl BinaryGreedyMesher {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn generate_mesh(&self, chunk: &Chunk, mesh: &mut ChunkMesh) {
|
||||
mesh.vertices.clear();
|
||||
mesh.normals.clear();
|
||||
mesh.indices.clear();
|
||||
|
||||
// Generate mesh for each of the 6 faces
|
||||
self.mesh_face(chunk, mesh, 0); // -X (left)
|
||||
self.mesh_face(chunk, mesh, 1); // +X (right)
|
||||
self.mesh_face(chunk, mesh, 2); // -Y (down)
|
||||
self.mesh_face(chunk, mesh, 3); // +Y (up)
|
||||
self.mesh_face(chunk, mesh, 4); // -Z (back)
|
||||
self.mesh_face(chunk, mesh, 5); // +Z (forward)
|
||||
}
|
||||
|
||||
fn mesh_face(&self, chunk: &Chunk, mesh: &mut ChunkMesh, face_dir: usize) {
|
||||
let size = CHUNK_SIZE as usize;
|
||||
|
||||
// For each slice perpendicular to the face direction
|
||||
for axis in 0..size {
|
||||
// Generate binary mask for this slice
|
||||
let mut face_mask = [0u32; 32];
|
||||
|
||||
for row in 0..size {
|
||||
for col in 0..size {
|
||||
let (x, y, z) = self.get_coords_for_face(face_dir, axis, row, col);
|
||||
|
||||
if x >= size || y >= size || z >= size {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos = Vector3i::new(x as i32, y as i32, z as i32);
|
||||
let is_solid = chunk.get_voxel(
|
||||
pos,
|
||||
Vector3i {
|
||||
x: 32,
|
||||
y: 32,
|
||||
z: 32,
|
||||
},
|
||||
);
|
||||
|
||||
// Check if neighbor in face direction is air
|
||||
let (nx, ny, nz) = self.get_neighbor_coords(face_dir, x, y, z);
|
||||
let neighbor_is_air = if nx < 0
|
||||
|| nx >= size as i32
|
||||
|| ny < 0
|
||||
|| ny >= size as i32
|
||||
|| nz < 0
|
||||
|| nz >= size as i32
|
||||
{
|
||||
true // Outside chunk = air
|
||||
} else {
|
||||
!chunk.get_voxel(
|
||||
Vector3i::new(nx, ny, nz),
|
||||
Vector3i {
|
||||
x: 32,
|
||||
y: 32,
|
||||
z: 32,
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
// Face is visible if this voxel is solid and neighbor is air
|
||||
if is_solid && neighbor_is_air {
|
||||
face_mask[row] |= 1 << col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Greedy mesh the binary mask
|
||||
let quads = self.greedy_mesh_binary_plane(face_mask);
|
||||
|
||||
// Add quads to mesh
|
||||
for quad in quads {
|
||||
self.add_quad_to_mesh(mesh, quad, face_dir, axis, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_coords_for_face(
|
||||
&self,
|
||||
face_dir: usize,
|
||||
axis: usize,
|
||||
row: usize,
|
||||
col: usize,
|
||||
) -> (usize, usize, usize) {
|
||||
match face_dir {
|
||||
0 => (axis, col, row), // -X: axis=x, row=z, col=y
|
||||
1 => (axis, col, row), // +X: axis=x, row=z, col=y
|
||||
2 => (row, axis, col), // -Y: axis=y, row=x, col=z
|
||||
3 => (row, axis, col), // +Y: axis=y, row=x, col=z
|
||||
4 => (row, col, axis), // -Z: axis=z, row=x, col=y
|
||||
5 => (row, col, axis), // +Z: axis=z, row=x, col=y
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_neighbor_coords(
|
||||
&self,
|
||||
face_dir: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
z: usize,
|
||||
) -> (i32, i32, i32) {
|
||||
let (x, y, z) = (x as i32, y as i32, z as i32);
|
||||
match face_dir {
|
||||
0 => (x - 1, y, z), // -X
|
||||
1 => (x + 1, y, z), // +X
|
||||
2 => (x, y - 1, z), // -Y
|
||||
3 => (x, y + 1, z), // +Y
|
||||
4 => (x, y, z - 1), // -Z
|
||||
5 => (x, y, z + 1), // +Z
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn greedy_mesh_binary_plane(&self, mut data: [u32; 32]) -> Vec<GreedyQuad> {
|
||||
let mut quads = Vec::new();
|
||||
let size = CHUNK_SIZE as u32;
|
||||
|
||||
for row in 0..data.len() {
|
||||
let mut y = 0;
|
||||
while y < size {
|
||||
// Find first solid bit (skip zeros)
|
||||
y += (data[row] >> y).trailing_zeros();
|
||||
if y >= size {
|
||||
break; // Reached end of row
|
||||
}
|
||||
|
||||
// Count consecutive solid bits (height)
|
||||
let h = (data[row] >> y).trailing_ones();
|
||||
|
||||
// Create a mask for these bits
|
||||
let h_as_mask = if h >= 32 { u32::MAX } else { (1u32 << h) - 1 };
|
||||
let mask = h_as_mask << y;
|
||||
|
||||
// Try to expand horizontally
|
||||
let mut w = 1;
|
||||
while row + w < size as usize {
|
||||
// Check if the next row has the same pattern at this position
|
||||
let next_row_bits = (data[row + w] >> y) & h_as_mask;
|
||||
if next_row_bits != h_as_mask {
|
||||
break; // Can't expand further
|
||||
}
|
||||
|
||||
// Clear the bits we're consuming
|
||||
data[row + w] &= !mask;
|
||||
w += 1;
|
||||
}
|
||||
|
||||
quads.push(GreedyQuad {
|
||||
x: row as u32,
|
||||
y,
|
||||
w: w as u32,
|
||||
h,
|
||||
});
|
||||
|
||||
y += h;
|
||||
}
|
||||
}
|
||||
|
||||
quads
|
||||
}
|
||||
|
||||
fn add_quad_to_mesh(
|
||||
&self,
|
||||
mesh: &mut ChunkMesh,
|
||||
quad: GreedyQuad,
|
||||
face_dir: usize,
|
||||
axis: usize,
|
||||
chunk: &Chunk,
|
||||
) {
|
||||
let base_idx = mesh.vertices.len() as i32;
|
||||
|
||||
// Convert quad coordinates back to 3D positions
|
||||
let vertices = self.get_quad_vertices(quad, face_dir, axis);
|
||||
let normal = self.get_face_normal(face_dir);
|
||||
|
||||
// Base color based on face direction with simple directional shading (no per-vertex AO)
|
||||
let (r, g, b) = match face_dir {
|
||||
0 => (0.55, 0.55, 0.55), // -X (left) - darker gray
|
||||
1 => (0.65, 0.65, 0.65), // +X (right) - lighter gray
|
||||
2 => (0.35, 0.35, 0.35), // -Y (down) - dark gray
|
||||
3 => (0.5, 0.8, 0.3), // +Y (up) - grass green (brightest)
|
||||
4 => (0.50, 0.50, 0.50), // -Z (back) - medium gray
|
||||
5 => (0.60, 0.60, 0.60), // +Z (forward) - lighter gray
|
||||
_ => (1.0, 1.0, 1.0),
|
||||
};
|
||||
|
||||
let color = Color::from_rgb(r, g, b);
|
||||
|
||||
// Add vertices with consistent color (no per-vertex variation)
|
||||
for vertex in &vertices {
|
||||
mesh.vertices.push(*vertex);
|
||||
mesh.normals.push(normal);
|
||||
mesh.colors.push(color);
|
||||
}
|
||||
|
||||
// Add indices for two triangles
|
||||
mesh.indices.push(base_idx);
|
||||
mesh.indices.push(base_idx + 1);
|
||||
mesh.indices.push(base_idx + 2);
|
||||
|
||||
mesh.indices.push(base_idx);
|
||||
mesh.indices.push(base_idx + 2);
|
||||
mesh.indices.push(base_idx + 3);
|
||||
}
|
||||
|
||||
fn get_quad_vertices(&self, quad: GreedyQuad, face_dir: usize, axis: usize) -> [Vector3; 4] {
|
||||
let x = quad.x as f32;
|
||||
let y = quad.y as f32;
|
||||
let w = quad.w as f32;
|
||||
let h = quad.h as f32;
|
||||
let a = axis as f32;
|
||||
|
||||
match face_dir {
|
||||
0 => [
|
||||
// -X (left)
|
||||
Vector3::new(a, y, x),
|
||||
Vector3::new(a, y + h, x),
|
||||
Vector3::new(a, y + h, x + w),
|
||||
Vector3::new(a, y, x + w),
|
||||
],
|
||||
1 => [
|
||||
// +X (right)
|
||||
Vector3::new(a + 1.0, y, x),
|
||||
Vector3::new(a + 1.0, y, x + w),
|
||||
Vector3::new(a + 1.0, y + h, x + w),
|
||||
Vector3::new(a + 1.0, y + h, x),
|
||||
],
|
||||
2 => [
|
||||
// -Y (down)
|
||||
Vector3::new(x, a, y),
|
||||
Vector3::new(x, a, y + h),
|
||||
Vector3::new(x + w, a, y + h),
|
||||
Vector3::new(x + w, a, y),
|
||||
],
|
||||
3 => [
|
||||
// +Y (up)
|
||||
Vector3::new(x, a + 1.0, y),
|
||||
Vector3::new(x + w, a + 1.0, y),
|
||||
Vector3::new(x + w, a + 1.0, y + h),
|
||||
Vector3::new(x, a + 1.0, y + h),
|
||||
],
|
||||
4 => [
|
||||
// -Z (back)
|
||||
Vector3::new(x, y, a),
|
||||
Vector3::new(x + w, y, a),
|
||||
Vector3::new(x + w, y + h, a),
|
||||
Vector3::new(x, y + h, a),
|
||||
],
|
||||
5 => [
|
||||
// +Z (forward)
|
||||
Vector3::new(x, y, a + 1.0),
|
||||
Vector3::new(x, y + h, a + 1.0),
|
||||
Vector3::new(x + w, y + h, a + 1.0),
|
||||
Vector3::new(x + w, y, a + 1.0),
|
||||
],
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_face_normal(&self, face_dir: usize) -> Vector3 {
|
||||
match face_dir {
|
||||
0 => Vector3::new(-1.0, 0.0, 0.0), // -X
|
||||
1 => Vector3::new(1.0, 0.0, 0.0), // +X
|
||||
2 => Vector3::new(0.0, -1.0, 0.0), // -Y
|
||||
3 => Vector3::new(0.0, 1.0, 0.0), // +Y
|
||||
4 => Vector3::new(0.0, 0.0, -1.0), // -Z
|
||||
5 => Vector3::new(0.0, 0.0, 1.0), // +Z
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mesher for BinaryGreedyMesher {
|
||||
fn generate_mesh(&self, chunk: &Chunk, mesh: &mut ChunkMesh) {
|
||||
self.generate_mesh(chunk, mesh);
|
||||
}
|
||||
|
||||
fn generate_mesh_with_registry(
|
||||
&self,
|
||||
chunk: &Chunk,
|
||||
_registry: &VoxelRegistry,
|
||||
mesh: &mut ChunkMesh,
|
||||
) {
|
||||
// Binary greedy mesher doesn't use registry (only solid/air)
|
||||
self.generate_mesh(chunk, mesh);
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &str {
|
||||
"BinaryGreedyMesher"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user