A multiplayer VR framework w/voice chat

vr locomotion and camera using vr view

phaz.uk 3fa5b5be 7ba81567

verified
Changed files
+165 -60
assets
src
felix
client
+30
Cargo.lock
··· 1600 1600 ] 1601 1601 1602 1602 [[package]] 1603 + name = "bevy_xr_utils" 1604 + version = "0.4.0" 1605 + source = "registry+https://github.com/rust-lang/crates.io-index" 1606 + checksum = "f82cd353262258aae6048ccad3544b8a978a301d9eb954d66efef6dccc91dfa9" 1607 + dependencies = [ 1608 + "bevy_app", 1609 + "bevy_color", 1610 + "bevy_derive", 1611 + "bevy_ecs", 1612 + "bevy_gizmos", 1613 + "bevy_log", 1614 + "bevy_math", 1615 + "bevy_mod_openxr", 1616 + "bevy_mod_xr", 1617 + "bevy_transform", 1618 + "openxr", 1619 + "openxr_mndx_xdev_space", 1620 + ] 1621 + 1622 + [[package]] 1603 1623 name = "bindgen" 1604 1624 version = "0.72.1" 1605 1625 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2460 2480 "bevy", 2461 2481 "bevy_mod_openxr", 2462 2482 "bevy_mod_xr", 2483 + "bevy_xr_utils", 2463 2484 "cpal 0.16.0", 2464 2485 "dotenvy", 2465 2486 "felix-assets", ··· 3903 3924 "cmake", 3904 3925 "libc", 3905 3926 "mint", 3927 + ] 3928 + 3929 + [[package]] 3930 + name = "openxr_mndx_xdev_space" 3931 + version = "0.1.1" 3932 + source = "registry+https://github.com/rust-lang/crates.io-index" 3933 + checksum = "fc03c3948dceb287224eaa9079a5d4f690e91b43a2385da6c686edf0f850408a" 3934 + dependencies = [ 3935 + "openxr", 3906 3936 ] 3907 3937 3908 3938 [[package]]
+2
assets/src/felix/mod.rs
··· 7 7 pub fn load( path: &'static str ) -> anyhow::Result<Vec<BevyObject>>{ 8 8 // TODO: Material loading 9 9 // TODO: UV Loading 10 + // TODO: Flat normals 11 + // TODO: Live updates directly from blender? 10 12 11 13 let mut objs = vec![]; 12 14
+1
client/Cargo.toml
··· 15 15 bevy_mod_openxr = "0.4.0" 16 16 openxr = "0.19.0" 17 17 bevy_mod_xr = "0.4.0" 18 + bevy_xr_utils = "0.4.0"
+77 -32
client/src/components/debug_camera.rs
··· 1 1 use std::f32::consts::PI; 2 2 3 3 use bevy::{ input::mouse::MouseMotion, prelude::* }; 4 - use bevy_mod_xr::session::XrState; 4 + use bevy_mod_openxr::{helper_traits::ToQuat, resources::OxrViews}; 5 + use bevy_mod_xr::session::{XrState, XrTrackingRoot}; 5 6 use felix_audio::voice::microphone::VoiceChatMicrophone; 6 7 7 8 use crate::net::connection::Connection; ··· 21 22 } 22 23 } 23 24 25 + pub fn vr_locomotion( // TODO: Move locomotion to controllers instead of keyboard 26 + views: ResMut<OxrViews>, 27 + mut position_query: Query<&mut Transform, With<XrTrackingRoot>>, 28 + keys: Res<ButtonInput<KeyCode>>, 29 + xr_state: Res<XrState>, 30 + time: Res<Time> 31 + ){ 32 + if *xr_state == XrState::Running{ 33 + let mut delta_time = time.delta_secs(); 34 + if keys.pressed(KeyCode::ShiftLeft){ delta_time *= 2.0; } 35 + 36 + let mut transform = position_query.single_mut().unwrap(); 37 + let view = views.first().unwrap(); 38 + 39 + let cam = view.pose.orientation.to_quat(); 40 + 41 + if keys.pressed(KeyCode::KeyW){ 42 + let mut dir = cam * -Vec3::Z * delta_time; 43 + dir.y = 0.0; 44 + 45 + transform.translation += dir; 46 + } else if keys.pressed(KeyCode::KeyS){ 47 + let mut dir = cam * Vec3::Z * delta_time; 48 + dir.y = 0.0; 49 + 50 + transform.translation += dir; 51 + } 52 + 53 + if keys.pressed(KeyCode::KeyA){ 54 + let mut dir = cam * -Vec3::X * delta_time; 55 + dir.y = 0.0; 56 + 57 + transform.translation += dir; 58 + } else if keys.pressed(KeyCode::KeyD){ 59 + let mut dir = cam * Vec3::X * delta_time; 60 + dir.y = 0.0; 61 + 62 + transform.translation += dir; 63 + } 64 + } 65 + } 66 + 24 67 pub fn update( 25 68 mut query: Query<(&mut DebugCamera, &mut Transform)>, 26 - 27 69 time: Res<Time>, 28 70 29 71 keys: Res<ButtonInput<KeyCode>>, ··· 37 79 xr_state: Res<XrState> 38 80 ){ 39 81 let net_manager = networking.single().unwrap(); 82 + let xr_state = xr_state.into_inner(); 40 83 41 - let mut delta_time = time.delta_secs(); 42 - if keys.pressed(KeyCode::ShiftLeft){ delta_time *= 2.0; } 84 + let ( mut debug, mut transform ) = query.single_mut().unwrap(); 43 85 44 - let ( mut debug, mut transform ) = query.single_mut().unwrap(); 86 + if *xr_state != XrState::Running{ 87 + let mut delta_time = time.delta_secs(); 88 + if keys.pressed(KeyCode::ShiftLeft){ delta_time *= 2.0; } 45 89 46 - for ev in mouse_motion.read(){ 47 - if mouse_buttons.pressed(MouseButton::Right){ 48 - debug.pitch -= ev.delta.y * delta_time * 0.1; 49 - debug.yaw -= ev.delta.x * delta_time * 0.1; 90 + for ev in mouse_motion.read(){ 91 + if mouse_buttons.pressed(MouseButton::Right){ 92 + debug.pitch -= ev.delta.y * delta_time * 0.1; 93 + debug.yaw -= ev.delta.x * delta_time * 0.1; 94 + } 50 95 } 51 - } 52 96 53 - transform.rotation = Quat::from_euler(EulerRot::YXZ, debug.yaw, debug.pitch, 0.0); 97 + transform.rotation = Quat::from_euler(EulerRot::YXZ, debug.yaw, debug.pitch, 0.0); 54 98 55 - if keys.pressed(KeyCode::KeyW){ 56 - let dir = transform.forward(); 57 - transform.translation += dir * delta_time; 58 - } else if keys.pressed(KeyCode::KeyS){ 59 - let dir = -transform.forward(); 60 - transform.translation += dir * delta_time; 61 - } 99 + if keys.pressed(KeyCode::KeyW){ 100 + let dir = transform.forward(); 101 + transform.translation += dir * delta_time; 102 + } else if keys.pressed(KeyCode::KeyS){ 103 + let dir = -transform.forward(); 104 + transform.translation += dir * delta_time; 105 + } 62 106 63 - if keys.pressed(KeyCode::KeyA){ 64 - let dir = transform.left(); 65 - transform.translation += dir * delta_time; 66 - } else if keys.pressed(KeyCode::KeyD){ 67 - let dir = -transform.left(); 68 - transform.translation += dir * delta_time; 69 - } 107 + if keys.pressed(KeyCode::KeyA){ 108 + let dir = transform.left(); 109 + transform.translation += dir * delta_time; 110 + } else if keys.pressed(KeyCode::KeyD){ 111 + let dir = -transform.left(); 112 + transform.translation += dir * delta_time; 113 + } 70 114 71 - if keys.pressed(KeyCode::KeyE){ 72 - let dir = transform.up(); 73 - transform.translation += dir * delta_time; 74 - } else if keys.pressed(KeyCode::KeyQ){ 75 - let dir = -transform.up(); 76 - transform.translation += dir * delta_time; 115 + if keys.pressed(KeyCode::KeyE){ 116 + let dir = transform.up(); 117 + transform.translation += dir * delta_time; 118 + } else if keys.pressed(KeyCode::KeyQ){ 119 + let dir = -transform.up(); 120 + transform.translation += dir * delta_time; 121 + } 77 122 } 78 123 79 124 let mut remote_rms = "".to_owned(); ··· 111 156 transform.rotation.z, 112 157 transform.rotation.w, 113 158 114 - xr_state.into_inner() 159 + xr_state 115 160 ); 116 161 } 117 162
+34 -27
client/src/main.rs
··· 2 2 3 3 use bevy::{app::{App, FixedUpdate, Startup, Update}, ecs::system::Commands, prelude::*, render::pipelined_rendering::PipelinedRenderingPlugin}; 4 4 use bevy_mod_openxr::{add_xr_plugins, resources::OxrSessionConfig}; 5 - use bevy_mod_xr::session::{XrSessionCreated, XrSessionPlugin}; 5 + use bevy_mod_xr::session::{XrSessionCreated, XrSessionPlugin, XrState}; 6 + use bevy_xr_utils::tracking_utils::TrackingUtilitiesPlugin; 6 7 use felix_audio::{FelixAudio, voice}; 7 - use openxr::EnvironmentBlendMode; 8 8 9 9 use crate::{components::{debug_camera, network_interface, remote_player}, setup::setup}; 10 10 ··· 14 14 15 15 fn main() { 16 16 if let Err(err) = dotenvy::dotenv(){ println!("[WARN] .ENV failed loading {:#?}", err); } 17 + let try_use_vr = env::var("DONT_USE_VR").is_err(); 17 18 18 - App::new() 19 - .add_plugins(( 19 + let mut app = App::new(); 20 + 21 + if try_use_vr{ 22 + app.add_plugins(( 20 23 add_xr_plugins( 21 24 DefaultPlugins 22 25 .build() 23 26 .disable::<PipelinedRenderingPlugin>(), 24 27 ).set(XrSessionPlugin { auto_handle: true }), 25 - FelixAudio 28 + TrackingUtilitiesPlugin 26 29 )) 27 30 .insert_resource(OxrSessionConfig { 28 - blend_mode_preference: vec![ 29 - EnvironmentBlendMode::ALPHA_BLEND, 30 - EnvironmentBlendMode::ADDITIVE, 31 - EnvironmentBlendMode::OPAQUE 32 - ], 33 31 ..Default::default() 34 32 }) 35 - .add_systems(XrSessionCreated, || { println!("[INFO] Started VR") }) 36 - .add_systems(Startup, setup) 37 - .add_systems(Startup, move | mut commands: Commands |{ 38 - let addr = env::var("HOST").unwrap().to_socket_addrs().unwrap().nth(0).unwrap(); 33 + .add_systems(XrSessionCreated, || { println!("[INFO] Started VR") }); 34 + } else{ 35 + app 36 + .add_plugins(DefaultPlugins) 37 + .insert_resource(XrState::Unavailable); 38 + } 39 + 40 + app.add_plugins(FelixAudio) 41 + .add_systems(Startup, setup) 42 + .add_systems(Startup, move | mut commands: Commands |{ 43 + let addr = env::var("HOST").unwrap().to_socket_addrs().unwrap().nth(0).unwrap(); 44 + 45 + let ( comp, voice ) = net::handle_net(addr.clone()).expect("Network Module Failed"); 46 + commands.spawn(comp); 39 47 40 - let ( comp, voice ) = net::handle_net(addr.clone()).expect("Network Module Failed"); 41 - commands.spawn(comp); 48 + match voice::init_microphone(addr, voice){ 49 + Ok(voice) => { commands.spawn(voice); }, 50 + Err(err) => println!("[WARN] Failed to start microphone: {}", err) 51 + } 52 + }) 53 + .add_systems(Update, debug_camera::update) 54 + .add_systems(Update, debug_camera::vr_locomotion) 55 + .add_systems(Startup, debug_camera::setup) 56 + .add_systems(Update, remote_player::update) 57 + .add_systems(FixedUpdate, network_interface::fixed_update); 42 58 43 - match voice::init_microphone(addr, voice){ 44 - Ok(voice) => { commands.spawn(voice); }, 45 - Err(err) => println!("[WARN] Failed to start microphone: {}", err) 46 - } 47 - }) 48 - .add_systems(Update, debug_camera::update) 49 - .add_systems(Startup, debug_camera::setup) 50 - .add_systems(Update, remote_player::update) 51 - .add_systems(FixedUpdate, network_interface::fixed_update) 52 - .run(); 53 - } 59 + app.run(); 60 + }
+21 -1
client/src/setup.rs
··· 1 1 use bevy::prelude::*; 2 + use bevy_mod_xr::session::XrTracker; 3 + use bevy_xr_utils::tracking_utils::{XrTrackedLocalFloor, XrTrackedStage, XrTrackedView}; 2 4 use felix_assets::{BevyObjectData, BevyObjectLightDataType}; 3 5 use felix_audio::FelixAudioListener; 4 6 ··· 40 42 DebugCamera::default(), // TODO: Build a proper player controller 41 43 Camera3d::default(), 42 44 Transform::from_xyz(0.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), 43 - FelixAudioListener 45 + FelixAudioListener, 46 + XrTracker, 47 + XrTrackedView 48 + )); 49 + 50 + commands.spawn(( 51 + Mesh3d(meshes.add(Cuboid::new(0.5, 0.1, 0.5))), 52 + MeshMaterial3d(materials.add(Color::srgb_u8(144, 255, 144))), 53 + Transform::from_xyz(0.0, 0.0, 0.0), 54 + XrTrackedLocalFloor, 55 + XrTracker, 56 + )); 57 + 58 + commands.spawn(( 59 + Mesh3d(meshes.add(Cuboid::new(0.5, 0.1, 0.5))), 60 + MeshMaterial3d(materials.add(Color::srgb_u8(144, 255, 255))), 61 + Transform::from_xyz(0.0, 0.0, 0.0), 62 + XrTrackedStage, 63 + XrTracker, 44 64 )); 45 65 46 66 let gos = felix_assets::load("/home/phaze/Documents/rindo.flx").unwrap();