Compare commits

..

33 commits

Author SHA1 Message Date
yuni 6d12033e23 add starry sky box with shader 2024-04-17 04:02:40 +02:00
yuni f95a69c095 fix jupiter's rotational axis 2024-04-16 23:31:08 +02:00
yuni 7475b104ba cleanup: simplify position_to_transform 2024-04-16 19:51:04 +02:00
yuni 24edac27e5 add "The Whale", hollowed-out asteroid to scoop up debris 2024-04-16 19:44:46 +02:00
yuni 5373edb02f cleanup 2024-04-16 19:44:05 +02:00
yuni c880a8fb97 add basic grainy texture to material_asteroid 2024-04-16 18:28:15 +02:00
yuni 8afd150223 fix collider hiding 2024-04-16 17:12:39 +02:00
yuni f56521e49f add dummy material_asteroid.wgsl and shading::AsteroidSurface 2024-04-16 16:41:15 +02:00
yuni babbef279a implement better scene collider removal 2024-04-16 16:40:20 +02:00
yuni b186b37ffb add debug function that applies JupitersRing shader to all scenes 2024-04-16 16:28:19 +02:00
yuni fc01b68086 add shading.rs 2024-04-16 16:27:17 +02:00
yuni 8248d43463 add textured asteroid "Lum" with cave system and light orb 2024-04-16 16:04:53 +02:00
yuni 3079b17a1b better error handling with target acquisition 2024-04-16 15:56:12 +02:00
yuni fd16d6931e add "targeted yes" command 2024-04-16 15:55:37 +02:00
yuni 4d4ccb9d9f give moonlet mesh collider 2024-04-16 05:53:04 +02:00
yuni 9d9482dd4a give pizzaria mesh collider 2024-04-16 05:50:58 +02:00
yuni 4ac1d020e2 update meshes in defs.txt 2024-04-16 04:11:52 +02:00
yuni 2402fe7b03 add "mesh handcrafted" command 2024-04-16 04:10:43 +02:00
yuni 23a85807a5 get colliders from scene mesh with name "Collider", hide it in render 2024-04-16 04:04:22 +02:00
yuni efd85e1433 fix mesh colliders breaking due to CENTER_WORLD_ON_PLAYER 2024-04-16 02:44:01 +02:00
yuni 830d371e36 set density/inertia for computed mesh 2024-04-16 02:31:25 +02:00
yuni cf34ab5a63 Revert "ok this is hilarious, you can fly the pizzeria now"
This reverts commit c7a050e2aa.
2024-04-16 01:55:53 +02:00
yuni c7a050e2aa ok this is hilarious, you can fly the pizzeria now 2024-04-16 01:55:23 +02:00
yuni 6002688bb4 determine closeness based on distance to surface, not center 2024-04-16 01:54:34 +02:00
yuni a66660cb44 fix path 2024-04-16 01:40:57 +02:00
yuni 390e7917ad add wayland feature to linux build, remove x11 from windows build 2024-04-16 01:22:06 +02:00
yuni 0b162de00f explicitly declare target for linux build 2024-04-16 01:21:19 +02:00
yuni f815e3d62e late console lines fade out completely when they expire 2024-04-15 23:45:46 +02:00
yuni 02dab4b4b7 tweak hud colors 2024-04-15 23:40:36 +02:00
yuni b8a122904a cleanup 2024-04-15 23:23:46 +02:00
yuni 22bfc62acc Revert "fade out text through color, not alpha, for performance reasons"
I don't actually think there's any performance reasons....

This reverts commit 35d6937793.
2024-04-15 23:23:16 +02:00
yuni 35d6937793 fade out text through color, not alpha, for performance reasons 2024-04-15 23:21:15 +02:00
yuni 45fbd4e2b5 add F1 key to show key bindings 2024-04-15 23:17:44 +02:00
18 changed files with 397 additions and 102 deletions

View file

@ -11,7 +11,7 @@ rust-version = "1.76.0"
[dependencies]
regex = "1"
bevy = { version = "0.13.2", default-features = false, features = ["jpeg", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit", "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render", "bevy_text", "bevy_ui", "multi-threaded", "png", "tonemapping_luts", "vorbis", "x11"]}
bevy = { version = "0.13.2", default-features = false, features = ["jpeg", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit", "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render", "bevy_text", "bevy_ui", "multi-threaded", "png", "tonemapping_luts", "vorbis"]}
bevy_xpbd_3d = { version = "0.4.2", default-features = false, features = ["3d", "f64", "parry-f64", "parallel", "async-collider"] }
bevy_embedded_assets = "0.10.2"
fastrand = "2.0"
@ -19,8 +19,10 @@ serde = "1.0"
serde_yaml = "0.9"
[features]
default = ["x11"]
dev = ["bevy/dynamic_linking", "bevy/file_watcher"]
wasm = ["bevy/webgl2"]
x11 = ["bevy/x11"]
wayland = ["bevy/wayland"]
[profile.dev]

View file

@ -27,6 +27,7 @@ Links:
# Key Bindings
- F1: Show key bindings
- Space: Slow down (or match velocity)
- AWSD/Shift/Ctrl: Accelerate
- R: Rotate (hold & move mouse)

Binary file not shown.

Binary file not shown.

BIN
assets/models/whale.glb Normal file

Binary file not shown.

View file

@ -0,0 +1,77 @@
#import bevy_pbr::{
mesh_view_bindings::view,
pbr_fragment::pbr_input_from_standard_material,
pbr_functions::alpha_discard,
utils::coords_to_viewport_uv,
}
#ifdef PREPASS_PIPELINE
#import bevy_pbr::{
prepass_io::{VertexOutput, FragmentOutput},
pbr_deferred_functions::deferred_output,
}
#else
#import bevy_pbr::{
forward_io::{VertexOutput, FragmentOutput},
pbr_functions::{apply_pbr_lighting, main_pass_post_lighting_processing},
}
#endif
struct MyExtendedMaterial {
quantize_steps: u32,
}
@group(2) @binding(100)
var<uniform> my_extended_material: MyExtendedMaterial;
@fragment
fn fragment(
in: VertexOutput,
@builtin(front_facing) is_front: bool,
) -> FragmentOutput {
// generate a PbrInput struct from the StandardMaterial bindings
var pbr_input = pbr_input_from_standard_material(in, is_front);
// we can optionally modify the input before lighting and alpha_discard is applied
//pbr_input.material.base_color.b = pbr_input.material.base_color.r;
// alpha discard
pbr_input.material.base_color = alpha_discard(pbr_input.material, pbr_input.material.base_color);
#ifdef PREPASS_PIPELINE
// in deferred mode we can't modify anything after that, as lighting is run in a separate fullscreen shader.
let out = deferred_output(in, pbr_input);
#else
var out: FragmentOutput;
// apply lighting
out.color = apply_pbr_lighting(pbr_input);
// we can optionally modify the lit color before post-processing is applied
//out.color = vec4<f32>(vec4<u32>(out.color * f32(my_extended_material.quantize_steps))) / f32(my_extended_material.quantize_steps);
// apply in-shader post processing (fog, alpha-premultiply, and also tonemapping, debanding if the camera is non-hdr)
// note this does not include fullscreen postprocessing effects like bloom.
out.color = main_pass_post_lighting_processing(pbr_input, out.color);
// we can optionally modify the final result here
//out.color = out.color * 2.0;
//#ifdef VERTEX_UV
// out.color = out.color * grain(in.u, in.v);
// out.color = vec4<f32>(0.0, 1.0, 0.0, 1.0);
//#else
// // Why am I not getting in.uv??
// let viewport_uv = coords_to_viewport_uv(in.position.xy, view.viewport);
// out.color = out.color * grain(viewport_uv);
//#endif
out.color = out.color * grain(in.position.xyz);
#endif
return out;
}
fn grain(uv: vec3<f32>) -> f32 {
return clamp(sin(uv[0]+uv[2])+cos(uv[1]), 0.0, 1.0);
}

View file

@ -0,0 +1,11 @@
#import bevy_pbr::{
mesh_view_bindings::globals,
forward_io::VertexOutput,
}
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let pos: vec2<f32> = 10 * floor(3500 * in.uv);
let brightness: vec3<f32> = vec3(max((fract(dot(sin(pos),pos)) - 0.995) * 90.0, 0.0));
return vec4<f32>(0.01 * brightness, 1.0);
}

View file

@ -5,8 +5,8 @@
set -e
if [ "$1" == "-b" ]; then
cargo build --release
cargo build --target=x86_64-pc-windows-gnu --release
cargo build --release --target=x86_64-unknown-linux-gnu --features "x11 wayland"
cargo build --release --target=x86_64-pc-windows-gnu --no-default-features
fi
VERSION="$(sed -nr 's/^\s*version\s*=\s*"(.*)"\s*$/\1/p' Cargo.toml)"
@ -25,7 +25,7 @@ cp ../target/x86_64-pc-windows-gnu/release/outfly.exe "$SRCPATH"
zip -v -r -9 ../"outfly_v${VERSION}_windows.zip" "$SRCPATH"
rm "$SRCPATH"/outfly.exe
cp ../target/release/outfly "$SRCPATH"
cp ../target/x86_64-unknown-linux-gnu/release/outfly "$SRCPATH"
zip -v -r -9 ../"outfly_v${VERSION}_linux.zip" "$SRCPATH"
cd ..

