diff --git a/client/Cargo.toml b/client/Cargo.toml index a373ba7..53ae4d1 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -23,4 +23,4 @@ rayon = "1.10.0" bincode = "1.3" bevy_app_compute = "0.16" bytemuck = { version = "1.14", features = ["derive"] } - +image = { version = "0.24", default-features = false, features = ["png"] } diff --git a/client/src/plugins/environment/systems/voxel_system.rs b/client/src/plugins/environment/systems/voxel_system.rs index 0cee037..6345a5b 100644 --- a/client/src/plugins/environment/systems/voxel_system.rs +++ b/client/src/plugins/environment/systems/voxel_system.rs @@ -77,7 +77,7 @@ pub fn generate_voxel_sphere_parallel(octree: &mut SparseVoxelOctree, center: Ve center.y + iy as f32 * step, center.z + iz as f32 * step, ); - (pos, Voxel::random_sides()) + (pos, Voxel::grass_block()) }) .collect::>() }) @@ -115,7 +115,7 @@ fn generate_voxel_sphere(octree: &mut SparseVoxelOctree, planet_radius: i32) { let position = Vec3::new(wx, wy, wz); // Insert the voxel - let voxel = Voxel::random_sides(); + let voxel = Voxel::grass_block(); octree.insert(position, voxel); } } @@ -150,7 +150,7 @@ fn generate_voxel_rect(octree: &mut SparseVoxelOctree) { let position = Vec3::new(wx, wy, wz); // Insert the voxel - let voxel = Voxel::random_sides(); + let voxel = Voxel::grass_block(); octree.insert(position, voxel); } } @@ -178,7 +178,7 @@ fn generate_large_plane(octree: &mut SparseVoxelOctree, width: usize, depth: usi let position = Vec3::new(wx, wy, wz); // Insert the voxel - let voxel = Voxel::random_sides(); + let voxel = Voxel::grass_block(); octree.insert(position, voxel); } } @@ -214,9 +214,9 @@ pub fn generate_solid_plane_with_noise( for iy in 0..=max_layer { let position = Vec3::new(x * step, iy as f32 * step, z * step); - let voxel = Voxel::random_sides(); + let voxel = Voxel::grass_block(); octree.insert(position, voxel); } } } -} +} \ No newline at end of file diff --git a/client/src/plugins/environment/systems/voxels/atlas.rs b/client/src/plugins/environment/systems/voxels/atlas.rs index 7ae4f28..8c4b41d 100644 --- a/client/src/plugins/environment/systems/voxels/atlas.rs +++ b/client/src/plugins/environment/systems/voxels/atlas.rs @@ -1,6 +1,7 @@ use bevy::asset::RenderAssetUsages; use bevy::prelude::*; use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat}; +use image::GenericImageView; /// Configuration and handle for the voxel texture atlas. #[derive(Resource, Clone)] @@ -11,32 +12,37 @@ pub struct VoxelTextureAtlas { } impl VoxelTextureAtlas { - /// Create a simple procedural atlas with solid colors. + /// Generate an atlas from PNG files located in `assets/textures/packs/mc/grass`. pub fn generate(images: &mut Assets) -> Self { - let tile_size = 16u32; - let columns = 2; - let rows = 3; + // Include the PNG files at compile time so we don't rely on runtime IO. + const TOP: &[u8] = include_bytes!("../../../../../assets/textures/packs/mc/grass/grass_block_top.png"); + const BOTTOM: &[u8] = include_bytes!("../../../../../assets/textures/packs/mc/grass/dirt.png"); + const SIDE: &[u8] = include_bytes!("../../../../../assets/textures/packs/mc/grass/grass_block_side.png"); + + let textures = [TOP, BOTTOM, SIDE]; + // Assume all textures have the same dimensions + let first = image::load_from_memory(TOP).expect("failed to load texture"); + let tile_size = first.width(); + + let columns = textures.len(); + let rows = 1usize; let width = tile_size * columns as u32; - let height = tile_size * rows as u32; + let height = tile_size; let mut data = vec![0u8; (width * height * 4) as usize]; - let colors = [ - [255, 0, 0, 255], // 0: red - [0, 0, 0, 255], // 1: black - [0, 255, 0, 255], // 2: green - [0, 0, 255, 255], // 3: blue - [255, 255, 0, 255], // 4: yellow - [255, 0, 255, 255], // 5: magenta - ]; - for (i, col) in colors.iter().enumerate() { - let cx = (i % columns) as u32 * tile_size; - let cy = (i / columns) as u32 * tile_size; + + for (i, tex_bytes) in textures.iter().enumerate() { + let img = image::load_from_memory(tex_bytes) + .expect("failed to load texture") + .to_rgba8(); for y in 0..tile_size { for x in 0..tile_size { - let idx = (((cy + y) * width + (cx + x)) * 4) as usize; - data[idx..idx + 4].copy_from_slice(col); + let idx = (((y) * width + x + (i as u32) * tile_size) * 4) as usize; + let pixel = img.get_pixel(x, y).0; + data[idx..idx + 4].copy_from_slice(&pixel); } } } + let image = Image::new_fill( Extent3d { width, @@ -48,6 +54,7 @@ impl VoxelTextureAtlas { TextureFormat::Rgba8UnormSrgb, RenderAssetUsages::default(), ); + let handle = images.add(image); Self { handle, @@ -68,4 +75,4 @@ impl VoxelTextureAtlas { let v1 = (row + 1) as f32 / rows; [[u0, v1], [u1, v1], [u1, v0], [u0, v0]] } -} +} \ No newline at end of file diff --git a/client/src/plugins/environment/systems/voxels/structure.rs b/client/src/plugins/environment/systems/voxels/structure.rs index 215ee09..3157a1e 100644 --- a/client/src/plugins/environment/systems/voxels/structure.rs +++ b/client/src/plugins/environment/systems/voxels/structure.rs @@ -84,6 +84,16 @@ impl Voxel { } Self { textures } } + + // Generate a simple grass block using the first three atlas indices. + /// Index 0: grass top, index 1: dirt (bottom), index 2: grass sides. + pub fn grass_block() -> Self { + let mut textures = [2usize; 6]; + // Face order: left, right, bottom, top, back, front + textures[3] = 0; // top + textures[2] = 1; // bottom + Self { textures } + } } pub const NEIGHBOR_OFFSETS: [(f32, f32, f32); 6] = [ diff --git a/client/src/plugins/input/systems/voxels.rs b/client/src/plugins/input/systems/voxels.rs index 151f578..2c4bfdd 100644 --- a/client/src/plugins/input/systems/voxels.rs +++ b/client/src/plugins/input/systems/voxels.rs @@ -36,7 +36,7 @@ pub fn voxel_system( if keyboard_input.just_pressed(KeyCode::KeyQ) && window.cursor_options.visible == false { for mut octree in octree_query.iter_mut() { - octree.insert(transform.translation, Voxel::random_sides()); + octree.insert(transform.translation, Voxel::grass_block()); } } if keyboard_input.just_pressed(KeyCode::F4) {