use std::hash::Hasher; use bevy::{ core_pipeline::{bloom::Bloom, fxaa::Fxaa, tonemapping::Tonemapping}, prelude::*, }; use bevy_ecs::entity::EntityHasher; use bevy_math::DVec3; use big_space::prelude::*; use noise::{NoiseFn, Perlin}; fn main() { App::new() .add_plugins(( DefaultPlugins, BigSpacePlugin::::default(), GridHashPlugin::::default(), GridPartitionPlugin::::default(), big_space::camera::CameraControllerPlugin::::default(), )) .add_systems(Startup, (spawn, setup_ui)) .add_systems( PostUpdate, ( move_player.after(TransformSystem::TransformPropagate), draw_partitions.after(GridHashMapSystem::UpdatePartition), ), ) .add_systems(Update, cursor_grab) .init_resource::() .run(); } // Try bumping this up to really stress test. I'm able to push a million entities with an M3 Max. const N_ENTITIES: usize = 100_000; const HALF_WIDTH: f32 = 40.0; const CELL_WIDTH: f32 = 10.0; // How fast the entities should move, causing them to move into neighboring cells. const MOVEMENT_SPEED: f32 = 5.0; const PERCENT_STATIC: f32 = 0.9; #[derive(Component)] struct Player; #[derive(Component)] struct NonPlayer; #[derive(Resource)] struct MaterialPresets { default: Handle, highlight: Handle, flood: Handle, } impl FromWorld for MaterialPresets { fn from_world(world: &mut World) -> Self { let mut materials = world.resource_mut::>(); let d: StandardMaterial = StandardMaterial { base_color: Color::from(Srgba::new(0.5, 0.5, 0.5, 1.0)), perceptual_roughness: 0.2, metallic: 0.0, ..Default::default() }; let h: StandardMaterial = Color::from(Srgba::new(2.0, 0.0, 8.0, 1.0)).into(); let f: StandardMaterial = Color::from(Srgba::new(1.1, 0.1, 1.0, 1.0)).into(); Self { default: materials.add(d), highlight: materials.add(h), flood: materials.add(f), } } } fn draw_partitions( mut gizmos: Gizmos, partitions: Res>, grids: Query<(&GlobalTransform, &Grid)>, camera: Query<&GridHash, With>, ) { for (id, p) in partitions.iter() { let Ok((transform, grid)) = grids.get(p.grid()) else { return; }; let l = grid.cell_edge_length(); let mut hasher = EntityHasher::default(); hasher.write_u64(id.id()); let f = hasher.finish(); let hue = (f % 360) as f32; p.iter() .filter(|hash| *hash != camera.single()) .for_each(|h| { let center = [h.cell().x, h.cell().y, h.cell().z]; let local_trans = Transform::from_translation(IVec3::from(center).as_vec3() * l) .with_scale(Vec3::splat(l)); gizmos.cuboid( transform.mul_transform(local_trans), Hsla::new(hue, 1.0, 0.5, 0.2), ); }); let Some(min) = p .iter() .filter(|hash| *hash != camera.single()) .map(|h| [h.cell().x, h.cell().y, h.cell().z]) .reduce(|[ax, ay, az], [ix, iy, iz]| [ax.min(ix), ay.min(iy), az.min(iz)]) .map(|v| IVec3::from(v).as_vec3() * l) else { continue; }; let Some(max) = p .iter() .filter(|hash| *hash != camera.single()) .map(|h| [h.cell().x, h.cell().y, h.cell().z]) .reduce(|[ax, ay, az], [ix, iy, iz]| [ax.max(ix), ay.max(iy), az.max(iz)]) .map(|v| IVec3::from(v).as_vec3() * l) else { continue; }; let size = max - min; let center = min + (size) * 0.5; let local_trans = Transform::from_translation(center).with_scale(size + l * 2.0); gizmos.cuboid( transform.mul_transform(local_trans), Hsla::new(hue, 1.0, 0.5, 0.2), ); } } #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] fn move_player( time: Res