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, Simplex}; use smallvec::SmallVec; use turborand::prelude::*; // Try bumping this up to really stress test. I'm able to push a million entities with an M3 Max. const HALF_WIDTH: f32 = 50.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.99; 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, spawn_spheres)) .init_resource::() .run(); } #[derive(Component)] struct Player; #[derive(Component)] struct NonPlayer; #[derive(Resource)] struct MaterialPresets { default: Handle, highlight: Handle, flood: Handle, sphere: Handle, } impl FromWorld for MaterialPresets { fn from_world(world: &mut World) -> Self { let mut materials = world.resource_mut::>(); let default = materials.add(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 highlight = materials.add(Color::from(Srgba::new(2.0, 0.0, 8.0, 1.0))); let flood = materials.add(Color::from(Srgba::new(1.1, 0.1, 1.0, 1.0))); let mut meshes = world.resource_mut::>(); let sphere = meshes.add( Sphere::new(HALF_WIDTH / (1_000_000_f32).powf(0.33) * 0.5) .mesh() .ico(0) .unwrap(), ); Self { default, highlight, flood, sphere, } } } fn draw_partitions( mut gizmos: Gizmos, partitions: Res>, grids: Query<(&GlobalTransform, &Grid)>, camera: Query<&GridHash, With>, ) { for (id, p) in partitions.iter().take(10_000) { 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()) .take(1_000) .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.05), ); }); let min = IVec3::from([p.min().x, p.min().y, p.min().z]).as_vec3() * l; let max = IVec3::from([p.max().x, p.max().y, p.max().z]).as_vec3() * l; 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