A multiplayer VR framework w/voice chat

Compare changes

Choose any two refs to compare.

Changed files
+287 -108
.vscode
assets
client
loader
examples
src
util
+2 -1
.gitignore
··· 1 1 target/ 2 2 .env 3 - *.assetbundle 3 + *.assetbundle 4 + *.flx
+3
.vscode/settings.json
··· 1 + { 2 + "python.analysis.autoImportCompletions": true 3 + }
+13 -12
Cargo.lock
··· 2428 2428 "simd-adler32", 2429 2429 ] 2430 2430 2431 + [[package]] 2432 + name = "felix-assets" 2433 + version = "0.1.0" 2434 + dependencies = [ 2435 + "anyhow", 2436 + "bevy", 2437 + "flate2", 2438 + "unity-asset", 2439 + "unity-asset-binary", 2440 + "unity-asset-decode", 2441 + ] 2442 + 2431 2443 [[package]] 2432 2444 name = "felix-audio" 2433 2445 version = "0.1.0" ··· 2451 2463 "bevy_mod_xr", 2452 2464 "cpal 0.16.0", 2453 2465 "dotenvy", 2466 + "felix-assets", 2454 2467 "felix-audio", 2455 - "felix-loader", 2456 2468 "felix-net", 2457 2469 "openxr", 2458 2470 "tokio", 2459 2471 ] 2460 2472 2461 - [[package]] 2462 - name = "felix-loader" 2463 - version = "0.1.0" 2464 - dependencies = [ 2465 - "anyhow", 2466 - "bevy", 2467 - "unity-asset", 2468 - "unity-asset-binary", 2469 - "unity-asset-decode", 2470 - ] 2471 - 2472 2473 [[package]] 2473 2474 name = "felix-net" 2474 2475 version = "0.1.0"
+1 -1
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = [ "audio", "client", "loader", "net", "server" ] 3 + members = [ "audio", "client", "assets", "net", "server" ] 4 4 5 5 [profile.dev.package."*"] 6 6 opt-level = 3
+2 -1
loader/Cargo.toml assets/Cargo.toml
··· 1 1 [package] 2 - name = "felix-loader" 2 + name = "felix-assets" 3 3 version = "0.1.0" 4 4 edition = "2024" 5 5 ··· 9 9 unity-asset-binary = "0.2.0" 10 10 unity-asset-decode = { version = "0.2.0", features = [ "mesh" ] } 11 11 bevy = "0.17.3" 12 + flate2 = "1.1.5"
+3
assets/examples/test.rs
··· 1 + fn main(){ 2 + felix_assets::load("test.flx").unwrap(); 3 + }
+87
assets/src/buffer.rs
··· 1 + use std::io::Read; 2 + 3 + use bevy::math::{Quat, Vec3}; 4 + 5 + pub struct StreamedBuffer{ 6 + stream: Box<dyn Read> 7 + } 8 + 9 + impl StreamedBuffer{ 10 + pub fn new( stream: impl Read + 'static ) -> Self{ 11 + Self { 12 + stream: Box::new(stream) 13 + } 14 + } 15 + 16 + pub fn get_u8(&mut self) -> u8{ 17 + let mut b = [0u8; 1]; 18 + self.stream.read(&mut b).unwrap(); 19 + 20 + b[0] 21 + } 22 + 23 + pub fn get_u8s(&mut self, count: usize) -> Vec<u8>{ 24 + let mut b = vec![0u8; count]; 25 + self.stream.read(&mut b).unwrap(); 26 + 27 + b 28 + } 29 + 30 + pub fn get_i16(&mut self) -> i16{ 31 + let b = self.get_u8s(2); 32 + i16::from_le_bytes([ b[0], b[1] ]) 33 + } 34 + 35 + pub fn get_i32(&mut self) -> i32{ 36 + let b = self.get_u8s(4); 37 + i32::from_le_bytes([ b[0], b[1], b[2], b[3] ]) 38 + } 39 + 40 + pub fn get_i64(&mut self) -> i64{ 41 + let b = self.get_u8s(8); 42 + i64::from_le_bytes([ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] ]) 43 + } 44 + 45 + pub fn get_u16(&mut self) -> u16{ 46 + let b = self.get_u8s(2); 47 + u16::from_le_bytes([ b[0], b[1] ]) 48 + } 49 + 50 + pub fn get_u32(&mut self) -> u32{ 51 + let b = self.get_u8s(4); 52 + u32::from_le_bytes([ b[0], b[1], b[2], b[3] ]) 53 + } 54 + 55 + pub fn get_u64(&mut self) -> u64{ 56 + let b = self.get_u8s(8); 57 + u64::from_le_bytes([ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] ]) 58 + } 59 + 60 + pub fn get_str(&mut self) -> String{ 61 + let length = self.get_u32(); 62 + let b = self.get_u8s(length as usize); 63 + 64 + let string = str::from_utf8(&b).unwrap(); 65 + string.to_owned() 66 + } 67 + 68 + 69 + pub fn get_vec3(&mut self) -> Vec3{ 70 + Vec3 { x: self.get_f32(), y: self.get_f32(), z: self.get_f32() } 71 + } 72 + 73 + pub fn get_quat(&mut self) -> Quat{ 74 + Quat::from_xyzw(self.get_f32(), self.get_f32(), self.get_f32(), self.get_f32()) 75 + } 76 + 77 + 78 + pub fn get_f32(&mut self) -> f32{ 79 + let b = self.get_u8s(4); 80 + f32::from_le_bytes([ b[0], b[1], b[2], b[3] ]) 81 + } 82 + 83 + pub fn get_f64(&mut self) -> f64{ 84 + let b = self.get_u8s(8); 85 + f64::from_le_bytes([ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7] ]) 86 + } 87 + }
+52
assets/src/felix/mod.rs
··· 1 + use std::fs::File; 2 + 3 + use bevy::{asset::RenderAssetUsages, mesh::{Indices, PrimitiveTopology}, prelude::*, transform::components::Transform}; 4 + use flate2::read::GzDecoder; 5 + 6 + use crate::{BevyObject, buffer::StreamedBuffer}; 7 + 8 + pub fn load( path: &'static str ) -> anyhow::Result<Vec<BevyObject>>{ 9 + let mut objs = vec![]; 10 + 11 + let decoder = GzDecoder::new(File::open(path).unwrap()); 12 + let mut buf = StreamedBuffer::new(decoder); 13 + 14 + let obj_count = buf.get_u32(); 15 + 16 + for _ in 0..obj_count{ 17 + let name = buf.get_str(); 18 + let transform = Transform { 19 + translation: buf.get_vec3(), 20 + rotation: buf.get_quat(), 21 + scale: buf.get_vec3() 22 + }; 23 + 24 + let mut vertices = vec![]; 25 + let mut normals = vec![]; 26 + let mut triangles = vec![]; 27 + 28 + let vertex_count = buf.get_u32(); 29 + for _ in 0..vertex_count{ 30 + vertices.push(buf.get_vec3()); 31 + normals.push(buf.get_vec3()); 32 + } 33 + 34 + let triangle_count = buf.get_u32(); 35 + for _ in 0..triangle_count{ 36 + triangles.push(buf.get_u32()); 37 + triangles.push(buf.get_u32()); 38 + triangles.push(buf.get_u32()); 39 + } 40 + 41 + objs.push(BevyObject { 42 + name, 43 + transform, 44 + mesh: Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD) 45 + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) 46 + .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals) 47 + .with_inserted_indices(Indices::U32(triangles)) 48 + }); 49 + } 50 + 51 + Ok(objs) 52 + }
+16
assets/src/lib.rs
··· 1 + use bevy::{mesh::Mesh, transform::components::Transform}; 2 + 3 + mod unity; 4 + mod felix; 5 + mod buffer; 6 + 7 + #[derive(Debug)] 8 + pub struct BevyObject{ 9 + pub name: String, 10 + pub transform: Transform, 11 + pub mesh: Mesh, 12 + } 13 + 14 + pub fn load( path: &'static str ) -> anyhow::Result<Vec<BevyObject>>{ 15 + felix::load(path) 16 + }
loader/src/unity/component/mesh_renderer.rs assets/src/unity/component/mesh_renderer.rs
loader/src/unity/component/mod.rs assets/src/unity/component/mod.rs
+1 -1
loader/src/unity/component/transform.rs assets/src/unity/component/transform.rs
··· 1 1 use bevy::transform::components::Transform; 2 2 use unity_asset_binary::object::UnityObject; 3 3 4 - use crate::util::unity_value::{unity_value_to_quat, unity_value_to_vec3}; 4 + use crate::unity::unity_value::{unity_value_to_quat, unity_value_to_vec3}; 5 5 6 6 pub fn into_bevy_transform(value: UnityObject) -> Transform { 7 7 let position = value.get("m_LocalPosition").unwrap();
+24 -7
loader/src/unity/mod.rs assets/src/unity/mod.rs
··· 1 + // NOTE: Very WIP, Does not work!! 2 + 1 3 use anyhow::bail; 2 4 use bevy::{prelude::*, transform::components::Transform}; 3 5 use unity_asset::environment::{Environment, EnvironmentObjectRef}; 4 6 use unity_asset_binary::unity_version::UnityVersion; 5 7 use unity_asset_decode::mesh::MeshParser; 6 8 7 - use crate::{unity::component::{Component, mesh_renderer::MeshRenderer, transform::into_bevy_transform}, util}; 9 + use crate::{BevyObject, unity::{component::{Component, mesh_renderer::MeshRenderer, transform::into_bevy_transform}, unity_value::try_unity_mesh_to_bevy}}; 8 10 9 11 mod component; 12 + mod unity_value; 10 13 11 14 #[derive(Debug)] 12 - pub struct GameObject{ 15 + pub struct UnityGameObject{ 13 16 pub name: String, 14 17 pub active: bool, 15 18 ··· 18 21 pub mesh_filter: Option<Mesh>, 19 22 } 20 23 21 - pub fn load( path: &'static str ) -> anyhow::Result<Vec<GameObject>>{ 24 + pub fn load( path: &'static str ) -> anyhow::Result<Vec<BevyObject>>{ 22 25 let mesh_parser = MeshParser::new(UnityVersion::parse_version("2020.3.12f1").unwrap()); 23 26 24 27 let mut env = Environment::new(); ··· 60 63 let mesh = env.find_binary_object(path_id).unwrap(); 61 64 let mesh_obj = mesh.read().unwrap(); 62 65 63 - if let Some(mesh) = util::unity_value::try_unity_mesh_to_bevy(mesh_obj, &mesh_parser){ 66 + if let Some(mesh) = try_unity_mesh_to_bevy(mesh_obj, &mesh_parser){ 64 67 mesh_filter = Some(mesh); 65 68 } else{ 66 69 println!("[WARN] Failed to load mesh for: {}", go.name); ··· 73 76 74 77 if transform.is_none(){ bail!("Cannot find transform for object {}", go.name); } 75 78 76 - let gameobject = GameObject { 79 + let gameobject = UnityGameObject { 77 80 name: go.name, 78 81 active: go.active, 79 82 ··· 83 86 }; 84 87 85 88 gameobjects.push(gameobject); 86 - // break; 89 + break; 87 90 } 88 91 }, 89 92 _ => {} 90 93 } 91 94 } 92 95 93 - Ok(gameobjects) 96 + let mut objs = vec![]; 97 + 98 + for go in gameobjects{ 99 + if go.active{ 100 + if let Some(mesh) = go.mesh_filter{ 101 + objs.push(BevyObject { 102 + name: go.name, 103 + transform: go.transform, 104 + mesh: mesh 105 + }); 106 + } 107 + } 108 + } 109 + 110 + Ok(objs) 94 111 }
+76 -64
loader/src/util/unity_value.rs assets/src/unity/unity_value.rs
··· 1 - use std::{fs::File, io::Write}; 1 + use std::{fs::File, io::Write, mem}; 2 2 3 3 use bevy::{asset::RenderAssetUsages, math::{Quat, Vec3}, mesh::{Indices, PrimitiveTopology}, prelude::*}; 4 4 use unity_asset::UnityValue; ··· 26 26 ) 27 27 } 28 28 29 - fn get_channel_dtype( format: u8 ) -> String{ 30 - match format{ 31 - 0 => "f", // kVertexFormatFloat 32 - // 1 => "e", // kVertexFormatFloat16 33 - 2 => "B", // kVertexFormatUNorm8 34 - 3 => "b", // kVertexFormatSNorm8 35 - 4 => "H", // kVertexFormatUNorm16 36 - 5 => "h", // kVertexFormatSNorm16 37 - 6 => "B", // kVertexFormatUInt8 38 - 7 => "b", // kVertexFormatSInt8 39 - 8 => "H", // kVertexFormatUInt16 40 - 9 => "h", // kVertexFormatSInt16 41 - 10 => "I", // kVertexFormatUInt32 42 - 11 => "i", // kVertexFormatSInt32 43 - 44 - _ => "" 45 - }.into() 46 - } 47 - 48 29 fn get_channel_component_size( format: u8 ) -> usize{ 49 30 match format{ 50 31 0 => 4, // kVertexFormatFloat 51 - // 1 => 2, // kVertexFormatFloat16 32 + 1 => 2, // kVertexFormatFloat16 52 33 2 => 1, // kVertexFormatUNorm8 53 34 3 => 1, // kVertexFormatSNorm8 54 35 4 => 2, // kVertexFormatUNorm16 ··· 66 47 67 48 enum UnityDataTypeArray{ 68 49 kVertexFormatFloat(Vec<Vec<f32>>), 69 - // kVertexFormatFloat16(Vec<Vec<f16>>), 50 + kVertexFormatFloat16(Vec<Vec<f32>>), 70 51 kVertexFormatUNorm8(Vec<Vec<u8>>), 71 52 kVertexFormatSNorm8(Vec<Vec<i8>>), 72 53 kVertexFormatUNorm16(Vec<Vec<u16>>), ··· 121 102 UnityDataTypeArray::kVertexFormatFloat(decoded) 122 103 }, 123 104 105 + 1 => { 106 + let mut decoded: Vec<Vec<_>> = vec![]; 107 + let item_size = get_channel_component_size(component_dtype); 108 + let vec_size = item_size * dimension; 109 + 110 + for i in 0..( bytes.len() / vec_size ){ 111 + let indx = i * vec_size; 112 + let mut vec = vec![0.0; dimension]; 113 + 114 + for j in 0..dimension{ 115 + vec[j] = f32::from_le_bytes([ 116 + bytes[indx + (j * item_size)], 117 + bytes[indx + (j * item_size) + 1], 118 + 0, 0 119 + ]); 120 + } 121 + decoded.push(vec); 122 + } 123 + 124 + UnityDataTypeArray::kVertexFormatFloat16(decoded) 125 + }, 126 + 124 127 2 => { 125 128 let mut decoded: Vec<Vec<u8>> = vec![]; 126 129 let item_size = get_channel_component_size(component_dtype); ··· 334 337 let mesh = mesh_parser.parse_from_unity_object(&mesh_obj).unwrap().mesh; 335 338 let vertex_data = mesh.vertex_data; 336 339 340 + dbg!(&vertex_data.channels); 341 + 337 342 let mut streams = vec![]; 338 - let stream_count = 1 + vertex_data.channels.iter().map(|x| x.stream).max()?; 343 + let stream_count = 1 + vertex_data.channels.iter().map(|x| x.stream).max()? as usize; 339 344 340 345 let mut offset = 0; 341 346 ··· 345 350 346 351 let mut i = 0; 347 352 for channel in &vertex_data.channels{ 348 - if channel.stream == s && channel.dimension > 0{ 353 + if channel.stream == s as u8 && channel.dimension > 0{ 349 354 chn_mask |= 1 << i; 350 355 351 356 let component_size = get_channel_component_size(channel.format); 352 - stride += (channel.dimension & 0xF) as usize * component_size; 357 + stride += channel.dimension as usize * component_size; 353 358 } 354 359 355 - streams.push(StreamInfo { 356 - channel_mask: chn_mask, 357 - offset, 358 - stride 359 - }); 360 - 361 - offset += vertex_data.vertex_count as usize * stride; 362 - offset = ( offset + ( 16 - 1 ) ) & !(16 - 1); 363 - 364 360 i += 1; 365 361 } 362 + 363 + streams.push(StreamInfo { 364 + channel_mask: chn_mask, 365 + offset, 366 + stride 367 + }); 368 + 369 + offset += vertex_data.vertex_count as usize * stride; 370 + offset = ( offset + ( 16 - 1 ) ) & !(16 - 1); 366 371 } 367 372 368 373 let mut i = 0; ··· 371 376 372 377 if stream.channel_mask >> i & 1 == 1{ 373 378 let component_byte_size = get_channel_component_size(channel.format); 374 - let channel_dimension = (channel.dimension & 0xF) as usize; 379 + dbg!(&component_byte_size, &channel.format); 380 + let channel_dimension = channel.dimension as usize; 375 381 376 382 let mut bytes = 377 383 vec![0; vertex_data.vertex_count as usize * channel_dimension * component_byte_size]; 378 384 379 - dbg!(vertex_data.vertex_count); 380 - 381 - let vertex_base_offset = stream.offset + channel.offset as usize; 382 385 for v in 0..vertex_data.vertex_count{ 383 - let vertex_offset = vertex_base_offset + stream.stride * v as usize; 386 + let vertex_offset = stream.offset + channel.offset as usize + stream.stride * v as usize; 384 387 385 388 for d in 0..channel_dimension{ 386 - let vertex_data_src = vertex_offset + component_byte_size * d as usize; 387 - let component_data_src = component_byte_size * ( v as usize * channel_dimension + d as usize ); 389 + let component_offset = vertex_offset + component_byte_size * d as usize; 390 + let dst_offset = component_byte_size * (v as usize * channel.dimension as usize + d as usize); 388 391 389 - let buff = vertex_data.data_size[vertex_data_src..vertex_data_src + component_byte_size].to_vec(); 390 - bytes.splice(component_data_src..component_data_src + component_byte_size, buff); 392 + let slice = &vertex_data.data_size[component_offset..component_offset + component_byte_size]; 393 + bytes[dst_offset..dst_offset + component_byte_size].clone_from_slice(slice); 391 394 } 392 395 } 393 396 ··· 409 412 let mut triangles = vec![]; 410 413 if mesh.index_buffer.len() > 0{ 411 414 for submesh in mesh.sub_meshes{ 412 - let first_index = submesh.first_byte / 4; 413 - let triangle_count = submesh.index_count / 6; 414 - 415 - for i in 0..triangle_count{ 416 - let indx = i * 6; 417 - 418 - triangles.push(u16::from_le_bytes([ 419 - mesh.index_buffer[(first_index + indx) as usize], 420 - mesh.index_buffer[(first_index + indx + 1) as usize] 421 - ])); 422 - 423 - triangles.push(u16::from_le_bytes([ 424 - mesh.index_buffer[(first_index + indx + 2) as usize], 425 - mesh.index_buffer[(first_index + indx + 3) as usize] 426 - ])); 427 - 428 - triangles.push(u16::from_le_bytes([ 429 - mesh.index_buffer[(first_index + indx + 4) as usize], 430 - mesh.index_buffer[(first_index + indx + 5) as usize] 431 - ])); 415 + let first_index = submesh.first_byte / 2; 416 + 417 + match submesh.topology{ 418 + 0 => { 419 + // for i in (0..submesh.index_count).step_by(3){ 420 + // triangles.push(mesh.index_buffer[(first_index + i) as usize] as u16); 421 + // triangles.push(mesh.index_buffer[(first_index + i + 1) as usize] as u16); 422 + // triangles.push(mesh.index_buffer[(first_index + i + 2) as usize] as u16); 423 + // } 424 + 425 + for i in (0..submesh.index_count).step_by(6){ 426 + triangles.push(u16::from_le_bytes([ 427 + mesh.index_buffer[(first_index + i) as usize], 428 + mesh.index_buffer[(first_index + i + 1) as usize] 429 + ])); 430 + 431 + triangles.push(u16::from_le_bytes([ 432 + mesh.index_buffer[(first_index + i + 2) as usize], 433 + mesh.index_buffer[(first_index + i + 3) as usize] 434 + ])); 435 + 436 + triangles.push(u16::from_le_bytes([ 437 + mesh.index_buffer[(first_index + i + 4) as usize], 438 + mesh.index_buffer[(first_index + i + 5) as usize] 439 + ])); 440 + } 441 + }, 442 + // TODO: Implement other topologys 443 + _ => {} 432 444 } 433 445 } 434 446 } else{ ··· 446 458 447 459 for i in 0..(triangles.len() / 3){ 448 460 let indx = i * 3; 449 - writeln!(file, "f {} {} {}", triangles[indx], triangles[indx + 1], triangles[indx + 2]).unwrap(); 461 + writeln!(file, "f {} {} {}", triangles[indx] + 1, triangles[indx + 1] + 1, triangles[indx + 2] + 1).unwrap(); 450 462 } 451 463 452 464 Some(Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD)
+1 -1
client/Cargo.toml
··· 10 10 dotenvy = "0.15.7" 11 11 felix-net = { path = '../net' } 12 12 felix-audio = { path = '../audio' } 13 - felix-loader = { path = '../loader' } 13 + felix-assets = { path = '../assets' } 14 14 tokio = { version = "1.48.0", features = ["full"] } 15 15 bevy_mod_openxr = "0.4.0" 16 16 openxr = "0.19.0"
+6 -8
client/src/setup.rs
··· 45 45 FelixAudioListener 46 46 )); 47 47 48 - let gos = felix_loader::load("bundle").unwrap(); 48 + let gos = felix_assets::load("test.flx").unwrap(); 49 49 for go in gos{ 50 - if let Some(mesh) = go.mesh_filter{ 51 - commands.spawn(( 52 - Mesh3d(meshes.add(mesh)), 53 - MeshMaterial3d(materials.add(Color::WHITE)), 54 - go.transform 55 - )); 56 - } 50 + commands.spawn(( 51 + Mesh3d(meshes.add(go.mesh)), 52 + MeshMaterial3d(materials.add(Color::WHITE)), 53 + go.transform 54 + )); 57 55 } 58 56 }
-3
loader/examples/test.rs
··· 1 - fn main(){ 2 - felix_loader::load("bundle").unwrap(); 3 - }
-8
loader/src/lib.rs
··· 1 - use crate::unity::GameObject; 2 - 3 - mod unity; 4 - mod util; 5 - 6 - pub fn load( path: &'static str ) -> anyhow::Result<Vec<GameObject>>{ 7 - unity::load(path) 8 - }
-1
loader/src/util/mod.rs
··· 1 - pub mod unity_value;