A multiplayer VR framework w/voice chat

some audio fixes / start on unity asset bundle loading

phaz.uk c8a357ab 52998727

verified
+2 -1
.gitignore
··· 1 1 target/ 2 - .env 2 + .env 3 + *.assetbundle
+229
Cargo.lock
··· 104 104 ] 105 105 106 106 [[package]] 107 + name = "alloc-no-stdlib" 108 + version = "2.0.4" 109 + source = "registry+https://github.com/rust-lang/crates.io-index" 110 + checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 111 + 112 + [[package]] 113 + name = "alloc-stdlib" 114 + version = "0.2.2" 115 + source = "registry+https://github.com/rust-lang/crates.io-index" 116 + checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 117 + dependencies = [ 118 + "alloc-no-stdlib", 119 + ] 120 + 121 + [[package]] 107 122 name = "alsa" 108 123 version = "0.9.1" 109 124 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 181 196 dependencies = [ 182 197 "num-traits", 183 198 ] 199 + 200 + [[package]] 201 + name = "array-init" 202 + version = "2.1.0" 203 + source = "registry+https://github.com/rust-lang/crates.io-index" 204 + checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" 184 205 185 206 [[package]] 186 207 name = "arrayref" ··· 1597 1618 ] 1598 1619 1599 1620 [[package]] 1621 + name = "binrw" 1622 + version = "0.15.0" 1623 + source = "registry+https://github.com/rust-lang/crates.io-index" 1624 + checksum = "81419ff39e6ed10a92a7f125290859776ced35d9a08a665ae40b23e7ca702f30" 1625 + dependencies = [ 1626 + "array-init", 1627 + "binrw_derive", 1628 + "bytemuck", 1629 + ] 1630 + 1631 + [[package]] 1632 + name = "binrw_derive" 1633 + version = "0.15.0" 1634 + source = "registry+https://github.com/rust-lang/crates.io-index" 1635 + checksum = "376404e55ec40d0d6f8b4b7df3f87b87954bd987f0cf9a7207ea3b6ea5c9add4" 1636 + dependencies = [ 1637 + "either", 1638 + "owo-colors", 1639 + "proc-macro2", 1640 + "quote", 1641 + "syn", 1642 + ] 1643 + 1644 + [[package]] 1600 1645 name = "bit-set" 1601 1646 version = "0.8.0" 1602 1647 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1678 1723 ] 1679 1724 1680 1725 [[package]] 1726 + name = "brotli" 1727 + version = "3.5.0" 1728 + source = "registry+https://github.com/rust-lang/crates.io-index" 1729 + checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" 1730 + dependencies = [ 1731 + "alloc-no-stdlib", 1732 + "alloc-stdlib", 1733 + "brotli-decompressor", 1734 + ] 1735 + 1736 + [[package]] 1737 + name = "brotli-decompressor" 1738 + version = "2.5.1" 1739 + source = "registry+https://github.com/rust-lang/crates.io-index" 1740 + checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" 1741 + dependencies = [ 1742 + "alloc-no-stdlib", 1743 + "alloc-stdlib", 1744 + ] 1745 + 1746 + [[package]] 1681 1747 name = "bumpalo" 1682 1748 version = "3.19.1" 1683 1749 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2060 2126 ] 2061 2127 2062 2128 [[package]] 2129 + name = "crc" 2130 + version = "3.4.0" 2131 + source = "registry+https://github.com/rust-lang/crates.io-index" 2132 + checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" 2133 + dependencies = [ 2134 + "crc-catalog", 2135 + ] 2136 + 2137 + [[package]] 2138 + name = "crc-catalog" 2139 + version = "2.4.0" 2140 + source = "registry+https://github.com/rust-lang/crates.io-index" 2141 + checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 2142 + 2143 + [[package]] 2063 2144 name = "crc32fast" 2064 2145 version = "1.5.0" 2065 2146 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2371 2452 "cpal 0.16.0", 2372 2453 "dotenvy", 2373 2454 "felix-audio", 2455 + "felix-loader", 2374 2456 "felix-net", 2375 2457 "openxr", 2376 2458 "tokio", 2377 2459 ] 2378 2460 2379 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 + [[package]] 2380 2473 name = "felix-net" 2381 2474 version = "0.1.0" 2382 2475 dependencies = [ ··· 3130 3223 checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 3131 3224 3132 3225 [[package]] 3226 + name = "lz4_flex" 3227 + version = "0.12.0" 3228 + source = "registry+https://github.com/rust-lang/crates.io-index" 3229 + checksum = "ab6473172471198271ff72e9379150e9dfd70d8e533e0752a27e515b48dd375e" 3230 + dependencies = [ 3231 + "twox-hash", 3232 + ] 3233 + 3234 + [[package]] 3235 + name = "lzma-rs" 3236 + version = "0.3.0" 3237 + source = "registry+https://github.com/rust-lang/crates.io-index" 3238 + checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" 3239 + dependencies = [ 3240 + "byteorder", 3241 + "crc", 3242 + ] 3243 + 3244 + [[package]] 3133 3245 name = "mach2" 3134 3246 version = "0.4.3" 3135 3247 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3403 3515 ] 3404 3516 3405 3517 [[package]] 3518 + name = "num_cpus" 3519 + version = "1.17.0" 3520 + source = "registry+https://github.com/rust-lang/crates.io-index" 3521 + checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" 3522 + dependencies = [ 3523 + "hermit-abi", 3524 + "libc", 3525 + ] 3526 + 3527 + [[package]] 3406 3528 name = "num_enum" 3407 3529 version = "0.7.5" 3408 3530 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3819 3941 dependencies = [ 3820 3942 "ttf-parser 0.25.1", 3821 3943 ] 3944 + 3945 + [[package]] 3946 + name = "owo-colors" 3947 + version = "4.2.3" 3948 + source = "registry+https://github.com/rust-lang/crates.io-index" 3949 + checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 3822 3950 3823 3951 [[package]] 3824 3952 name = "parking" ··· 4400 4528 ] 4401 4529 4402 4530 [[package]] 4531 + name = "serde_bytes" 4532 + version = "0.11.19" 4533 + source = "registry+https://github.com/rust-lang/crates.io-index" 4534 + checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" 4535 + dependencies = [ 4536 + "serde", 4537 + "serde_core", 4538 + ] 4539 + 4540 + [[package]] 4403 4541 name = "serde_core" 4404 4542 version = "1.0.228" 4405 4543 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4433 4571 ] 4434 4572 4435 4573 [[package]] 4574 + name = "serde_yaml" 4575 + version = "0.9.34+deprecated" 4576 + source = "registry+https://github.com/rust-lang/crates.io-index" 4577 + checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 4578 + dependencies = [ 4579 + "indexmap", 4580 + "itoa", 4581 + "ryu", 4582 + "serde", 4583 + "unsafe-libyaml", 4584 + ] 4585 + 4586 + [[package]] 4436 4587 name = "sharded-slab" 4437 4588 version = "0.1.7" 4438 4589 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5110 5261 version = "0.2.6" 5111 5262 source = "registry+https://github.com/rust-lang/crates.io-index" 5112 5263 checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 5264 + 5265 + [[package]] 5266 + name = "unity-asset" 5267 + version = "0.2.0" 5268 + source = "registry+https://github.com/rust-lang/crates.io-index" 5269 + checksum = "6705b95fda449527055ff77fe5cff49f774dde7335ed92bc3d048fc74066ebaf" 5270 + dependencies = [ 5271 + "unity-asset-binary", 5272 + "unity-asset-core", 5273 + "unity-asset-yaml", 5274 + ] 5275 + 5276 + [[package]] 5277 + name = "unity-asset-binary" 5278 + version = "0.2.0" 5279 + source = "registry+https://github.com/rust-lang/crates.io-index" 5280 + checksum = "d739d1414c1732be1035417f83b1e60f234c7d5eb8abda0ee70d178a58137cb7" 5281 + dependencies = [ 5282 + "binrw", 5283 + "brotli", 5284 + "byteorder", 5285 + "flate2", 5286 + "indexmap", 5287 + "lz4_flex", 5288 + "lzma-rs", 5289 + "memmap2", 5290 + "num_cpus", 5291 + "once_cell", 5292 + "regex", 5293 + "serde", 5294 + "serde_json", 5295 + "thiserror 2.0.17", 5296 + "unity-asset-core", 5297 + ] 5298 + 5299 + [[package]] 5300 + name = "unity-asset-core" 5301 + version = "0.2.0" 5302 + source = "registry+https://github.com/rust-lang/crates.io-index" 5303 + checksum = "12903fd47e122b5e7e335fc9cb5540bb35ab0ffe5bb57a7741a8c7be8d361361" 5304 + dependencies = [ 5305 + "indexmap", 5306 + "lazy_static", 5307 + "serde", 5308 + "serde_bytes", 5309 + "thiserror 2.0.17", 5310 + ] 5311 + 5312 + [[package]] 5313 + name = "unity-asset-decode" 5314 + version = "0.2.0" 5315 + source = "registry+https://github.com/rust-lang/crates.io-index" 5316 + checksum = "9ea6e630cdefde54a88ca4fbae54cf3d3e5ba527fac96b085562da3b3834b77b" 5317 + dependencies = [ 5318 + "indexmap", 5319 + "serde", 5320 + "unity-asset-binary", 5321 + "unity-asset-core", 5322 + ] 5323 + 5324 + [[package]] 5325 + name = "unity-asset-yaml" 5326 + version = "0.2.0" 5327 + source = "registry+https://github.com/rust-lang/crates.io-index" 5328 + checksum = "b991e340c27b1578e657defee9f57de867d853b8f40448fb079d0ee3a5ebc77e" 5329 + dependencies = [ 5330 + "indexmap", 5331 + "serde", 5332 + "serde_yaml", 5333 + "thiserror 2.0.17", 5334 + "unity-asset-core", 5335 + ] 5336 + 5337 + [[package]] 5338 + name = "unsafe-libyaml" 5339 + version = "0.2.11" 5340 + source = "registry+https://github.com/rust-lang/crates.io-index" 5341 + checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 5113 5342 5114 5343 [[package]] 5115 5344 name = "uuid"
+4 -1
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = [ "audio", "client", "net", "server" ] 3 + members = [ "audio", "client", "loader", "net", "server" ] 4 + 5 + [profile.dev.package."*"] 6 + opt-level = 3
+1 -6
audio/src/source/static_source.rs
··· 1 1 use std::{sync::{Arc, Mutex}, thread}; 2 2 3 3 use bevy::transform::components::Transform; 4 - use kira::{Decibels, Easing, Mapping, Mix, Tween, Value, effect::{filter::FilterBuilder, reverb::ReverbBuilder}, sound::static_sound::StaticSoundData, track::{SendTrackBuilder, SendTrackHandle, SpatialTrackBuilder, SpatialTrackHandle}}; 4 + use kira::{Decibels, Easing, Mapping, Tween, Value, effect::{filter::FilterBuilder}, sound::static_sound::StaticSoundData, track::{SendTrackBuilder, SendTrackHandle, SpatialTrackBuilder, SpatialTrackHandle}}; 5 5 6 6 use crate::{FelixAudioComponent, source::AudioSource}; 7 7 ··· 52 52 .with_effect(FilterBuilder::new().cutoff(Value::FromListenerDistance(Mapping { 53 53 input_range: ( 0.0, 20.0 ), 54 54 output_range: ( 18000.0, 2000.0 ), 55 - easing: Easing::Linear 56 - }))) 57 - .with_effect(ReverbBuilder::new().mix(Value::FromListenerDistance(Mapping { 58 - input_range: ( 0.0, 100.0 ), 59 - output_range: ( Mix::DRY, Mix::WET ), 60 55 easing: Easing::Linear 61 56 }))) 62 57 .with_send(&send_track, Value::FromListenerDistance(Mapping {
+1 -6
audio/src/source/stream_source.rs
··· 1 1 use bevy::transform::components::Transform; 2 - use kira::{Decibels, Easing, Mapping, Mix, Tween, Value, effect::{filter::FilterBuilder, reverb::ReverbBuilder}, track::{SendTrackBuilder, SendTrackHandle, SpatialTrackBuilder, SpatialTrackHandle}}; 2 + use kira::{Decibels, Easing, Mapping, Tween, Value, effect::{filter::FilterBuilder}, track::{SendTrackBuilder, SendTrackHandle, SpatialTrackBuilder, SpatialTrackHandle}}; 3 3 4 4 use crate::{FelixAudioComponent, source::{AudioSource, stream_source::sound_data::StreamAudioSourceSoundData}}; 5 5 ··· 44 44 .with_effect(FilterBuilder::new().cutoff(Value::FromListenerDistance(Mapping { 45 45 input_range: ( 0.0, 20.0 ), 46 46 output_range: ( 18000.0, 2000.0 ), 47 - easing: Easing::Linear 48 - }))) 49 - .with_effect(ReverbBuilder::new().mix(Value::FromListenerDistance(Mapping { 50 - input_range: ( 0.0, 100.0 ), 51 - output_range: ( Mix::DRY, Mix::WET ), 52 47 easing: Easing::Linear 53 48 }))) 54 49 .with_send(&send_track, Value::FromListenerDistance(Mapping {
+1 -1
audio/src/voice/decoder.rs
··· 26 26 27 27 let mut total = 0.0; 28 28 for sample in buffer{ total += sample.powi(2) } 29 - *set_rms.write().unwrap() = ( total / buffer.len() as f32 ).sqrt(); 29 + *set_rms.write().unwrap() = 20.0 * ( total / buffer.len() as f32 ).sqrt().log10(); 30 30 31 31 let mut voice = queue.lock().unwrap(); 32 32 for sample in buffer{ voice.push_back(sample); }
+34 -22
audio/src/voice/microphone.rs
··· 1 - use std::{env, net::{ToSocketAddrs, UdpSocket}, sync::{Arc, Mutex, RwLock}}; 1 + use std::{env, net::{SocketAddr, UdpSocket}, sync::{Arc, Mutex, RwLock}}; 2 2 3 3 use bevy::ecs::component::Component; 4 4 use cpal::{BufferSize, SampleRate, Stream, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}}; ··· 10 10 #[derive(Component)] 11 11 pub struct VoiceChatMicrophone{ 12 12 stream: Option<Stream>, 13 - udp: Option<UdpSocket>, 13 + addr: SocketAddr, 14 + udp: UdpSocket, 14 15 muted: Arc<Mutex<bool>>, 15 - last_rms: Arc<RwLock<isize>> 16 + last_rms: Arc<RwLock<f32>>, 17 + gate_level: Arc<RwLock<f32>> 16 18 } 17 19 18 20 impl VoiceChatMicrophone{ 19 - pub fn new( socket: UdpSocket ) -> Self{ 21 + pub fn new( addr: SocketAddr, socket: UdpSocket ) -> Self{ 20 22 Self { 21 23 stream: None, 22 - udp: Some(socket), 24 + addr, 25 + udp: socket, 23 26 muted: Arc::new(Mutex::new(false)), // TODO: Default to muted 24 - last_rms: Arc::new(RwLock::new(0)) 27 + last_rms: Arc::new(RwLock::new(0.0)), 28 + gate_level: Arc::new(RwLock::new(-65.0)) 25 29 } 26 30 } 27 31 ··· 58 62 let mut buffer = [0; MONO_20MS]; 59 63 60 64 let muted = self.muted.clone(); 61 - let udp = self.udp.take().unwrap(); 62 65 63 - let addr = env::var("HOST")?.to_socket_addrs()?.nth(0).unwrap(); 66 + let udp = self.udp.try_clone().unwrap(); 67 + let addr = self.addr.clone(); 64 68 65 69 for conf in mic.supported_input_configs().unwrap(){ 66 70 println!("{} {:?} {} {:?}", conf.channels(), conf.buffer_size(), conf.sample_format(), conf.with_max_sample_rate()); 67 71 } 68 72 69 73 let set_rms = self.last_rms.clone(); 74 + let gate_level = self.gate_level.clone(); 70 75 71 76 let stream = mic.build_input_stream( 72 77 &StreamConfig { ··· 81 86 buffer_indx = 0; 82 87 } else{ 83 88 for i in 0..data.len(){ 84 - if i % 2 == 0{ continue; } 89 + if i % 2 == 1{ continue; } 85 90 86 91 buffer[buffer_indx] = data[i]; 87 92 buffer_indx += 1; ··· 89 94 if buffer_indx >= MONO_20MS{ 90 95 buffer_indx = 0; 91 96 92 - let mut total = 0; 93 - for sample in buffer{ total += (sample as isize).pow(2) } 94 - *set_rms.write().unwrap() = ( total / buffer.len() as isize ).isqrt(); 97 + let mut total = 0.0; 98 + for sample in buffer{ total += (sample as f32).powi(2) } 95 99 96 - let len = encoder.encode(&buffer, &mut output).unwrap(); 97 - let buf_to_send = output[0..len].to_vec(); 100 + let rms = 20.0 * ( ( total / buffer.len() as f32 ).sqrt() / i16::MAX as f32 ).log10(); 101 + *set_rms.write().unwrap() = rms; 98 102 99 - let packet = PlayerVoicePacket { 100 - id: "".into(), // NOTE: I know this kinda seems stupid but it's easier for me to get the player id on the server so imma just make the server fill this in. 101 - packet: buf_to_send 102 - }; 103 + if rms > *gate_level.read().unwrap(){ 104 + let len = encoder.encode(&buffer, &mut output).unwrap(); 105 + let buf_to_send = output[0..len].to_vec(); 103 106 104 - let packet: Vec<u8> = packet.to_buf().into(); 105 - udp.send_to(&packet, addr).unwrap(); 107 + let packet = PlayerVoicePacket { 108 + id: "".into(), // NOTE: I know this kinda seems stupid but it's easier for me to get the player id on the server so imma just make the server fill this in. 109 + packet: buf_to_send 110 + }; 111 + 112 + let packet: Vec<u8> = packet.to_buf().into(); 113 + udp.send_to(&packet, addr).unwrap(); 114 + } 106 115 } 107 116 } 108 117 } ··· 117 126 Ok(()) 118 127 } 119 128 120 - pub fn get_rms_of_last_packet( &self ) -> f32{ 121 - *self.last_rms.read().unwrap() as f32 / i16::MAX as f32 129 + pub fn get_rms_of_last_packet( &self ) -> ( f32, bool ){ 130 + let rms = *self.last_rms.read().unwrap(); 131 + let gate = *self.gate_level.read().unwrap(); 132 + 133 + ( rms, rms > gate ) 122 134 } 123 135 }
+3 -3
audio/src/voice/mod.rs
··· 1 - use std::net::UdpSocket; 1 + use std::net::{SocketAddr, UdpSocket}; 2 2 3 3 use crate::voice::microphone::VoiceChatMicrophone; 4 4 5 5 pub mod decoder; 6 6 pub mod microphone; 7 7 8 - pub fn init_microphone( socket: UdpSocket ) -> anyhow::Result<VoiceChatMicrophone>{ 9 - let mut handle = VoiceChatMicrophone::new(socket); 8 + pub fn init_microphone( addr: SocketAddr, socket: UdpSocket ) -> anyhow::Result<VoiceChatMicrophone>{ 9 + let mut handle = VoiceChatMicrophone::new(addr, socket); 10 10 handle.start_stream()?; 11 11 12 12 Ok(handle)
+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 14 tokio = { version = "1.48.0", features = ["full"] } 14 15 bevy_mod_openxr = "0.4.0" 15 16 openxr = "0.19.0"
+11 -6
client/src/components/debug_camera.rs
··· 78 78 79 79 let mut remote_rms = "".to_owned(); 80 80 for id in net_manager.get_remote_player_voice_ids(){ 81 - remote_rms += format!("{}: {:.4}\n", id, net_manager.get_remote_player_voice_rms(&id)).as_str(); 81 + remote_rms += format!( 82 + "{}: {:.4}db\n", 83 + id, 84 + net_manager.get_remote_player_voice_rms(&id) 85 + ).as_str(); 82 86 } 83 87 84 - let rms = if let Ok(mic) = voice.single(){ 88 + let ( rms, mic_open ) = if let Ok(mic) = voice.single(){ 85 89 mic.get_rms_of_last_packet() 86 90 } else{ 87 - -1.0 91 + ( -1.0, false ) 88 92 }; 89 93 90 94 let ( mut text, _ ) = debug_text.single_mut().unwrap(); 91 95 text.0 = format!( 92 - "Microphone RMS: {:.4} 93 - {}Position: {:.2} {:.2} {:.2} 94 - Rotation: {:.2} {:.2} {:.2} {:.2} 96 + "Microphone RMS: {:.4}db Open: {} 97 + {}Position: X: {:.2} Y: {:.2} Z: {:.2} 98 + Rotation: X: {:.2} Y: {:.2} Z: {:.2} W: {:.2} 95 99 XR State: {:#?}", 96 100 rms, 101 + mic_open, 97 102 98 103 remote_rms, 99 104
+6 -2
client/src/main.rs
··· 1 + use std::{env, net::ToSocketAddrs}; 2 + 1 3 use bevy::{app::{App, FixedUpdate, Startup, Update}, ecs::system::Commands, prelude::*, render::pipelined_rendering::PipelinedRenderingPlugin}; 2 4 use bevy_mod_openxr::{add_xr_plugins, resources::OxrSessionConfig}; 3 5 use bevy_mod_xr::session::{XrSessionCreated, XrSessionPlugin}; ··· 33 35 .add_systems(XrSessionCreated, || { println!("[INFO] Started VR") }) 34 36 .add_systems(Startup, setup) 35 37 .add_systems(Startup, move | mut commands: Commands |{ 36 - let ( comp, voice ) = net::handle_net().expect("Network Module Failed"); 38 + let addr = env::var("HOST").unwrap().to_socket_addrs().unwrap().nth(0).unwrap(); 39 + 40 + let ( comp, voice ) = net::handle_net(addr.clone()).expect("Network Module Failed"); 37 41 commands.spawn(comp); 38 42 39 - match voice::init_microphone(voice){ 43 + match voice::init_microphone(addr, voice){ 40 44 Ok(voice) => { commands.spawn(voice); }, 41 45 Err(err) => println!("[WARN] Failed to start microphone: {}", err) 42 46 }
+1 -1
client/src/net/connection.rs
··· 40 40 voice_queues: Arc::new(RwLock::new(HashMap::new())), 41 41 id: "".to_owned(), 42 42 43 - killed_signal: killed_signal_send 43 + killed_signal: killed_signal_send 44 44 }; 45 45 46 46 conn.start_listener_thread(event_sender, killed_signal).unwrap();
+2 -4
client/src/net/mod.rs
··· 1 - use std::{env, net::{TcpStream, ToSocketAddrs, UdpSocket}}; 1 + use std::net::{SocketAddr, TcpStream, UdpSocket}; 2 2 3 3 use bevy::prelude::*; 4 4 use felix_net::packet::PacketTypes; ··· 14 14 RecvPacket(PacketTypes) 15 15 } 16 16 17 - pub fn handle_net() -> anyhow::Result<( Connection, UdpSocket )>{ 18 - let addr = env::var("HOST")?.to_socket_addrs()?.nth(0).unwrap(); 19 - 17 + pub fn handle_net( addr: SocketAddr ) -> anyhow::Result<( Connection, UdpSocket )>{ 20 18 let tcp = TcpStream::connect(addr)?; 21 19 let udp = UdpSocket::bind("0.0.0.0:0")?; 22 20
+31 -20
client/src/setup.rs
··· 14 14 15 15 // TODO: UI and Menus 16 16 17 - commands.spawn(( 18 - Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), 19 - MeshMaterial3d(materials.add(Color::WHITE)), 20 - Transform::from_xyz(5.0, 0.0, 0.0), 21 - )); 17 + // commands.spawn(( 18 + // Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), 19 + // MeshMaterial3d(materials.add(Color::WHITE)), 20 + // Transform::from_xyz(5.0, 0.0, 0.0), 21 + // )); 22 22 23 - commands.spawn(( 24 - Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))), 25 - MeshMaterial3d(materials.add(Color::WHITE)), 26 - Transform::from_xyz(-5.0, 0.0, 0.0) 27 - )); 23 + // commands.spawn(( 24 + // Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))), 25 + // MeshMaterial3d(materials.add(Color::WHITE)), 26 + // Transform::from_xyz(-5.0, 0.0, 0.0) 27 + // )); 28 28 29 - commands.spawn(( 30 - Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))), 31 - MeshMaterial3d(materials.add(Color::WHITE)), 32 - Transform::from_xyz(0.0, 0.0, 5.0) 33 - )); 29 + // commands.spawn(( 30 + // Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))), 31 + // MeshMaterial3d(materials.add(Color::WHITE)), 32 + // Transform::from_xyz(0.0, 0.0, 5.0) 33 + // )); 34 34 35 - commands.spawn(( 36 - Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))), 37 - MeshMaterial3d(materials.add(Color::WHITE)), 38 - Transform::from_xyz(0.0, 0.0, -5.0) 39 - )); 35 + // commands.spawn(( 36 + // Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))), 37 + // MeshMaterial3d(materials.add(Color::WHITE)), 38 + // Transform::from_xyz(0.0, 0.0, -5.0) 39 + // )); 40 40 41 41 commands.spawn(( 42 42 DebugCamera::default(), // TODO: Build a proper player controller ··· 44 44 Transform::from_xyz(0.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), 45 45 FelixAudioListener 46 46 )); 47 + 48 + let gos = felix_loader::load("bundle").unwrap(); 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 + } 57 + } 47 58 }
+11
loader/Cargo.toml
··· 1 + [package] 2 + name = "felix-loader" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.100" 8 + unity-asset = "0.2.0" 9 + unity-asset-binary = "0.2.0" 10 + unity-asset-decode = { version = "0.2.0", features = [ "mesh" ] } 11 + bevy = "0.17.3"
+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 + }
+22
loader/src/unity/component/mesh_renderer.rs
··· 1 + use unity_asset_binary::object::UnityObject; 2 + 3 + use crate::unity::component::Component; 4 + 5 + #[derive(Debug)] 6 + pub struct MeshRenderer{ 7 + 8 + } 9 + 10 + impl Component for MeshRenderer{ 11 + 12 + } 13 + 14 + impl From<UnityObject> for MeshRenderer{ 15 + fn from(value: UnityObject) -> Self { 16 + // dbg!(value.property_names()); 17 + 18 + Self { 19 + 20 + } 21 + } 22 + }
+6
loader/src/unity/component/mod.rs
··· 1 + pub mod transform; 2 + pub mod mesh_renderer; 3 + 4 + pub trait Component{ 5 + 6 + }
+16
loader/src/unity/component/transform.rs
··· 1 + use bevy::transform::components::Transform; 2 + use unity_asset_binary::object::UnityObject; 3 + 4 + use crate::util::unity_value::{unity_value_to_quat, unity_value_to_vec3}; 5 + 6 + pub fn into_bevy_transform(value: UnityObject) -> Transform { 7 + let position = value.get("m_LocalPosition").unwrap(); 8 + let rotation = value.get("m_LocalRotation").unwrap(); 9 + let scale = value.get("m_LocalScale").unwrap(); 10 + 11 + Transform { 12 + translation: unity_value_to_vec3(position), 13 + rotation: unity_value_to_quat(rotation), 14 + scale: unity_value_to_vec3(scale) 15 + } 16 + }
+94
loader/src/unity/mod.rs
··· 1 + use anyhow::bail; 2 + use bevy::{prelude::*, transform::components::Transform}; 3 + use unity_asset::environment::{Environment, EnvironmentObjectRef}; 4 + use unity_asset_binary::unity_version::UnityVersion; 5 + use unity_asset_decode::mesh::MeshParser; 6 + 7 + use crate::{unity::component::{Component, mesh_renderer::MeshRenderer, transform::into_bevy_transform}, util}; 8 + 9 + mod component; 10 + 11 + #[derive(Debug)] 12 + pub struct GameObject{ 13 + pub name: String, 14 + pub active: bool, 15 + 16 + pub transform: Transform, 17 + pub mesh_renderer: Option<MeshRenderer>, 18 + pub mesh_filter: Option<Mesh>, 19 + } 20 + 21 + pub fn load( path: &'static str ) -> anyhow::Result<Vec<GameObject>>{ 22 + let mesh_parser = MeshParser::new(UnityVersion::parse_version("2020.3.12f1").unwrap()); 23 + 24 + let mut env = Environment::new(); 25 + env.load(path)?; 26 + 27 + let mut gameobjects = vec![]; 28 + 29 + for obj in env.objects(){ 30 + match obj{ 31 + EnvironmentObjectRef::Binary(v) => { 32 + if v.object.class_id() == 1{ 33 + let obj = v.object.read()?; 34 + let go = obj.as_gameobject().unwrap(); 35 + 36 + let mut components: Vec<Box<dyn Component>> = vec![]; 37 + 38 + let mut transform = None; 39 + let mut mesh_renderer = None; 40 + let mut mesh_filter = None; 41 + 42 + let comps = obj.get("m_Component").unwrap().as_array().unwrap(); 43 + 44 + for comp in comps{ 45 + let obj = comp.as_object().unwrap().get("component").unwrap(); 46 + let path_id = obj.as_object().unwrap().get("m_PathID").unwrap().as_i64().unwrap(); 47 + 48 + let comp = env.find_binary_object(path_id).unwrap(); 49 + let comp = comp.read().unwrap(); 50 + 51 + match comp.class_id(){ 52 + 4 => transform = Some(into_bevy_transform(comp)), // Normal Transform 53 + 224 => transform = Some(into_bevy_transform(comp)), // Rect Transform (gonna just treat it as normal) 54 + 55 + 23 => mesh_renderer = Some(MeshRenderer::from(comp)), 56 + 33 => { 57 + let mesh = comp.get("m_Mesh").unwrap(); 58 + let path_id = mesh.as_object().unwrap().get("m_PathID").unwrap().as_i64().unwrap(); 59 + 60 + let mesh = env.find_binary_object(path_id).unwrap(); 61 + let mesh_obj = mesh.read().unwrap(); 62 + 63 + if let Some(mesh) = util::unity_value::try_unity_mesh_to_bevy(mesh_obj, &mesh_parser){ 64 + mesh_filter = Some(mesh); 65 + } else{ 66 + println!("[WARN] Failed to load mesh for: {}", go.name); 67 + } 68 + }, 69 + 70 + _ => {} 71 + } 72 + } 73 + 74 + if transform.is_none(){ bail!("Cannot find transform for object {}", go.name); } 75 + 76 + let gameobject = GameObject { 77 + name: go.name, 78 + active: go.active, 79 + 80 + transform: transform.unwrap(), 81 + mesh_renderer, 82 + mesh_filter 83 + }; 84 + 85 + gameobjects.push(gameobject); 86 + // break; 87 + } 88 + }, 89 + _ => {} 90 + } 91 + } 92 + 93 + Ok(gameobjects) 94 + }
+1
loader/src/util/mod.rs
··· 1 + pub mod unity_value;
+455
loader/src/util/unity_value.rs
··· 1 + use std::{fs::File, io::Write}; 2 + 3 + use bevy::{asset::RenderAssetUsages, math::{Quat, Vec3}, mesh::{Indices, PrimitiveTopology}, prelude::*}; 4 + use unity_asset::UnityValue; 5 + use unity_asset_binary::object::UnityObject; 6 + use unity_asset_decode::mesh::MeshParser; 7 + 8 + pub fn unity_value_to_vec3( val: &UnityValue ) -> Vec3{ 9 + let obj = val.as_object().unwrap(); 10 + 11 + Vec3 { 12 + x: obj.get("x").unwrap().as_f64().unwrap() as f32, 13 + y: obj.get("y").unwrap().as_f64().unwrap() as f32, 14 + z: obj.get("z").unwrap().as_f64().unwrap() as f32 15 + } 16 + } 17 + 18 + pub fn unity_value_to_quat( val: &UnityValue ) -> Quat{ 19 + let obj = val.as_object().unwrap(); 20 + 21 + Quat::from_xyzw( 22 + obj.get("x").unwrap().as_f64().unwrap() as f32, 23 + obj.get("y").unwrap().as_f64().unwrap() as f32, 24 + obj.get("z").unwrap().as_f64().unwrap() as f32, 25 + obj.get("w").unwrap().as_f64().unwrap() as f32 26 + ) 27 + } 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 + fn get_channel_component_size( format: u8 ) -> usize{ 49 + match format{ 50 + 0 => 4, // kVertexFormatFloat 51 + // 1 => 2, // kVertexFormatFloat16 52 + 2 => 1, // kVertexFormatUNorm8 53 + 3 => 1, // kVertexFormatSNorm8 54 + 4 => 2, // kVertexFormatUNorm16 55 + 5 => 2, // kVertexFormatSNorm16 56 + 6 => 1, // kVertexFormatUInt8 57 + 7 => 1, // kVertexFormatSInt8 58 + 8 => 2, // kVertexFormatUInt16 59 + 9 => 2, // kVertexFormatSInt16 60 + 10 => 4, // kVertexFormatUInt32 61 + 11 => 4, // kVertexFormatSInt32 62 + 63 + _ => 0 64 + } 65 + } 66 + 67 + enum UnityDataTypeArray{ 68 + kVertexFormatFloat(Vec<Vec<f32>>), 69 + // kVertexFormatFloat16(Vec<Vec<f16>>), 70 + kVertexFormatUNorm8(Vec<Vec<u8>>), 71 + kVertexFormatSNorm8(Vec<Vec<i8>>), 72 + kVertexFormatUNorm16(Vec<Vec<u16>>), 73 + kVertexFormatSNorm16(Vec<Vec<i16>>), 74 + kVertexFormatUInt8(Vec<Vec<u8>>), 75 + kVertexFormatSInt8(Vec<Vec<i8>>), 76 + kVertexFormatUInt16(Vec<Vec<u16>>), 77 + kVertexFormatSInt16(Vec<Vec<i16>>), 78 + kVertexFormatUInt32(Vec<Vec<u32>>), 79 + kVertexFormatSInt32(Vec<Vec<i32>>), 80 + Unknown 81 + } 82 + 83 + impl UnityDataTypeArray{ 84 + pub fn as_vec3_array( &self ) -> Vec<Vec3>{ 85 + if let Self::kVertexFormatFloat(array) = self{ 86 + let mut vecs = vec![]; 87 + 88 + for val in array{ 89 + vecs.push(Vec3 { x: val[0], y: val[1], z: val[2] }); 90 + } 91 + 92 + vecs 93 + } else{ 94 + panic!("Not a float array"); 95 + } 96 + } 97 + } 98 + 99 + fn unpack_struct( dimension: usize, component_dtype: u8, bytes: Vec<u8> ) -> UnityDataTypeArray{ 100 + match component_dtype{ 101 + 0 => { 102 + let mut decoded: Vec<Vec<_>> = vec![]; 103 + let item_size = get_channel_component_size(component_dtype); 104 + let vec_size = item_size * dimension; 105 + 106 + for i in 0..( bytes.len() / vec_size ){ 107 + let indx = i * vec_size; 108 + let mut vec = vec![0.0; dimension]; 109 + 110 + for j in 0..dimension{ 111 + vec[j] = f32::from_le_bytes([ 112 + bytes[indx + (j * item_size)], 113 + bytes[indx + (j * item_size) + 1], 114 + bytes[indx + (j * item_size) + 2], 115 + bytes[indx + (j * item_size) + 3] 116 + ]); 117 + } 118 + decoded.push(vec); 119 + } 120 + 121 + UnityDataTypeArray::kVertexFormatFloat(decoded) 122 + }, 123 + 124 + 2 => { 125 + let mut decoded: Vec<Vec<u8>> = vec![]; 126 + let item_size = get_channel_component_size(component_dtype); 127 + let vec_size = item_size * dimension; 128 + 129 + for i in 0..( bytes.len() / vec_size ){ 130 + let indx = i * vec_size; 131 + let mut vec = vec![0; vec_size]; 132 + 133 + for j in 0..vec_size{ vec[j] = u8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 134 + decoded.push(vec); 135 + } 136 + 137 + UnityDataTypeArray::kVertexFormatUNorm8(decoded) 138 + }, 139 + 140 + 3 => { 141 + let mut decoded: Vec<Vec<_>> = vec![]; 142 + let item_size = get_channel_component_size(component_dtype); 143 + let vec_size = item_size * dimension; 144 + 145 + for i in 0..( bytes.len() / vec_size ){ 146 + let indx = i * vec_size; 147 + let mut vec = vec![0; vec_size]; 148 + 149 + for j in 0..vec_size{ vec[j] = i8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 150 + decoded.push(vec); 151 + } 152 + 153 + UnityDataTypeArray::kVertexFormatSNorm8(decoded) 154 + }, 155 + 156 + 4 => { 157 + let mut decoded: Vec<Vec<_>> = vec![]; 158 + let item_size = get_channel_component_size(component_dtype); 159 + let vec_size = item_size * dimension; 160 + 161 + for i in 0..( bytes.len() / vec_size ){ 162 + let indx = i * vec_size; 163 + let mut vec = vec![0; vec_size]; 164 + 165 + for j in 0..vec_size{ 166 + vec[j] = u16::from_le_bytes([ 167 + bytes[indx + (j * item_size)], 168 + bytes[indx + (j * item_size) + 1] 169 + ]); 170 + } 171 + decoded.push(vec); 172 + } 173 + 174 + UnityDataTypeArray::kVertexFormatUNorm16(decoded) 175 + }, 176 + 177 + 5 => { 178 + let mut decoded: Vec<Vec<_>> = vec![]; 179 + let item_size = get_channel_component_size(component_dtype); 180 + let vec_size = item_size * dimension; 181 + 182 + for i in 0..( bytes.len() / vec_size ){ 183 + let indx = i * vec_size; 184 + let mut vec = vec![0; vec_size]; 185 + 186 + for j in 0..vec_size{ 187 + vec[j] = i16::from_le_bytes([ 188 + bytes[indx + (j * item_size)], 189 + bytes[indx + (j * item_size) + 1] 190 + ]); 191 + } 192 + decoded.push(vec); 193 + } 194 + 195 + UnityDataTypeArray::kVertexFormatSNorm16(decoded) 196 + }, 197 + 198 + 6 => { 199 + let mut decoded: Vec<Vec<u8>> = vec![]; 200 + let item_size = get_channel_component_size(component_dtype); 201 + let vec_size = item_size * dimension; 202 + 203 + for i in 0..( bytes.len() / vec_size ){ 204 + let indx = i * vec_size; 205 + let mut vec = vec![0; vec_size]; 206 + 207 + for j in 0..vec_size{ vec[j] = u8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 208 + decoded.push(vec); 209 + } 210 + 211 + UnityDataTypeArray::kVertexFormatUInt8(decoded) 212 + }, 213 + 214 + 7 => { 215 + let mut decoded: Vec<Vec<_>> = vec![]; 216 + let item_size = get_channel_component_size(component_dtype); 217 + let vec_size = item_size * dimension; 218 + 219 + for i in 0..( bytes.len() / vec_size ){ 220 + let indx = i * vec_size; 221 + let mut vec = vec![0; vec_size]; 222 + 223 + for j in 0..vec_size{ vec[j] = i8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 224 + decoded.push(vec); 225 + } 226 + 227 + UnityDataTypeArray::kVertexFormatSInt8(decoded) 228 + }, 229 + 230 + 8 => { 231 + let mut decoded: Vec<Vec<_>> = vec![]; 232 + let item_size = get_channel_component_size(component_dtype); 233 + let vec_size = item_size * dimension; 234 + 235 + for i in 0..( bytes.len() / vec_size ){ 236 + let indx = i * vec_size; 237 + let mut vec = vec![0; vec_size]; 238 + 239 + for j in 0..vec_size{ 240 + vec[j] = u16::from_le_bytes([ 241 + bytes[indx + (j * item_size)], 242 + bytes[indx + (j * item_size) + 1] 243 + ]); 244 + } 245 + decoded.push(vec); 246 + } 247 + 248 + UnityDataTypeArray::kVertexFormatUInt16(decoded) 249 + }, 250 + 251 + 9 => { 252 + let mut decoded: Vec<Vec<_>> = vec![]; 253 + let item_size = get_channel_component_size(component_dtype); 254 + let vec_size = item_size * dimension; 255 + 256 + for i in 0..( bytes.len() / vec_size ){ 257 + let indx = i * vec_size; 258 + let mut vec = vec![0; vec_size]; 259 + 260 + for j in 0..vec_size{ 261 + vec[j] = i16::from_le_bytes([ 262 + bytes[indx + (j * item_size)], 263 + bytes[indx + (j * item_size) + 1] 264 + ]); 265 + } 266 + decoded.push(vec); 267 + } 268 + 269 + UnityDataTypeArray::kVertexFormatSInt16(decoded) 270 + }, 271 + 272 + 10 => { 273 + let mut decoded: Vec<Vec<_>> = vec![]; 274 + let item_size = get_channel_component_size(component_dtype); 275 + let vec_size = item_size * dimension; 276 + 277 + for i in 0..( bytes.len() / vec_size ){ 278 + let indx = i * vec_size; 279 + let mut vec = vec![0; vec_size]; 280 + 281 + for j in 0..vec_size{ 282 + vec[j] = u32::from_le_bytes([ 283 + bytes[indx + (j * item_size)], 284 + bytes[indx + (j * item_size) + 1], 285 + bytes[indx + (j * item_size) + 2], 286 + bytes[indx + (j * item_size) + 3] 287 + ]); 288 + } 289 + decoded.push(vec); 290 + } 291 + 292 + UnityDataTypeArray::kVertexFormatUInt32(decoded) 293 + }, 294 + 295 + 11 => { 296 + let mut decoded: Vec<Vec<_>> = vec![]; 297 + let item_size = get_channel_component_size(component_dtype); 298 + let vec_size = item_size * dimension; 299 + 300 + for i in 0..( bytes.len() / vec_size ){ 301 + let indx = i * vec_size; 302 + let mut vec = vec![0; vec_size]; 303 + 304 + for j in 0..vec_size{ 305 + vec[j] = i32::from_le_bytes([ 306 + bytes[indx + (j * item_size)], 307 + bytes[indx + (j * item_size) + 1], 308 + bytes[indx + (j * item_size) + 2], 309 + bytes[indx + (j * item_size) + 3] 310 + ]); 311 + } 312 + decoded.push(vec); 313 + } 314 + 315 + UnityDataTypeArray::kVertexFormatSInt32(decoded) 316 + }, 317 + 318 + _ => { UnityDataTypeArray::Unknown } 319 + } 320 + } 321 + 322 + struct StreamInfo{ 323 + channel_mask: usize, 324 + offset: usize, 325 + stride: usize 326 + } 327 + 328 + pub fn try_unity_mesh_to_bevy( mesh_obj: UnityObject, mesh_parser: &MeshParser ) -> Option<Mesh>{ 329 + let mut vertices = None; 330 + let mut normals = None; 331 + let mut tangents = None; 332 + let mut uv0 = None; 333 + 334 + let mesh = mesh_parser.parse_from_unity_object(&mesh_obj).unwrap().mesh; 335 + let vertex_data = mesh.vertex_data; 336 + 337 + let mut streams = vec![]; 338 + let stream_count = 1 + vertex_data.channels.iter().map(|x| x.stream).max()?; 339 + 340 + let mut offset = 0; 341 + 342 + for s in 0..stream_count{ 343 + let mut chn_mask = 0; 344 + let mut stride = 0; 345 + 346 + let mut i = 0; 347 + for channel in &vertex_data.channels{ 348 + if channel.stream == s && channel.dimension > 0{ 349 + chn_mask |= 1 << i; 350 + 351 + let component_size = get_channel_component_size(channel.format); 352 + stride += (channel.dimension & 0xF) as usize * component_size; 353 + } 354 + 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 + i += 1; 365 + } 366 + } 367 + 368 + let mut i = 0; 369 + for channel in vertex_data.channels{ 370 + let stream = &streams[channel.stream as usize]; 371 + 372 + if stream.channel_mask >> i & 1 == 1{ 373 + let component_byte_size = get_channel_component_size(channel.format); 374 + let channel_dimension = (channel.dimension & 0xF) as usize; 375 + 376 + let mut bytes = 377 + vec![0; vertex_data.vertex_count as usize * channel_dimension * component_byte_size]; 378 + 379 + dbg!(vertex_data.vertex_count); 380 + 381 + let vertex_base_offset = stream.offset + channel.offset as usize; 382 + for v in 0..vertex_data.vertex_count{ 383 + let vertex_offset = vertex_base_offset + stream.stride * v as usize; 384 + 385 + 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 ); 388 + 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); 391 + } 392 + } 393 + 394 + let component_data = unpack_struct(channel_dimension, channel.format, bytes); 395 + 396 + match i{ 397 + 0 => vertices = Some(component_data), 398 + 1 => normals = Some(component_data), 399 + 2 => tangents = Some(component_data), 400 + 4 => uv0 = Some(component_data), 401 + 402 + _ => {} 403 + } 404 + } 405 + 406 + i += 1; 407 + } 408 + 409 + let mut triangles = vec![]; 410 + if mesh.index_buffer.len() > 0{ 411 + 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 + ])); 432 + } 433 + } 434 + } else{ 435 + return None; 436 + } 437 + 438 + if vertices.is_none(){ panic!("Cannot parse vertices") } 439 + let vertices = vertices.unwrap().as_vec3_array(); 440 + 441 + let mut file = File::create("test.obj").unwrap(); 442 + 443 + for vec in &vertices{ 444 + writeln!(file, "v {} {} {}", vec.x, vec.y, vec.z).unwrap(); 445 + } 446 + 447 + for i in 0..(triangles.len() / 3){ 448 + let indx = i * 3; 449 + writeln!(file, "f {} {} {}", triangles[indx], triangles[indx + 1], triangles[indx + 2]).unwrap(); 450 + } 451 + 452 + Some(Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD) 453 + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) 454 + .with_inserted_indices(Indices::U16(triangles))) 455 + }