use crate::{actor, audio, hud, nature, shading, var}; use bevy::prelude::*; use bevy::math::{DVec3, I64Vec3}; use bevy::scene::{InstanceId, SceneInstance}; use bevy_xpbd_3d::prelude::*; use bevy_xpbd_3d::plugins::sync; use std::collections::HashMap; use std::f32::consts::PI; use fastrand; const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds const ASTEROID_SIZE_FACTOR: f32 = 10.0; const RING_THICKNESS: f64 = 8.0e6; const STARS_MAX_MAGNITUDE: f32 = 5.5; // max 7.0, see generate_starchart.py const CENTER_WORLD_ON_PLAYER: bool = true; const ASTEROID_SPAWN_STEP: f64 = 500.0; const ASTEROID_VIEW_RADIUS: f64 = 3000.0; 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_with_collider.glb#Scene0", "suit_ar_chefhat" => "models/suit_ar_chefhat.glb#Scene0", "asteroid1" => ASSET_ASTEROID1, "asteroid2" => ASSET_ASTEROID2, "asteroid_lum" => "models/asteroid_lum.glb#Scene0", "moonlet" => "models/moonlet.glb#Scene0", "monolith" => "models/monolith_neon.glb#Scene0", "lightorb" => "models/lightorb.glb#Scene0", "orb_busstop" => "models/orb_busstop.glb#Scene0", "orb_busstop_dim" => "models/orb_busstop_dim.glb#Scene0", "MeteorAceGT" => "models/MeteorAceGT.glb#Scene0", "satellite" => "models/satellite.glb#Scene0", "pizzeria" => "models/pizzeria2.glb#Scene0", "pizzasign" => "models/pizzasign.glb#Scene0", "selectagon" => "models/selectagon.glb#Scene0", "clippy" => "models/clippy.glb#Scene0", "clippy_ar" => "models/clippy_ar.glb#Scene0", "whale" => "models/whale.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(Update, handle_cheats); app.add_systems(PostUpdate, handle_despawn); app.add_systems(Update, spawn_despawn_asteroids); app.add_plugins(PhysicsPlugins::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.insert_resource(ActiveAsteroids(HashMap::new())); app.add_event::(); if CENTER_WORLD_ON_PLAYER { // Disable bevy_xpbd's position->transform sync function app.insert_resource(sync::SyncConfig { position_to_transform: true, transform_to_position: false, }); // Add own position->transform sync function app.add_systems(PostUpdate, position_to_transform .after(sync::position_to_transform) .in_set(sync::SyncSet::PositionToTransform)); } } } #[derive(Resource)] struct AsteroidUpdateTimer(Timer); #[derive(Resource)] struct AsteroidDatabase(Vec); #[derive(Resource)] struct ActiveAsteroids(HashMap); #[derive(Resource)] struct AsteroidModel1(Handle); #[derive(Resource)] struct AsteroidModel2(Handle); #[derive(Component)] struct Asteroid; #[derive(Component)] pub struct DespawnOnPlayerDeath; struct AsteroidData { entity: Entity, //viewdistance: f64, } #[derive(Event)] pub struct DespawnEvent { entity: Entity, sceneinstance: InstanceId, origin: I64Vec3, } #[derive(Component)] pub struct Star; pub fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, mut materials_custom: ResMut>, asset_server: Res, ) { // Load assets commands.insert_resource(AsteroidModel1(asset_server.load(ASSET_ASTEROID1))); commands.insert_resource(AsteroidModel2(asset_server.load(ASSET_ASTEROID2))); // Generate starmap let sphere_handle = meshes.add(Circle::new(1.0)); let mut starcount = 0; for star in nature::STARS { let mag = star.3; if mag > STARS_MAX_MAGNITUDE { continue; } let is_sun = mag < -20.0; let mag = mag.min(6.0); let scale_color = {|color: f32| if is_sun { color * 13.0f32 } else { color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3) } }; let scale_size = {|mag: f32| if is_sun { 40000.0f32 } 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 mesh_distance = 1e9; let starchart_distance = if is_sun { Some(nature::DIST_JUPTER_SUN) } else if star.5 >= 100000.0 { None } else { Some(nature::PARSEC2METER * star.5 as f64) }; let name = if star.6.is_empty() { "Uncharted Star".to_string() } else { star.6.to_string() }; let translation = Vec3::new( mesh_distance * star.0, mesh_distance * star.1, mesh_distance * star.2, ); let rotation = Quat::from_rotation_arc(Vec3::Z, (-translation).normalize()); commands.spawn(( Star, hud::IsClickable { name: Some(name), distance: starchart_distance, }, PbrBundle { mesh: sphere_handle.clone(), material: star_color_handle, transform: Transform { translation, rotation, 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(shading::JupitersRing { alpha_mode: AlphaMode::Blend, ring_radius: ring_radius, jupiter_radius: jupiter_radius, }), ..default() }, Position::from_xyz(0.0, 0.0, 0.0), Rotation::from(Quat::IDENTITY), //Rotation::from(Quat::from_rotation_x(-0.3f32.to_radians())), )); } fn spawn_despawn_asteroids( time: Res