messy state

This commit is contained in:
2026-03-12 19:25:03 +03:30
commit a47f1f9818
30 changed files with 2791 additions and 0 deletions

View 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"
}
}