use crate::{actor, nature, settings}; use bevy::prelude::*; use bevy::pbr::CascadeShadowConfigBuilder; 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; const ASTEROID_SIZE: f32 = 100.0; const STARS_MAX_MAGNITUDE: f32 = 5.5; 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(Update, handle_cheats); app.add_plugins(PhysicsPlugins::default()); app.add_plugins(MaterialPlugin::::default()); //app.add_plugins(PhysicsDebugPlugin::default()); app.insert_resource(Gravity(DVec3::splat(0.0))); // Disable bevy_xpbd's position->transform sync because we have a // custom syncing function. app.insert_resource(SyncConfig { position_to_transform: false, transform_to_position: false, }); app.add_systems(PreUpdate, position_to_transform); } } #[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>, asset_server: Res, ) { // Generate a bunch of asteriods let maxdist = 4; for i in -maxdist..maxdist { for j in -maxdist..maxdist { for k in -maxdist..maxdist { let offset = 500.0; let dist = 8e3; let wobble = dist/2.0; let (i, j, k) = (i as f32, j as f32, k as f32); let asset = match ((i+j+k) as i32) % 2 { 0 => ASSET_ASTEROID1, _ => ASSET_ASTEROID2, }; commands.spawn(( actor::Actor::default(), RigidBody::Dynamic, AngularVelocity(DVec3::new(0.1, 0.1, 0.03)), LinearVelocity(DVec3::new(0.0, 0.0, 0.35)), Collider::sphere(1.0), SceneBundle { transform: Transform { translation: Vec3::new( offset + dist * i + wobble * (j+k/PI).sin() * (k+j/PI).cos(), offset + dist * j + wobble * (k+i/PI).sin() * (i+k/PI).cos(), offset + dist * k + wobble * (i+j/PI).sin() * (j+i/PI).cos(), ), rotation: Quat::from_rotation_y(-PI / 3.), scale: Vec3::splat(ASTEROID_SIZE), }, scene: asset_server.load(asset), ..default() }, )); } } } // 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) } }; 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 = 1e7; 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 = 640000.0; let jupiter_radius = 200000.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, 0.0, 0.0), Rotation::from(Quat::from_rotation_z(1f32.to_radians())), )); // Add Light from the Sun commands.spawn(DirectionalLightBundle { directional_light: DirectionalLight { illuminance: 1000.0, shadows_enabled: false, ..default() }, transform: Transform::from_rotation(Quat::from_rotation_y(PI/2.0)), cascade_shadow_config: CascadeShadowConfigBuilder { first_cascade_far_bound: 7.0, maximum_distance: 25.0, ..default() } .into(), ..default() }); } fn handle_cheats( key_input: Res>, mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With>, mut q_life: Query<(&mut actor::LifeForm, ), With>, settings: ResMut, ) { if !settings.dev_mode || q_player.is_empty() || q_life.is_empty() { return; } let (trans, mut pos, mut v) = q_player.get_single_mut().unwrap(); let (mut lifeform, ) = q_life.get_single_mut().unwrap(); if key_input.just_pressed(settings.key_cheat_pizza) { pos.0 = DVec3::new(-3370.0, 0.0, 0.0); } if key_input.just_pressed(settings.key_cheat_farview1) { pos.0 = DVec3::new(-800000.0, 800000.0, 0.0); } if key_input.just_pressed(settings.key_cheat_farview2) { pos.0 = DVec3::new(800000.0, 400000.0, 0.0); } if key_input.just_pressed(settings.key_cheat_stop) { v.0 = DVec3::ZERO; } if key_input.pressed(settings.key_cheat_speed) { v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, 1000.0)); } if key_input.pressed(settings.key_cheat_adrenaline_zero) { lifeform.adrenaline = 0.0; } if key_input.pressed(settings.key_cheat_adrenaline_mid) { lifeform.adrenaline = 0.5; } if key_input.pressed(settings.key_cheat_adrenaline_max) { lifeform.adrenaline = 1.0; } } // A variant of bevy_xpbd_3d::plugins::position_to_transform that adjusts // the rendering position to center entities at the player camera. // This avoids rendering glitches when very far away from the origin. pub fn position_to_transform( q_player: Query<&Position, With>, mut q_trans: Query<(&mut Transform, &Position, &Rotation)>, ) { if let Ok(player_pos) = q_player.get_single() { for (mut transform, pos, rot) in &mut q_trans { transform.translation = Vec3::new( (pos.x - player_pos.x) as f32, (pos.y - player_pos.y) as f32, (pos.z - player_pos.z) as f32, ); transform.rotation = rot.as_quat(); } } }