use crate::{actor, nature, settings, hud}; use bevy::prelude::*; use bevy::render::render_resource::{AsBindGroup, ShaderRef}; use bevy::math::DVec3; use bevy_xpbd_3d::prelude::*; use bevy_xpbd_3d::plugins::sync::SyncConfig; use std::f32::consts::PI; use std::f64::consts::PI as PI64; const ASTEROID_UPDATE_INTERVAL: f32 = 1.0; // seconds const ASTEROID_SIZE: f32 = 5000.0; const STARS_MAX_MAGNITUDE: f32 = 5.5; const CENTER_WORLD_ON_PLAYER: bool = true; const ASTEROIDS_ARE_SPHERES: bool = false; const ASSET_ASTEROID1: &str = "models/asteroid.glb#Scene0"; const ASSET_ASTEROID2: &str = "models/asteroid2.glb#Scene0"; pub fn asset_name_to_path(name: &str) -> &'static str { match name { "suit" => "models/suit.glb#Scene0", "asteroid1" => ASSET_ASTEROID1, "asteroid2" => ASSET_ASTEROID2, "moonlet" => "models/moonlet.glb#Scene0", "monolith" => "models/monolith_neon.glb#Scene0", "lightorb" => "models/lightorb.glb#Scene0", "MeteorAceGT" => "models/MeteorAceGT.glb#Scene0", "pizzeria" => "models/pizzeria2.glb#Scene0", "pizzasign" => "models/pizzasign.glb#Scene0", _ => "models/error.glb#Scene0", } } pub struct WorldPlugin; impl Plugin for WorldPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup); app.add_systems(Startup, generate_asteroids); app.add_systems(Update, handle_cheats); app.add_systems(PostUpdate, handle_despawn); app.add_systems(Update, spawn_despawn_asteroids); app.add_plugins(PhysicsPlugins::default()); app.add_plugins(MaterialPlugin::::default()); //app.add_plugins(PhysicsDebugPlugin::default()); app.insert_resource(Gravity(DVec3::splat(0.0))); app.insert_resource(AsteroidUpdateTimer( Timer::from_seconds(ASTEROID_UPDATE_INTERVAL, TimerMode::Repeating))); app.insert_resource(AsteroidDatabase(Vec::new())); app.add_event::(); if CENTER_WORLD_ON_PLAYER { // Disable bevy_xpbd's position->transform sync function app.insert_resource(SyncConfig { position_to_transform: false, transform_to_position: false, }); // Add own position->transform sync function app.add_systems(PreUpdate, position_to_transform); } } } #[derive(Resource)] struct AsteroidUpdateTimer(Timer); #[derive(Resource)] struct AsteroidDatabase(Vec); #[derive(Component)] struct Asteroid; struct AsteroidData { entity: Option, is_spawned: bool, class: u8, size: f32, pos: DVec3, } #[derive(Event)] pub struct DespawnEvent(Entity); #[derive(Asset, TypePath, AsBindGroup, Debug, Clone)] pub struct RingMaterial { alpha_mode: AlphaMode, #[uniform(0)] ring_radius: f32, #[uniform(1)] jupiter_radius: f32, } impl Material for RingMaterial { fn fragment_shader() -> ShaderRef { "shaders/jupiters_rings.wgsl".into() } fn alpha_mode(&self) -> AlphaMode { self.alpha_mode } } #[derive(Component)] pub struct Star; pub fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, mut materials_custom: ResMut>, ) { // Generate starmap let sphere_handle = meshes.add(Sphere::new(1.0)); let mut starcount = 0; for star in nature::STARS { let mag = star[3]; if mag > STARS_MAX_MAGNITUDE { continue; } let scale_color = {|color: f32| if mag < -20.0 { color * 13.0f32 // Sun } else { color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3) } }; let scale_size = {|mag: f32| if mag < -20.0 { 40000.0f32 // Sun } else { 1000.0 * (0.230299 * mag * mag - 3.09013 * mag + 15.1782) } * 100.0 }; let (r, g, b) = nature::star_color_index_to_rgb(star[4]); let star_color_handle = materials.add(StandardMaterial { base_color: Color::rgb(scale_color(r), scale_color(g), scale_color(b)), unlit: true, ..default() }); let dist = 1e9; commands.spawn(( Star, PbrBundle { mesh: sphere_handle.clone(), material: star_color_handle.clone(), transform: Transform::from_xyz( dist * star[0], dist * star[1], dist * star[2], ) .with_scale(Vec3::splat(scale_size(mag))), ..default() } )); starcount += 1; } info!("Generated {starcount} stars"); // Add shaded ring let ring_radius = 229_000_000.0; let jupiter_radius = 71_492_000.0; commands.spawn(( MaterialMeshBundle { mesh: meshes.add(Mesh::from(Cylinder::new(ring_radius, 1.0))), material: materials_custom.add(RingMaterial { alpha_mode: AlphaMode::Blend, ring_radius: ring_radius, jupiter_radius: jupiter_radius, }), ..default() }, Position::from_xyz(0.0, -10000.0, 0.0), //Rotation::from(Quat::IDENTITY), Rotation::from(Quat::from_rotation_x(-0.3f32.to_radians())), )); } fn generate_asteroids( mut db: ResMut, ) { let phase_steps: i16 = 600; let radius_steps: i16 = 1000; let max_height_step: i16 = 6; // this one is inclusive for symmetry let min_height_step: i16 = -max_height_step; let min_radius: f64 = 92e6; let max_radius: f64 = 229e6; let ring_thickness: f64 = 2.0e6; let height_step_factor: f64 = ring_thickness / 2.0 / (max_height_step as f64); let max_phase_step_factor: f64 = 2.0 * PI64 / (phase_steps as f64); let max_radius_factor: f64 = (max_radius - min_radius) / (radius_steps as f64); let wobble: f64 = (ASTEROID_SIZE * 50.0).into(); let mut count = 0; for phase_step in 0..phase_steps { let phase = max_phase_step_factor * phase_step as f64; for radius_step in 0..radius_steps { let radius = max_radius_factor * radius_step as f64 + min_radius; for height_step in min_height_step..=max_height_step { let height = height_step_factor * height_step as f64; let rand1 = (phase+radius).sin() + (height+radius).cos(); let rand2 = (phase+height).sin() + (phase+radius).cos(); let rand3 = (radius+height).sin() + (phase+height).cos(); let x = phase.sin() * radius + wobble * rand1; let y = height + wobble * rand2; let z = phase.cos() * radius + wobble * rand3; db.0.push(AsteroidData { entity: None, is_spawned: false, pos: DVec3::new(x, y, z), size: ASTEROID_SIZE, class: ((phase_step+radius_step+height_step) % 2) as u8, }); count += 1; } } } info!("Generated {count} asteroids"); } fn spawn_despawn_asteroids( time: Res