View file

@ -264,6 +264,7 @@ pub fn apply_input_to_player(
}
}
}
// TODO: handle mass
v.0 += acceleration_total;
engine.current_warmup = (engine.current_warmup + dt / engine.warmup_seconds).clamp(0.0, 1.0);
play_thruster_sound = true;
@ -405,11 +406,12 @@ pub fn find_closest_target<TargetSpecifier>(
// not on the player mesh but on the camera, which doesn't have a position.
let (angular_diameter, angle, distance) = calc_angular_diameter_known_target_vector(
trans, camera_transform, &target_vector);
let distance_to_surface = distance - trans.scale.x;
if angle <= angular_diameter.clamp(0.001, PI) {
// It's in the field of view!
//commands.entity(entity).insert(IsTargeted);
if distance < closest_distance {
closest_distance = distance;
if distance_to_surface < closest_distance {
closest_distance = distance_to_surface;
closest_entity = Some(entity);
}
}

View file

@ -691,6 +691,11 @@ pub fn handle_chat_events(
hud::LogLevel::Warning => {
log.warning(message.into());
}
hud::LogLevel::Always => {
log.add(message.into(),
chat.talker.name.clone().unwrap_or("".to_string()),
hud::LogLevel::Always);
}
}
chat.timer = now + ((message.len() as f32).max(CHAT_SPEED_MIN_LEN) * TALKER_SPEED_FACTOR * chat.talker.talking_speed / settings.chat_speed) as f64;

View file

@ -14,10 +14,13 @@ impl Plugin for CommandsPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, load_defs);
app.add_systems(Update, spawn_entities);
app.add_systems(PreUpdate, hide_colliders
.run_if(any_with_component::<NeedsSceneColliderRemoved>));
app.add_event::<SpawnEvent>();
}
}
#[derive(Component)] pub struct NeedsSceneColliderRemoved;
#[derive(Event)] pub struct SpawnEvent(ParserState);
#[derive(PartialEq, Clone)]
enum DefClass {
@ -49,10 +52,12 @@ struct ParserState {
is_suited: bool,
is_vehicle: bool,
is_clickable: bool,
is_targeted_on_startup: bool,
has_physics: bool,
wants_maxrotation: Option<f64>,
wants_maxvelocity: Option<f64>,
collider_is_mesh: bool,
collider_is_one_mesh_of_scene: bool,
thrust_forward: f32,
thrust_sideways: f32,
thrust_back: f32,
@ -92,10 +97,12 @@ impl Default for ParserState {
is_suited: false,
is_vehicle: false,
is_clickable: true,
is_targeted_on_startup: false,
has_physics: true,
wants_maxrotation: None,
wants_maxvelocity: None,
collider_is_mesh: false,
collider_is_one_mesh_of_scene: false,
thrust_forward: default_engine.thrust_forward,
thrust_sideways: default_engine.thrust_forward,
thrust_back: default_engine.thrust_back,
@ -184,6 +191,7 @@ pub fn load_defs(
["relativeto", id] => {
// NOTE: call this command before "id", otherwise actors that
// set their position relative to this actor will get the wrong offset
// TODO: fix the above
match id2pos.get(&id.to_string()) {
Some(pos) => {
state.pos += *pos;
@ -362,6 +370,9 @@ pub fn load_defs(
["collider", "mesh"] => {
state.collider_is_mesh = true;
}
["collider", "handcrafted"] => {
state.collider_is_one_mesh_of_scene = true;
}
["player", "yes"] => {
state.is_player = true;
state.is_alive = true;
@ -414,6 +425,9 @@ pub fn load_defs(
["armodel", asset_name] => {
state.ar_model = Some(asset_name.to_string());
}
["targeted", "yes"] => {
state.is_targeted_on_startup = true;
}
_ => {
error!("No match for [{}]", parts.join(","));
}
@ -482,11 +496,23 @@ fn spawn_entities(
actor.insert(AngularVelocity(state.angular_momentum));
actor.insert(ColliderDensity(state.density));
if state.collider_is_mesh {
actor.insert(MassPropertiesBundle::new_computed(
&Collider::sphere(0.5 * state.model_scale as f64), state.density));
actor.insert(AsyncSceneCollider::new(Some(
ComputedCollider::TriMesh
//ComputedCollider::ConvexDecomposition(VHACDParameters::default())
)));
}
else if state.collider_is_one_mesh_of_scene {
actor.insert(MassPropertiesBundle::new_computed(
&Collider::sphere(0.5 * state.model_scale as f64), state.density));
actor.insert(AsyncSceneCollider::new(None)
.with_shape_for_name("Collider", ComputedCollider::TriMesh)
.with_layers_for_name("Collider", CollisionLayers::ALL)
//.with_density_for_name("Collider", state.density)
);
actor.insert(NeedsSceneColliderRemoved);
}
else {
actor.insert(state.collider.clone());
}
@ -498,6 +524,9 @@ fn spawn_entities(
actor.insert(actor::Player);
actor.insert(actor::PlayerCamera);
}
if state.is_targeted_on_startup {
actor.insert(hud::IsTargeted);
}
if state.is_player || state.is_vehicle {
// used to apply mouse movement to actor rotation
actor.insert(ExternalTorque::ZERO.with_persistence(false));
@ -586,3 +615,20 @@ fn spawn_entities(
}
}
}
pub fn hide_colliders(
//mut commands: Commands,
mut q_mesh: Query<(&mut Visibility, &Name), With<Handle<Mesh>>>,
//q_flag: Query<Entity, With<NeedsSceneColliderRemoved>>,
) {
for (mut visibility, name) in &mut q_mesh {
if name.as_str() == "Collider" {
*visibility = Visibility::Hidden;
}
}
// TODO: not quite done here yet...
// for entity in &q_flag {
// commands.entity(entity).remove::<NeedsSceneColliderRemoved>();
// }
}

20
src/data/keybindings.in Normal file
View file

@ -0,0 +1,20 @@
X: Teleport to target [CHEAT]
C: Impossibly instant stopping [CHEAT]
Shift+V/B: Same as V/B, but a thousand times faster [CHEAT]
V/B: Impossible acceleration forward/backward [CHEAT]
G: Toggle god mode / cheats [CHEAT]
M: Toggle sound effects
T: Toggle music
Y: Toggle rotation stabilizer
F: Toggle 3rd person view
F11: Toggle fullscreen
Tab: Toggle HUD + Augmented Reality
Right click: Zoom [AUGMENTED REALITY ONLY]
Left click: Target objects [AUGMENTED REALITY ONLY]
JKULIO: Mouseless camera rotation
F7: Restart game
Q: Exit vehicle
E: Interact: Talk to people, enter vehicles
R: Rotate (hold & move mouse)
AWSD/Shift/Ctrl: Accelerate
Space: Slow down (or match velocity)

View file

@ -14,10 +14,11 @@ actor 0 593051 0 suit
player yes
id player
scale 2
density 200
collider handcrafted
oxygen 0.008
health 0.3
angularmomentum 0 0 0
collider capsule 1 0.5
thrust 1.2 1 1 400 1.5
rotationy 0.65
engine monopropellant
@ -27,11 +28,11 @@ actor 10 -30 20 MeteorAceGT
relativeto player
scale 5
vehicle yes
thrust 24.5 4.8 3.3 500000 3
collider mesh
thrust 24.5 4.8 3.3 100000 3
engine ion
collider sphere 1
camdistance 50
density 200
density 500
angularmomentum 0.1 0.1 0.3
actor 0 0 0 io
@ -88,9 +89,25 @@ actor 0 0 0 moonlet
actor 3000 0 0 moonlet
name Moonlet
collider mesh
relativeto player
density 10000000000
scale 500
angularmomentum 0 0.15 0
angularmomentum 0 0.015 0
actor 220 -2400 410 asteroid_lum
relativeto player
name Lum
id Lum
collider mesh
density 10000000000
scale 300
angularmomentum 0 0.015 0
actor -80 0 0 lightorb
relativeto Lum
name "Light Orb"
scale 0.3
light FF8F4A 500000
actor -200 -110 1000 satellite
name "Communications Satellite"
@ -139,6 +156,7 @@ actor -3300 10 0 pizzeria
relativeto player
id pizzeria
scale 40
collider mesh
rotationy 0.30
angularmomentum 0 0 0
actor -120 0 20 MeteorAceGT
@ -146,11 +164,11 @@ actor -3300 10 0 pizzeria
relativeto pizzeria
scale 5
vehicle yes
thrust 24.5 4.8 3.3 500000 3
collider mesh
thrust 24.5 4.8 3.3 100000 3
engine ion
collider sphere 1
camdistance 50
density 200
density 500
angularmomentum 0 0 0.2
actor -100 63 -13 pizzasign
name "Pizzeria Sign"
@ -159,12 +177,12 @@ actor -3300 10 0 pizzeria
density 200
rotationy 0.45
angularmomentum 0 0 0
actor -16 -10 0 lightorb
actor -36 -10 0 lightorb
name "Light Orb"
relativeto pizzeria
scale 0.5
light FF8F4A 1000000
actor -14 -3 -2 lightorb
actor -34 -3 -2 lightorb
name "Light Orb"
relativeto pizzeria
scale 0.5
@ -188,7 +206,7 @@ actor -3300 10 0 pizzeria
armodel suit_ar_chefhat
alive yes
scale 2
collider capsule 1 0.5
collider handcrafted
thrust 1.2 1 1 10 1.5
wants maxrotation 0
wants maxvelocity 0
@ -203,7 +221,7 @@ actor 60 -15 -40 suit
chatid Icarus
alive yes
scale 2
collider capsule 1 0.5
collider handcrafted
angularmomentum 0.4 0.2 0.1
rotationy 0.6
rotationx 1
@ -219,7 +237,7 @@ actor -300 0 40 suit
chatid Drifter
oxygen 0.08
scale 2
collider capsule 1 0.5
collider handcrafted
actor 100 -18000 2000 "orb_busstop"
relativeto player
@ -337,3 +355,15 @@ actor 27643e3 -44e3 -124434e3 "orb_busstop"
name "Light Orb"
relativeto busstopclippy3
light "47FF00" 1000000
actor 110 -2000 0 whale
relativeto busstop3
name "The Whale"
vehicle yes
collider mesh
density 100000
camdistance 4000
scale 300
angularmomentum 0 0.015 0
thrust 2.45 0.48 0.33 1000000000000000 3
engine ion

View file

@ -9,9 +9,9 @@ use std::time::SystemTime;
pub const HUD_REFRESH_TIME: f32 = 0.1;
pub const FONT: &str = "fonts/Yupiter-Regular.ttf";
pub const LOG_MAX: usize = 16;
pub const LOG_MAX_TIME_S: f64 = 30.0;
pub const LOG_MAX_ROWS: usize = 30;
pub const LOG_MAX: usize = LOG_MAX_ROWS;
pub const MAX_CHOICES: usize = 10;
pub const AMBIENT_LIGHT: f32 = 0.0; // Space is DARK
pub const AMBIENT_LIGHT_AR: f32 = 15.0;
@ -74,6 +74,7 @@ pub struct AugmentedRealityOverlay {
struct FPSUpdateTimer(Timer);
pub enum LogLevel {
Always,
Warning,
//Error,
Info,
@ -234,6 +235,9 @@ fn setup(
));
// Add Console
// This one is intentionally NOT a ToggleableHudElement. Instead, console entries
// are filtered based on whether the hud is active or not. LogLevel::Always is
// even shown when hud is inactive.
let bundle_chatbox = TextBundle::from_sections((0..LOG_MAX_ROWS).map(|_|
TextSection::new("", style_console.clone()))
).with_style(Style {
@ -243,7 +247,6 @@ fn setup(
..default()
}).with_text_justify(JustifyText::Right);
commands.spawn((
ToggleableHudElement,
NodeBundle {
style: Style {
width: Val::Percent(50.0),
@ -253,7 +256,6 @@ fn setup(
right: Val::VMin(3.0),
..default()
},
visibility,
..default()
},
)).with_children(|parent| {
@ -420,6 +422,8 @@ fn update_hud(
// Target display
let dist_scalar: f64;
let mut target_multiple = false;
let mut target_error = false;
if let Ok((IsClickable { distance: Some(dist), .. }, _, _)) = q_target.get_single() {
dist_scalar = *dist;
}
@ -431,7 +435,12 @@ fn update_hud(
else if q_target.is_empty() {
target = Some(DVec3::new(0.0, 0.0, 0.0));
}
else if q_target.iter().len() > 1 {
target_multiple = true;
target = None;
}
else {
target_error = true;
target = None;
}
if let Some(target_pos) = target {
@ -456,7 +465,13 @@ fn update_hud(
let speed_readable = nature::readable_distance(speed);
text.sections[14].value = format!("\n{speed_readable}/s\n{gforce:.1}g{dev_speed}");
if let Ok((clickable, _, target_v_maybe)) = q_target.get_single() {
if target_multiple {
text.sections[15].value = "\n\nERROR: MULTIPLE TARGETS".to_string();
}
else if target_error {
text.sections[15].value = "\n\nERROR: FAILED TO AQUIRE TARGET".to_string();
}
else if let Ok((clickable, _, target_v_maybe)) = q_target.get_single() {
let distance = if dist_scalar.is_nan() {
"UNKNOWN".to_string()
} else if dist_scalar != 0.0 {
@ -503,7 +518,16 @@ fn update_hud(
let mut row = 0;
// Chat Log and System Log
let logfilter = if settings.hud_active {
|_msg: &&Message| { true }
} else {
|msg: &&Message| { match msg.level {
LogLevel::Always => true,
_ => false
}}
};
let messages: Vec<&Message> = log.logs.iter()
.filter(logfilter)
.rev()
.take(LOG_MAX_ROWS)
.collect();
@ -511,14 +535,14 @@ fn update_hud(
for msg in &messages {
chat.sections[row].value = msg.format();
let freshness = msg.get_freshness();
let clr: f32 = (freshness.powf(1.5) as f32).clamp(0.1, 1.0);
let opacity: f32 = (freshness.powf(1.5) as f32).clamp(0.0, 1.0);
freshest_line = freshest_line.max(freshness);
chat.sections[row].style.color = match msg.level {
LogLevel::Warning => settings.hud_color_console_warn,
LogLevel::Info => settings.hud_color_console_system,
_ => settings.hud_color_console,
};
chat.sections[row].style.color.set_a(clr);
chat.sections[row].style.color.set_a(opacity);
row += 1;
}
@ -591,6 +615,7 @@ fn handle_input(
keyboard_input: Res<ButtonInput<KeyCode>>,
mouse_input: Res<ButtonInput<MouseButton>>,
mut settings: ResMut<var::Settings>,
mut log: ResMut<Log>,
mut q_hud: Query<(&mut Visibility, Option<&OnlyHideWhenTogglingHud>), With<ToggleableHudElement>>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
mut ew_togglemusic: EventWriter<audio::ToggleMusicEvent>,
@ -599,6 +624,11 @@ fn handle_input(
q_objects: Query<(Entity, &Transform), (With<IsClickable>, Without<IsTargeted>, Without<actor::PlayerDrivesThis>, Without<actor::Player>)>,
q_camera: Query<&Transform, With<Camera>>,
) {
if keyboard_input.just_pressed(settings.key_help) {
for line in include_str!("data/keybindings.in").trim().split("\n") {
log.add(line.to_string(), "".to_string(), LogLevel::Always);
}
}
if keyboard_input.just_pressed(settings.key_togglehud) {
if settings.hud_active {
for (mut hudelement_visibility, _) in q_hud.iter_mut() {

View file

@ -5,6 +5,7 @@ mod chat;
mod commands;
mod effects;
mod hud;
mod shading;
mod var;
mod world;
@ -15,6 +16,7 @@ use bevy::window::{Window, WindowMode, PrimaryWindow, CursorGrabMode};
use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
use bevy::prelude::*;
use bevy_embedded_assets::{EmbeddedAssetPlugin, PluginMode};
use bevy::pbr::ExtendedMaterial;
use std::env;
fn main() {
@ -46,6 +48,7 @@ impl Plugin for OutFlyPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup);
app.add_systems(Update, handle_input);
app.add_systems(Update, debug);
app.insert_resource(var::Settings::default());
app.insert_resource(var::GameVars::default());
app.add_plugins((
@ -59,6 +62,7 @@ impl Plugin for OutFlyPlugin {
commands::CommandsPlugin,
effects::EffectsPlugin,
hud::HudPlugin,
shading::ShadingPlugin,
world::WorldPlugin,
));
}
@ -94,3 +98,21 @@ fn handle_input(
}
}
}
fn debug(
settings: Res<var::Settings>,
keyboard_input: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
mut extended_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, shading::AsteroidSurface>>>,
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
) {
if settings.dev_mode && keyboard_input.pressed(KeyCode::KeyP) {
for (entity, _name, mesh) in &materials {
dbg!(mesh);
let mut entity = commands.entity(entity);
entity.remove::<Handle<StandardMaterial>>();
let material = extended_materials.add(shading::AsteroidSurface::material());
entity.insert(material);
}
}
}

77
src/shading.rs Normal file
View file

@ -0,0 +1,77 @@
use bevy::prelude::*;
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
use bevy::pbr::{ExtendedMaterial, MaterialExtension, OpaqueRendererMethod};
pub struct ShadingPlugin;
impl Plugin for ShadingPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(MaterialPlugin::<JupitersRing>::default());
app.add_plugins(MaterialPlugin::<SkyBox>::default());
app.add_plugins(MaterialPlugin::<ExtendedMaterial<StandardMaterial, AsteroidSurface, >>::default());
}
}
// Jupiter's Ring
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
pub struct JupitersRing {
pub alpha_mode: AlphaMode,
#[uniform(0)]
pub ring_radius: f32,
#[uniform(1)]
pub jupiter_radius: f32,
}
impl Material for JupitersRing {
fn fragment_shader() -> ShaderRef {
"shaders/jupiters_rings.wgsl".into()
}
fn alpha_mode(&self) -> AlphaMode {
self.alpha_mode
}
}
// Sky Box
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
pub struct SkyBox {}
impl Material for SkyBox {
fn fragment_shader() -> ShaderRef {
"shaders/skybox.wgsl".into()
}
}
// Asteroid Surface
#[derive(Asset, Reflect, AsBindGroup, Debug, Clone)]
pub struct AsteroidSurface {
#[uniform(100)]
quantize_steps: u32
}
impl MaterialExtension for AsteroidSurface {
fn fragment_shader() -> ShaderRef {
"shaders/material_asteroid.wgsl".into()
}
fn deferred_fragment_shader() -> ShaderRef {
"shaders/material_asteroid.wgsl".into()
}
}
impl AsteroidSurface {
pub fn material() -> ExtendedMaterial<StandardMaterial, AsteroidSurface> {
ExtendedMaterial {
base: StandardMaterial {
base_color: Color::rgb(0.29, 0.255, 0.208),
perceptual_roughness: 1.0,
opaque_render_method: OpaqueRendererMethod::Auto,
..default()
},
extension: Self::default(),
}
}
}
impl Default for AsteroidSurface {
fn default() -> Self {
Self {
quantize_steps: 3,
}
}
}

View file

@ -49,6 +49,7 @@ pub struct Settings {
pub key_exit: KeyCode,
pub key_restart: KeyCode,
pub key_fullscreen: KeyCode,
pub key_help: KeyCode,
pub key_forward: KeyCode,
pub key_back: KeyCode,
pub key_left: KeyCode,
@ -126,11 +127,11 @@ impl Default for Settings {
font_size_conversations: 32.0,
font_size_choices: 28.0,
font_size_console: 20.0,
hud_color: Color::rgb(0.2, 0.5, 0.2),
hud_color_console: Color::rgb(0.2, 0.5, 0.2),
hud_color: Color::rgb(0.1, 0.5, 0.1),
hud_color_console: Color::rgb(0.1, 0.5, 0.1),
hud_color_console_warn: Color::rgb(1.0, 0.3, 0.3),
hud_color_console_system: Color::rgb(0.5, 0.5, 0.5),
hud_color_alert: Color::rgb(0.7, 0.3, 0.3),
hud_color_alert: Color::rgb(0.6, 0.094, 0.322),
hud_color_subtitles: Color::rgb(0.8, 0.8, 0.8),
hud_color_choices: Color::rgb(0.45, 0.45, 0.45),
chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 },
@ -144,6 +145,7 @@ impl Default for Settings {
key_exit: KeyCode::Escape,
key_restart: KeyCode::F7,
key_fullscreen: KeyCode::F11,
key_help: KeyCode::F1,
key_forward: KeyCode::KeyW,
key_back: KeyCode::KeyS,
key_left: KeyCode::KeyA,

View file

@ -1,10 +1,10 @@
use crate::{actor, audio, hud, nature, var};
use crate::{actor, audio, hud, nature, shading, var};
use bevy::prelude::*;
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
use bevy::math::{DVec3, I64Vec3};
use bevy::scene::{InstanceId, SceneInstance};
use bevy::render::mesh::Indices;
use bevy_xpbd_3d::prelude::*;
use bevy_xpbd_3d::plugins::sync::SyncConfig;
use bevy_xpbd_3d::plugins::sync;
use std::collections::HashMap;
use std::f32::consts::PI;
use fastrand;
@ -23,10 +23,11 @@ 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",
"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",
@ -39,6 +40,7 @@ pub fn asset_name_to_path(name: &str) -> &'static str {
"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",
}
}
@ -51,7 +53,6 @@ impl Plugin for WorldPlugin {
app.add_systems(PostUpdate, handle_despawn);
app.add_systems(Update, spawn_despawn_asteroids);
app.add_plugins(PhysicsPlugins::default());
app.add_plugins(MaterialPlugin::<RingMaterial>::default());
//app.add_plugins(PhysicsDebugPlugin::default());
app.insert_resource(Gravity(DVec3::splat(0.0)));
app.insert_resource(AsteroidUpdateTimer(
@ -62,12 +63,14 @@ impl Plugin for WorldPlugin {
if CENTER_WORLD_ON_PLAYER {
// Disable bevy_xpbd's position->transform sync function
app.insert_resource(SyncConfig {
position_to_transform: false,
app.insert_resource(sync::SyncConfig {
position_to_transform: true,
transform_to_position: false,
});
// Add own position->transform sync function
app.add_systems(PreUpdate, position_to_transform);
app.add_systems(PostUpdate, position_to_transform
.after(sync::position_to_transform)
.in_set(sync::SyncSet::PositionToTransform));
}
}
}
@ -93,24 +96,6 @@ pub struct DespawnEvent {
origin: I64Vec3,
}
#[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;
@ -118,7 +103,8 @@ pub fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut materials_custom: ResMut<Assets<RingMaterial>>,
mut materials_jupiter: ResMut<Assets<shading::JupitersRing>>,
mut materials_skybox: ResMut<Assets<shading::SkyBox>>,
asset_server: Res<AssetServer>,
) {
// Load assets
@ -195,13 +181,34 @@ pub fn setup(
}
info!("Generated {starcount} stars");
// Add shaded skybox
//let mut mesh = Mesh::from(Sphere::new(1e9).mesh().uv(50, 50));
let mut mesh = Mesh::from(Sphere::new(1e10).mesh().uv(5, 5));
//let mut mesh = Mesh::from(Cuboid::from_size(Vec3::splat(2e10)));
if let Some(Indices::U32(indices)) = mesh.indices_mut() {
// Reverse the order of each triangle to avoid backface culling
for slice in indices.chunks_mut(3) {
slice.reverse();
}
}
commands.spawn((
MaterialMeshBundle {
mesh: meshes.add(mesh),
material: materials_skybox.add(shading::SkyBox {}),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
..default()
},
Position::from_xyz(0.0, 0.0, 0.0),
Rotation::from(Quat::IDENTITY),
));
// 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 {
material: materials_jupiter.add(shading::JupitersRing {
alpha_mode: AlphaMode::Blend,
ring_radius: ring_radius,
jupiter_radius: jupiter_radius,
@ -466,58 +473,21 @@ fn handle_cheats(
}
}
// A variant of bevy_xpbd_3d::plugins::position_to_transform that adjusts
// An extension 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<actor::PlayerCamera>>,
mut q_trans: Query<(&'static mut Transform, &'static Position, &'static Rotation, Option<&'static Parent>)>,
parents: Query<(&'static GlobalTransform, Option<&'static Position>, Option<&'static Rotation>), With<Children>>,
mut q_trans: Query<(&'static mut Transform, &'static Position, &'static Rotation), Without<Parent>>,
) {
if let Ok(player_pos) = q_player.get_single() {
for (mut transform, pos, rot, parent) in &mut q_trans {
if let Some(parent) = parent {
if let Ok((parent_transform, parent_pos, parent_rot)) = parents.get(**parent) {
// Compute the global transform of the parent using its Position and Rotation
let parent_transform = parent_transform.compute_transform();
let parent_pos = parent_pos.map_or(parent_transform.translation, |pos| {
pos.as_vec3()
// NOTE: I commented out this because it turns a vec3 to a vec4,
// and I don't understand why bevy_xpbd would do that.
//.extend(parent_transform.translation.z)
});
let parent_rot = parent_rot.map_or(parent_transform.rotation, |rot| {
rot.as_quat()
});
let parent_scale = parent_transform.scale;
let parent_transform = Transform::from_translation(parent_pos)
.with_rotation(parent_rot)
.with_scale(parent_scale);
// The new local transform of the child body,
// computed from the its global transform and its parents global transform
let new_transform = GlobalTransform::from(
Transform::from_translation(
pos.as_vec3()
// NOTE: I commented out this because it turns a vec3 to a vec4,
// and I don't understand why bevy_xpbd would do that.
//.extend(parent_pos.z + transform.translation.z * parent_scale.z),
)
.with_rotation(rot.as_quat()),
)
.reparented_to(&GlobalTransform::from(parent_transform));
transform.translation = new_transform.translation;
transform.rotation = new_transform.rotation;
}
} else {
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();
}
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();
}
}
}