//! An example that showcases how to update the mesh. #[allow(unused_imports, dead_code)] use bevy::pbr::wireframe::{Wireframe, WireframeConfig, WireframePlugin}; use bevy::prelude::*; use bevy_meshem::prelude::*; use rand::prelude::*; /// Constants for us to use. const FACTOR: usize = 8; const CHUNK_LEN: usize = FACTOR * FACTOR * FACTOR; const SPEED: f32 = FACTOR as f32 * 2.0; fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins).add_plugins(WireframePlugin); let mesh = generate_voxel_mesh( [1.0, 1.0, 1.0], [1, 4], [ (Top, [0, 0]), (Bottom, [0, 0]), (Right, [0, 0]), (Left, [0, 0]), (Back, [0, 0]), (Forward, [0, 0]), ], [0.0, 0.0, 0.0], 0.05, Some(0.8), 1.0, ); let mesh2 = generate_voxel_mesh( [1.0, 1.0, 1.0], [1, 4], [ (Top, [0, 1]), (Bottom, [0, 1]), (Right, [0, 1]), (Left, [0, 1]), (Back, [0, 1]), (Forward, [0, 1]), ], [0.0, 0.0, 0.0], 0.05, Some(0.8), 1.0, ); app.insert_resource(BlockRegistry { grass: mesh, dirt: mesh2, }) .insert_resource(AmbientLight { brightness: 400.0, color: Color::WHITE, }); app.add_systems(Startup, setup).add_systems( Update, ( input_handler, toggle_wireframe, input_handler_rotation, mesh_update, ), ); app.add_event::() .add_event::(); app.run(); } #[derive(Component)] struct Meshy { meta: MeshMD, grid: [u16; CHUNK_LEN], } #[derive(Component)] struct MeshInfo; #[derive(Event, Default)] struct ToggleWireframe; #[derive(Event, Default)] struct RegenerateMesh; /// Setting up everything to showcase the mesh. fn setup( breg: Res, mut commands: Commands, mut materials: ResMut>, // wireframe_config: ResMut, mut meshes: ResMut>, asset_server: Res, ) { let mut grid: Vec = vec![1; CHUNK_LEN]; grid = grid .iter_mut() .enumerate() .map(|(i, x)| { if i >= FACTOR * FACTOR * FACTOR - FACTOR * FACTOR { 2 } else { *x } }) .collect(); let g: [u16; CHUNK_LEN] = grid.try_into().unwrap(); let dims: Dimensions = (FACTOR, FACTOR, FACTOR); let texture_mesh = asset_server.load("array_texture.png"); let (culled_mesh, metadata) = mesh_grid( dims, &[], &g, breg.into_inner(), MeshingAlgorithm::Culling, None, ) .unwrap(); let culled_mesh_handle: Handle = meshes.add(culled_mesh.clone()); commands.spawn(( PbrBundle { mesh: culled_mesh_handle, material: materials.add(StandardMaterial { // base_color: Color::LIME_GREEN, // alpha_mode: AlphaMode::Mask(0.5), base_color_texture: Some(texture_mesh), ..default() }), ..default() }, Meshy { meta: metadata, grid: g, }, )); // Transform for the camera and lighting, looking at (0,0,0) (the position of the mesh). let camera_and_light_transform = Transform::from_xyz( FACTOR as f32 * 1.7, FACTOR as f32 * 1.7, FACTOR as f32 * 1.7, ) .looking_at( Vec3::new( FACTOR as f32 * 0.5, FACTOR as f32 * 0.5, FACTOR as f32 * 0.5, ), Vec3::Y, ); // Camera in 3D space. commands.spawn(Camera3dBundle { transform: camera_and_light_transform, ..default() }); // Light up the scene. commands.spawn(PointLightBundle { point_light: PointLight { intensity: 7000.0, range: 1000.0, ..default() }, transform: camera_and_light_transform, ..default() }); // for (att, _val) in culled_mesh.attributes() { // // dbg!(att); // if att == Mesh::ATTRIBUTE_POSITION.id {} // } commands.spawn( TextBundle::from_section( format!( "X/Y/Z: Rotate\nR: Reset orientation\nMove Camera: W/A/S/D/Left-Shift/Space\nToggle Wireframe: T\n"), TextStyle { font_size: 26.0, color: Color::LIME_GREEN, ..default() }, ) .with_style(Style { position_type: PositionType::Absolute, top: Val::Px(12.0), left: Val::Px(12.0), ..default() }), ); commands.spawn(( MeshInfo, TextBundle::from_section( format!("Press -C- To Break / Add a random voxel\n",), TextStyle { font_size: 26.0, color: Color::LIME_GREEN, ..default() }, ) .with_style(Style { position_type: PositionType::Absolute, bottom: Val::Px(12.0), left: Val::Px(12.0), ..default() }), )); } #[derive(Resource)] struct BlockRegistry { grass: Mesh, dirt: Mesh, } /// The important part! Without implementing a [`VoxelRegistry`], you can't use the function. impl VoxelRegistry for BlockRegistry { /// The type of our Voxel, the example uses u16 for Simplicity but you may have a struct /// Block { Name: ..., etc ...}, and you'll define that as the type, but encoding the block /// data onto simple type like u16 or u64 is probably prefferable. type Voxel = u16; /// The get_mesh function, probably the most important function in the /// [`VoxelRegistry`], it is what allows us to quickly access the Mesh of each Voxel. fn get_mesh(&self, voxel: &Self::Voxel) -> VoxelMesh<&Mesh> { if *voxel == 0 { return VoxelMesh::Null; } if *voxel == 1 { return VoxelMesh::NormalCube(&self.dirt); } if *voxel == 2 { return VoxelMesh::NormalCube(&self.grass); } VoxelMesh::Null } /// Important function that tells our Algorithm if the Voxel is "full", for example, the Air /// in minecraft is not "full", but it is still on the chunk data, to singal there is nothing. fn is_covering(&self, voxel: &u16, _side: Face) -> bool { return *voxel != 0; } /// The center of the Mesh, out mesh is defined in src/voxel_mesh.rs, just a constant. fn get_center(&self) -> [f32; 3] { return [0.0, 0.0, 0.0]; } /// The dimensions of the Mesh, out mesh is defined in src/voxel_mesh.rs, just a constant. fn get_voxel_dimensions(&self) -> [f32; 3] { return [1.0, 1.0, 1.0]; } /// The attributes we want to take from out voxels, note that using a lot of different /// attributes will likely lead to performance problems and unpredictible behaviour. /// We chose these 3 because they are very common, the algorithm does preserve UV data. fn all_attributes(&self) -> Vec { return vec![ Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_UV_0, Mesh::ATTRIBUTE_NORMAL, ]; } } /// Simple system to handle inputs for the showcase. fn input_handler( keyboard_input: Res>, mut query: Query<&mut Transform, With>, time: Res