bevy_bones3 0.4.0

A voxel world storage and mesh generation plugin for Bevy.
Documentation
use bevy::prelude::*;
use bevy_bones3::prelude::*;
use bones3_core::util::anchor::ChunkAnchor;
use bones3_remesh::ecs::resources::ChunkMaterialList;
use bones3_remesh::mesh::block_model::{BlockOcclusion, BlockShape};
use bones3_remesh::vertex_data::{CubeModelBuilder, ShapeBuilder};
use bones3_remesh::{Bones3RemeshPlugin, RemeshAnchor};
use bones3_worldgen::ecs::components::{WorldGenerator, WorldGeneratorHandler};
use bones3_worldgen::{Bones3WorldGenPlugin, WorldGenAnchor};

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            Bones3CorePlugin::<BlockState>::default(),
            Bones3RemeshPlugin::<BlockState>::default(),
            Bones3WorldGenPlugin::<BlockState>::default(),
        ))
        .add_systems(Startup, init)
        .add_systems(Update, fly)
        .run();
}

#[derive(Debug, Default, Reflect, Clone, Copy)]
enum BlockState {
    #[default]
    Empty,
    Solid(u16),
}

impl BlockShape for BlockState {
    fn write_shape(&self, shape_builder: &mut ShapeBuilder) {
        match self {
            BlockState::Empty => {},
            BlockState::Solid(material) => {
                shape_builder.add_shape(
                    CubeModelBuilder::new().set_occlusion(shape_builder.get_occlusion()),
                    *material,
                );
            },
        }
    }

    fn check_occlude(&self, _: BlockOcclusion, _: Self) -> bool {
        match self {
            BlockState::Empty => false,
            BlockState::Solid(_) => true,
        }
    }
}

struct GrassyHillsWorld {
    material_index: u16,
}

impl WorldGenerator<BlockState> for GrassyHillsWorld {
    fn generate_chunk(&self, chunk_coords: IVec3) -> VoxelStorage<BlockState> {
        let mut block_storage = VoxelStorage::default();

        for block_pos in Region::CHUNK.shift(chunk_coords * 16).iter() {
            let pos = block_pos.as_vec3();
            let block_state = if pos.y <= f32::sin(pos.x / 64.0) * f32::sin(pos.z / 64.0) * 16.0 {
                BlockState::Solid(self.material_index)
            } else {
                BlockState::Empty
            };

            block_storage.set_block(block_pos, block_state);
        }

        block_storage
    }
}

fn init(
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut chunk_materials: ResMut<ChunkMaterialList>,
    mut commands: VoxelCommands,
) {
    let stone_handle = materials.add(Color::WHITE.into());
    let stone_index = chunk_materials.add_material(stone_handle, None);

    let world_id = commands
        .spawn_world((
            SpatialBundle::default(),
            WorldGeneratorHandler::from(GrassyHillsWorld {
                material_index: stone_index,
            }),
        ))
        .id();

    let commands = commands.commands();

    commands.spawn(DirectionalLightBundle {
        transform: Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, -0.5, 0.25, 0.0)),
        directional_light: DirectionalLight {
            illuminance: 50000.0,
            ..default()
        },
        ..default()
    });

    commands.insert_resource(AmbientLight {
        color:      Color::WHITE,
        brightness: 2.5,
    });

    commands.spawn((
        Camera3dBundle {
            transform: Transform::from_xyz(0.0, 32.0, 0.0).with_rotation(Quat::from_euler(
                EulerRot::XYZ,
                -0.5,
                0.0,
                0.0,
            )),
            ..default()
        },
        ChunkAnchor::<WorldGenAnchor>::new(world_id, UVec3::new(10, 10, 10)),
        ChunkAnchor::<RemeshAnchor>::new(world_id, UVec3::new(10, 10, 10)),
    ));
}

fn fly(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera3d>>) {
    let dist = time.delta_seconds() * 5.0;

    for mut transform in camera_query.iter_mut() {
        transform.translation += Vec3::NEG_Z * dist;
    }
}