Compare commits
33 commits
a26a8c79f7
...
6d12033e23
Author | SHA1 | Date | |
---|---|---|---|
yuni | 6d12033e23 | ||
yuni | f95a69c095 | ||
yuni | 7475b104ba | ||
yuni | 24edac27e5 | ||
yuni | 5373edb02f | ||
yuni | c880a8fb97 | ||
yuni | 8afd150223 | ||
yuni | f56521e49f | ||
yuni | babbef279a | ||
yuni | b186b37ffb | ||
yuni | fc01b68086 | ||
yuni | 8248d43463 | ||
yuni | 3079b17a1b | ||
yuni | fd16d6931e | ||
yuni | 4d4ccb9d9f | ||
yuni | 9d9482dd4a | ||
yuni | 4ac1d020e2 | ||
yuni | 2402fe7b03 | ||
yuni | 23a85807a5 | ||
yuni | efd85e1433 | ||
yuni | 830d371e36 | ||
yuni | cf34ab5a63 | ||
yuni | c7a050e2aa | ||
yuni | 6002688bb4 | ||
yuni | a66660cb44 | ||
yuni | 390e7917ad | ||
yuni | 0b162de00f | ||
yuni | f815e3d62e | ||
yuni | 02dab4b4b7 | ||
yuni | b8a122904a | ||
yuni | 22bfc62acc | ||
yuni | 35d6937793 | ||
yuni | 45fbd4e2b5 |
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
BIN
assets/models/asteroid_lum.glb
Normal file
BIN
assets/models/asteroid_lum.glb
Normal file
Binary file not shown.
BIN
assets/models/suit_with_collider.glb
Normal file
BIN
assets/models/suit_with_collider.glb
Normal file
Binary file not shown.
BIN
assets/models/whale.glb
Normal file
BIN
assets/models/whale.glb
Normal file
Binary file not shown.
77
assets/shaders/material_asteroid.wgsl
Normal file
77
assets/shaders/material_asteroid.wgsl
Normal 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);
|
||||
}
|
11
assets/shaders/skybox.wgsl
Normal file
11
assets/shaders/skybox.wgsl
Normal 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);
|
||||
}
|
|
@ -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 ..
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
20
src/data/keybindings.in
Normal 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)
|
56
src/defs.txt
56
src/defs.txt
|
@ -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
|
||||
|
|
42
src/hud.rs
42
src/hud.rs
|
@ -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() {
|
||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -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
77
src/shading.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
118
src/world.rs
118
src/world.rs
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue