A multiplayer VR framework w/voice chat

Compare changes

Choose any two refs to compare.

+3 -1
.gitignore
··· 1 1 target/ 2 - .env 2 + .env 3 + *.assetbundle 4 + *.flx
+3
.vscode/settings.json
··· 1 + { 2 + "python.analysis.autoImportCompletions": true 3 + }
+330 -4
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" ··· 135 150 "bitflags 2.10.0", 136 151 "cc", 137 152 "cesu8", 138 - "jni", 153 + "jni 0.21.1", 139 154 "jni-sys", 140 155 "libc", 141 156 "log", ··· 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" ··· 987 1008 checksum = "7ef8e4b7e61dfe7719bb03c884dc270cd46a82efb40f93e9933b990c5c190c59" 988 1009 989 1010 [[package]] 1011 + name = "bevy_mod_openxr" 1012 + version = "0.4.0" 1013 + source = "registry+https://github.com/rust-lang/crates.io-index" 1014 + checksum = "1ead3d12dc1c364bc8f5d063c8cd82df23a3d6261807abecfbbca70ca3cb2c15" 1015 + dependencies = [ 1016 + "android_system_properties", 1017 + "ash", 1018 + "bevy_app", 1019 + "bevy_camera", 1020 + "bevy_derive", 1021 + "bevy_ecs", 1022 + "bevy_log", 1023 + "bevy_math", 1024 + "bevy_mod_xr", 1025 + "bevy_platform", 1026 + "bevy_render", 1027 + "bevy_transform", 1028 + "jni 0.20.0", 1029 + "ndk-context", 1030 + "openxr", 1031 + "thiserror 2.0.17", 1032 + "wgpu", 1033 + "wgpu-hal", 1034 + ] 1035 + 1036 + [[package]] 1037 + name = "bevy_mod_xr" 1038 + version = "0.4.0" 1039 + source = "registry+https://github.com/rust-lang/crates.io-index" 1040 + checksum = "4107b79e3e3b38bc57a7c3fceb465300070b4bc3d2ebd839a9e04468b06cc11c" 1041 + dependencies = [ 1042 + "bevy_app", 1043 + "bevy_camera", 1044 + "bevy_color", 1045 + "bevy_derive", 1046 + "bevy_ecs", 1047 + "bevy_gizmos", 1048 + "bevy_log", 1049 + "bevy_math", 1050 + "bevy_render", 1051 + "bevy_transform", 1052 + ] 1053 + 1054 + [[package]] 990 1055 name = "bevy_pbr" 991 1056 version = "0.17.3" 992 1057 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1553 1618 ] 1554 1619 1555 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]] 1556 1645 name = "bit-set" 1557 1646 version = "0.8.0" 1558 1647 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1631 1720 "futures-io", 1632 1721 "futures-lite", 1633 1722 "piper", 1723 + ] 1724 + 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", 1634 1744 ] 1635 1745 1636 1746 [[package]] ··· 1976 2086 "core-foundation-sys", 1977 2087 "coreaudio-rs 0.11.3", 1978 2088 "dasp_sample", 1979 - "jni", 2089 + "jni 0.21.1", 1980 2090 "js-sys", 1981 2091 "libc", 1982 2092 "mach2", ··· 1998 2108 "alsa", 1999 2109 "coreaudio-rs 0.13.0", 2000 2110 "dasp_sample", 2001 - "jni", 2111 + "jni 0.21.1", 2002 2112 "js-sys", 2003 2113 "libc", 2004 2114 "mach2", ··· 2016 2126 ] 2017 2127 2018 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]] 2019 2144 name = "crc32fast" 2020 2145 version = "1.5.0" 2021 2146 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2304 2429 ] 2305 2430 2306 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 + 2443 + [[package]] 2307 2444 name = "felix-audio" 2308 2445 version = "0.1.0" 2309 2446 dependencies = [ ··· 2322 2459 dependencies = [ 2323 2460 "anyhow", 2324 2461 "bevy", 2462 + "bevy_mod_openxr", 2463 + "bevy_mod_xr", 2325 2464 "cpal 0.16.0", 2326 2465 "dotenvy", 2466 + "felix-assets", 2327 2467 "felix-audio", 2328 2468 "felix-net", 2469 + "openxr", 2329 2470 "tokio", 2330 2471 ] 2331 2472 ··· 2882 3023 2883 3024 [[package]] 2884 3025 name = "jni" 3026 + version = "0.20.0" 3027 + source = "registry+https://github.com/rust-lang/crates.io-index" 3028 + checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" 3029 + dependencies = [ 3030 + "cesu8", 3031 + "combine", 3032 + "jni-sys", 3033 + "log", 3034 + "thiserror 1.0.69", 3035 + "walkdir", 3036 + ] 3037 + 3038 + [[package]] 3039 + name = "jni" 2885 3040 version = "0.21.1" 2886 3041 source = "registry+https://github.com/rust-lang/crates.io-index" 2887 3042 checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" ··· 3069 3224 checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 3070 3225 3071 3226 [[package]] 3227 + name = "lz4_flex" 3228 + version = "0.12.0" 3229 + source = "registry+https://github.com/rust-lang/crates.io-index" 3230 + checksum = "ab6473172471198271ff72e9379150e9dfd70d8e533e0752a27e515b48dd375e" 3231 + dependencies = [ 3232 + "twox-hash", 3233 + ] 3234 + 3235 + [[package]] 3236 + name = "lzma-rs" 3237 + version = "0.3.0" 3238 + source = "registry+https://github.com/rust-lang/crates.io-index" 3239 + checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" 3240 + dependencies = [ 3241 + "byteorder", 3242 + "crc", 3243 + ] 3244 + 3245 + [[package]] 3072 3246 name = "mach2" 3073 3247 version = "0.4.3" 3074 3248 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3342 3516 ] 3343 3517 3344 3518 [[package]] 3519 + name = "num_cpus" 3520 + version = "1.17.0" 3521 + source = "registry+https://github.com/rust-lang/crates.io-index" 3522 + checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" 3523 + dependencies = [ 3524 + "hermit-abi", 3525 + "libc", 3526 + ] 3527 + 3528 + [[package]] 3345 3529 name = "num_enum" 3346 3530 version = "0.7.5" 3347 3531 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3657 3841 source = "registry+https://github.com/rust-lang/crates.io-index" 3658 3842 checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" 3659 3843 dependencies = [ 3660 - "jni", 3844 + "jni 0.21.1", 3661 3845 "ndk 0.8.0", 3662 3846 "ndk-context", 3663 3847 "num-derive", ··· 3700 3884 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 3701 3885 3702 3886 [[package]] 3887 + name = "openxr" 3888 + version = "0.19.0" 3889 + source = "registry+https://github.com/rust-lang/crates.io-index" 3890 + checksum = "2a2d6934d2508f94fd4cbda6c2a326f111f60ce59fd9136df6d478564397dd40" 3891 + dependencies = [ 3892 + "libc", 3893 + "libloading", 3894 + "ndk-context", 3895 + "openxr-sys", 3896 + ] 3897 + 3898 + [[package]] 3899 + name = "openxr-sys" 3900 + version = "0.11.0" 3901 + source = "registry+https://github.com/rust-lang/crates.io-index" 3902 + checksum = "f10e7e38c47f2175fc39363713b656db899fa0b4a14341029702cbdfa6f44d05" 3903 + dependencies = [ 3904 + "cmake", 3905 + "libc", 3906 + "mint", 3907 + ] 3908 + 3909 + [[package]] 3703 3910 name = "opus" 3704 3911 version = "0.3.0" 3705 3912 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3735 3942 dependencies = [ 3736 3943 "ttf-parser 0.25.1", 3737 3944 ] 3945 + 3946 + [[package]] 3947 + name = "owo-colors" 3948 + version = "4.2.3" 3949 + source = "registry+https://github.com/rust-lang/crates.io-index" 3950 + checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 3738 3951 3739 3952 [[package]] 3740 3953 name = "parking" ··· 4316 4529 ] 4317 4530 4318 4531 [[package]] 4532 + name = "serde_bytes" 4533 + version = "0.11.19" 4534 + source = "registry+https://github.com/rust-lang/crates.io-index" 4535 + checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" 4536 + dependencies = [ 4537 + "serde", 4538 + "serde_core", 4539 + ] 4540 + 4541 + [[package]] 4319 4542 name = "serde_core" 4320 4543 version = "1.0.228" 4321 4544 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4346 4569 "ryu", 4347 4570 "serde", 4348 4571 "serde_core", 4572 + ] 4573 + 4574 + [[package]] 4575 + name = "serde_yaml" 4576 + version = "0.9.34+deprecated" 4577 + source = "registry+https://github.com/rust-lang/crates.io-index" 4578 + checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 4579 + dependencies = [ 4580 + "indexmap", 4581 + "itoa", 4582 + "ryu", 4583 + "serde", 4584 + "unsafe-libyaml", 4349 4585 ] 4350 4586 4351 4587 [[package]] ··· 5028 5264 checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 5029 5265 5030 5266 [[package]] 5267 + name = "unity-asset" 5268 + version = "0.2.0" 5269 + source = "registry+https://github.com/rust-lang/crates.io-index" 5270 + checksum = "6705b95fda449527055ff77fe5cff49f774dde7335ed92bc3d048fc74066ebaf" 5271 + dependencies = [ 5272 + "unity-asset-binary", 5273 + "unity-asset-core", 5274 + "unity-asset-yaml", 5275 + ] 5276 + 5277 + [[package]] 5278 + name = "unity-asset-binary" 5279 + version = "0.2.0" 5280 + source = "registry+https://github.com/rust-lang/crates.io-index" 5281 + checksum = "d739d1414c1732be1035417f83b1e60f234c7d5eb8abda0ee70d178a58137cb7" 5282 + dependencies = [ 5283 + "binrw", 5284 + "brotli", 5285 + "byteorder", 5286 + "flate2", 5287 + "indexmap", 5288 + "lz4_flex", 5289 + "lzma-rs", 5290 + "memmap2", 5291 + "num_cpus", 5292 + "once_cell", 5293 + "regex", 5294 + "serde", 5295 + "serde_json", 5296 + "thiserror 2.0.17", 5297 + "unity-asset-core", 5298 + ] 5299 + 5300 + [[package]] 5301 + name = "unity-asset-core" 5302 + version = "0.2.0" 5303 + source = "registry+https://github.com/rust-lang/crates.io-index" 5304 + checksum = "12903fd47e122b5e7e335fc9cb5540bb35ab0ffe5bb57a7741a8c7be8d361361" 5305 + dependencies = [ 5306 + "indexmap", 5307 + "lazy_static", 5308 + "serde", 5309 + "serde_bytes", 5310 + "thiserror 2.0.17", 5311 + ] 5312 + 5313 + [[package]] 5314 + name = "unity-asset-decode" 5315 + version = "0.2.0" 5316 + source = "registry+https://github.com/rust-lang/crates.io-index" 5317 + checksum = "9ea6e630cdefde54a88ca4fbae54cf3d3e5ba527fac96b085562da3b3834b77b" 5318 + dependencies = [ 5319 + "indexmap", 5320 + "serde", 5321 + "unity-asset-binary", 5322 + "unity-asset-core", 5323 + ] 5324 + 5325 + [[package]] 5326 + name = "unity-asset-yaml" 5327 + version = "0.2.0" 5328 + source = "registry+https://github.com/rust-lang/crates.io-index" 5329 + checksum = "b991e340c27b1578e657defee9f57de867d853b8f40448fb079d0ee3a5ebc77e" 5330 + dependencies = [ 5331 + "indexmap", 5332 + "serde", 5333 + "serde_yaml", 5334 + "thiserror 2.0.17", 5335 + "unity-asset-core", 5336 + ] 5337 + 5338 + [[package]] 5339 + name = "unsafe-libyaml" 5340 + version = "0.2.11" 5341 + source = "registry+https://github.com/rust-lang/crates.io-index" 5342 + checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 5343 + 5344 + [[package]] 5031 5345 name = "uuid" 5032 5346 version = "1.19.0" 5033 5347 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5294 5608 "js-sys", 5295 5609 "log", 5296 5610 "naga", 5611 + "parking_lot", 5297 5612 "portable-atomic", 5298 5613 "profiling", 5299 5614 "raw-window-handle", 5300 5615 "smallvec", 5301 5616 "static_assertions", 5302 5617 "wasm-bindgen", 5618 + "wasm-bindgen-futures", 5303 5619 "web-sys", 5304 5620 "wgpu-core", 5305 5621 "wgpu-hal", ··· 5331 5647 "smallvec", 5332 5648 "thiserror 2.0.17", 5333 5649 "wgpu-core-deps-apple", 5650 + "wgpu-core-deps-emscripten", 5334 5651 "wgpu-core-deps-wasm", 5335 5652 "wgpu-core-deps-windows-linux-android", 5336 5653 "wgpu-hal", ··· 5342 5659 version = "26.0.0" 5343 5660 source = "registry+https://github.com/rust-lang/crates.io-index" 5344 5661 checksum = "18ae5fbde6a4cbebae38358aa73fcd6e0f15c6144b67ef5dc91ded0db125dbdf" 5662 + dependencies = [ 5663 + "wgpu-hal", 5664 + ] 5665 + 5666 + [[package]] 5667 + name = "wgpu-core-deps-emscripten" 5668 + version = "26.0.0" 5669 + source = "registry+https://github.com/rust-lang/crates.io-index" 5670 + checksum = "d7670e390f416006f746b4600fdd9136455e3627f5bd763abf9a65daa216dd2d" 5345 5671 dependencies = [ 5346 5672 "wgpu-hal", 5347 5673 ]
+4 -1
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = [ "audio", "client", "net", "server" ] 3 + members = [ "audio", "client", "assets", "net", "server" ] 4 + 5 + [profile.dev.package."*"] 6 + opt-level = 3
+12
assets/Cargo.toml
··· 1 + [package] 2 + name = "felix-assets" 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" 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 + }
+22
assets/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
assets/src/unity/component/mod.rs
··· 1 + pub mod transform; 2 + pub mod mesh_renderer; 3 + 4 + pub trait Component{ 5 + 6 + }
+16
assets/src/unity/component/transform.rs
··· 1 + use bevy::transform::components::Transform; 2 + use unity_asset_binary::object::UnityObject; 3 + 4 + use crate::unity::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 + }
+111
assets/src/unity/mod.rs
··· 1 + // NOTE: Very WIP, Does not work!! 2 + 3 + use anyhow::bail; 4 + use bevy::{prelude::*, transform::components::Transform}; 5 + use unity_asset::environment::{Environment, EnvironmentObjectRef}; 6 + use unity_asset_binary::unity_version::UnityVersion; 7 + use unity_asset_decode::mesh::MeshParser; 8 + 9 + use crate::{BevyObject, unity::{component::{Component, mesh_renderer::MeshRenderer, transform::into_bevy_transform}, unity_value::try_unity_mesh_to_bevy}}; 10 + 11 + mod component; 12 + mod unity_value; 13 + 14 + #[derive(Debug)] 15 + pub struct UnityGameObject{ 16 + pub name: String, 17 + pub active: bool, 18 + 19 + pub transform: Transform, 20 + pub mesh_renderer: Option<MeshRenderer>, 21 + pub mesh_filter: Option<Mesh>, 22 + } 23 + 24 + pub fn load( path: &'static str ) -> anyhow::Result<Vec<BevyObject>>{ 25 + let mesh_parser = MeshParser::new(UnityVersion::parse_version("2020.3.12f1").unwrap()); 26 + 27 + let mut env = Environment::new(); 28 + env.load(path)?; 29 + 30 + let mut gameobjects = vec![]; 31 + 32 + for obj in env.objects(){ 33 + match obj{ 34 + EnvironmentObjectRef::Binary(v) => { 35 + if v.object.class_id() == 1{ 36 + let obj = v.object.read()?; 37 + let go = obj.as_gameobject().unwrap(); 38 + 39 + let mut components: Vec<Box<dyn Component>> = vec![]; 40 + 41 + let mut transform = None; 42 + let mut mesh_renderer = None; 43 + let mut mesh_filter = None; 44 + 45 + let comps = obj.get("m_Component").unwrap().as_array().unwrap(); 46 + 47 + for comp in comps{ 48 + let obj = comp.as_object().unwrap().get("component").unwrap(); 49 + let path_id = obj.as_object().unwrap().get("m_PathID").unwrap().as_i64().unwrap(); 50 + 51 + let comp = env.find_binary_object(path_id).unwrap(); 52 + let comp = comp.read().unwrap(); 53 + 54 + match comp.class_id(){ 55 + 4 => transform = Some(into_bevy_transform(comp)), // Normal Transform 56 + 224 => transform = Some(into_bevy_transform(comp)), // Rect Transform (gonna just treat it as normal) 57 + 58 + 23 => mesh_renderer = Some(MeshRenderer::from(comp)), 59 + 33 => { 60 + let mesh = comp.get("m_Mesh").unwrap(); 61 + let path_id = mesh.as_object().unwrap().get("m_PathID").unwrap().as_i64().unwrap(); 62 + 63 + let mesh = env.find_binary_object(path_id).unwrap(); 64 + let mesh_obj = mesh.read().unwrap(); 65 + 66 + if let Some(mesh) = try_unity_mesh_to_bevy(mesh_obj, &mesh_parser){ 67 + mesh_filter = Some(mesh); 68 + } else{ 69 + println!("[WARN] Failed to load mesh for: {}", go.name); 70 + } 71 + }, 72 + 73 + _ => {} 74 + } 75 + } 76 + 77 + if transform.is_none(){ bail!("Cannot find transform for object {}", go.name); } 78 + 79 + let gameobject = UnityGameObject { 80 + name: go.name, 81 + active: go.active, 82 + 83 + transform: transform.unwrap(), 84 + mesh_renderer, 85 + mesh_filter 86 + }; 87 + 88 + gameobjects.push(gameobject); 89 + break; 90 + } 91 + }, 92 + _ => {} 93 + } 94 + } 95 + 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) 111 + }
+467
assets/src/unity/unity_value.rs
··· 1 + use std::{fs::File, io::Write, mem}; 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_component_size( format: u8 ) -> usize{ 30 + match format{ 31 + 0 => 4, // kVertexFormatFloat 32 + 1 => 2, // kVertexFormatFloat16 33 + 2 => 1, // kVertexFormatUNorm8 34 + 3 => 1, // kVertexFormatSNorm8 35 + 4 => 2, // kVertexFormatUNorm16 36 + 5 => 2, // kVertexFormatSNorm16 37 + 6 => 1, // kVertexFormatUInt8 38 + 7 => 1, // kVertexFormatSInt8 39 + 8 => 2, // kVertexFormatUInt16 40 + 9 => 2, // kVertexFormatSInt16 41 + 10 => 4, // kVertexFormatUInt32 42 + 11 => 4, // kVertexFormatSInt32 43 + 44 + _ => 0 45 + } 46 + } 47 + 48 + enum UnityDataTypeArray{ 49 + kVertexFormatFloat(Vec<Vec<f32>>), 50 + kVertexFormatFloat16(Vec<Vec<f32>>), 51 + kVertexFormatUNorm8(Vec<Vec<u8>>), 52 + kVertexFormatSNorm8(Vec<Vec<i8>>), 53 + kVertexFormatUNorm16(Vec<Vec<u16>>), 54 + kVertexFormatSNorm16(Vec<Vec<i16>>), 55 + kVertexFormatUInt8(Vec<Vec<u8>>), 56 + kVertexFormatSInt8(Vec<Vec<i8>>), 57 + kVertexFormatUInt16(Vec<Vec<u16>>), 58 + kVertexFormatSInt16(Vec<Vec<i16>>), 59 + kVertexFormatUInt32(Vec<Vec<u32>>), 60 + kVertexFormatSInt32(Vec<Vec<i32>>), 61 + Unknown 62 + } 63 + 64 + impl UnityDataTypeArray{ 65 + pub fn as_vec3_array( &self ) -> Vec<Vec3>{ 66 + if let Self::kVertexFormatFloat(array) = self{ 67 + let mut vecs = vec![]; 68 + 69 + for val in array{ 70 + vecs.push(Vec3 { x: val[0], y: val[1], z: val[2] }); 71 + } 72 + 73 + vecs 74 + } else{ 75 + panic!("Not a float array"); 76 + } 77 + } 78 + } 79 + 80 + fn unpack_struct( dimension: usize, component_dtype: u8, bytes: Vec<u8> ) -> UnityDataTypeArray{ 81 + match component_dtype{ 82 + 0 => { 83 + let mut decoded: Vec<Vec<_>> = vec![]; 84 + let item_size = get_channel_component_size(component_dtype); 85 + let vec_size = item_size * dimension; 86 + 87 + for i in 0..( bytes.len() / vec_size ){ 88 + let indx = i * vec_size; 89 + let mut vec = vec![0.0; dimension]; 90 + 91 + for j in 0..dimension{ 92 + vec[j] = f32::from_le_bytes([ 93 + bytes[indx + (j * item_size)], 94 + bytes[indx + (j * item_size) + 1], 95 + bytes[indx + (j * item_size) + 2], 96 + bytes[indx + (j * item_size) + 3] 97 + ]); 98 + } 99 + decoded.push(vec); 100 + } 101 + 102 + UnityDataTypeArray::kVertexFormatFloat(decoded) 103 + }, 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 + 127 + 2 => { 128 + let mut decoded: Vec<Vec<u8>> = vec![]; 129 + let item_size = get_channel_component_size(component_dtype); 130 + let vec_size = item_size * dimension; 131 + 132 + for i in 0..( bytes.len() / vec_size ){ 133 + let indx = i * vec_size; 134 + let mut vec = vec![0; vec_size]; 135 + 136 + for j in 0..vec_size{ vec[j] = u8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 137 + decoded.push(vec); 138 + } 139 + 140 + UnityDataTypeArray::kVertexFormatUNorm8(decoded) 141 + }, 142 + 143 + 3 => { 144 + let mut decoded: Vec<Vec<_>> = vec![]; 145 + let item_size = get_channel_component_size(component_dtype); 146 + let vec_size = item_size * dimension; 147 + 148 + for i in 0..( bytes.len() / vec_size ){ 149 + let indx = i * vec_size; 150 + let mut vec = vec![0; vec_size]; 151 + 152 + for j in 0..vec_size{ vec[j] = i8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 153 + decoded.push(vec); 154 + } 155 + 156 + UnityDataTypeArray::kVertexFormatSNorm8(decoded) 157 + }, 158 + 159 + 4 => { 160 + let mut decoded: Vec<Vec<_>> = vec![]; 161 + let item_size = get_channel_component_size(component_dtype); 162 + let vec_size = item_size * dimension; 163 + 164 + for i in 0..( bytes.len() / vec_size ){ 165 + let indx = i * vec_size; 166 + let mut vec = vec![0; vec_size]; 167 + 168 + for j in 0..vec_size{ 169 + vec[j] = u16::from_le_bytes([ 170 + bytes[indx + (j * item_size)], 171 + bytes[indx + (j * item_size) + 1] 172 + ]); 173 + } 174 + decoded.push(vec); 175 + } 176 + 177 + UnityDataTypeArray::kVertexFormatUNorm16(decoded) 178 + }, 179 + 180 + 5 => { 181 + let mut decoded: Vec<Vec<_>> = vec![]; 182 + let item_size = get_channel_component_size(component_dtype); 183 + let vec_size = item_size * dimension; 184 + 185 + for i in 0..( bytes.len() / vec_size ){ 186 + let indx = i * vec_size; 187 + let mut vec = vec![0; vec_size]; 188 + 189 + for j in 0..vec_size{ 190 + vec[j] = i16::from_le_bytes([ 191 + bytes[indx + (j * item_size)], 192 + bytes[indx + (j * item_size) + 1] 193 + ]); 194 + } 195 + decoded.push(vec); 196 + } 197 + 198 + UnityDataTypeArray::kVertexFormatSNorm16(decoded) 199 + }, 200 + 201 + 6 => { 202 + let mut decoded: Vec<Vec<u8>> = vec![]; 203 + let item_size = get_channel_component_size(component_dtype); 204 + let vec_size = item_size * dimension; 205 + 206 + for i in 0..( bytes.len() / vec_size ){ 207 + let indx = i * vec_size; 208 + let mut vec = vec![0; vec_size]; 209 + 210 + for j in 0..vec_size{ vec[j] = u8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 211 + decoded.push(vec); 212 + } 213 + 214 + UnityDataTypeArray::kVertexFormatUInt8(decoded) 215 + }, 216 + 217 + 7 => { 218 + let mut decoded: Vec<Vec<_>> = vec![]; 219 + let item_size = get_channel_component_size(component_dtype); 220 + let vec_size = item_size * dimension; 221 + 222 + for i in 0..( bytes.len() / vec_size ){ 223 + let indx = i * vec_size; 224 + let mut vec = vec![0; vec_size]; 225 + 226 + for j in 0..vec_size{ vec[j] = i8::from_le_bytes([ bytes[indx + (j * item_size)] ]); } 227 + decoded.push(vec); 228 + } 229 + 230 + UnityDataTypeArray::kVertexFormatSInt8(decoded) 231 + }, 232 + 233 + 8 => { 234 + let mut decoded: Vec<Vec<_>> = vec![]; 235 + let item_size = get_channel_component_size(component_dtype); 236 + let vec_size = item_size * dimension; 237 + 238 + for i in 0..( bytes.len() / vec_size ){ 239 + let indx = i * vec_size; 240 + let mut vec = vec![0; vec_size]; 241 + 242 + for j in 0..vec_size{ 243 + vec[j] = u16::from_le_bytes([ 244 + bytes[indx + (j * item_size)], 245 + bytes[indx + (j * item_size) + 1] 246 + ]); 247 + } 248 + decoded.push(vec); 249 + } 250 + 251 + UnityDataTypeArray::kVertexFormatUInt16(decoded) 252 + }, 253 + 254 + 9 => { 255 + let mut decoded: Vec<Vec<_>> = vec![]; 256 + let item_size = get_channel_component_size(component_dtype); 257 + let vec_size = item_size * dimension; 258 + 259 + for i in 0..( bytes.len() / vec_size ){ 260 + let indx = i * vec_size; 261 + let mut vec = vec![0; vec_size]; 262 + 263 + for j in 0..vec_size{ 264 + vec[j] = i16::from_le_bytes([ 265 + bytes[indx + (j * item_size)], 266 + bytes[indx + (j * item_size) + 1] 267 + ]); 268 + } 269 + decoded.push(vec); 270 + } 271 + 272 + UnityDataTypeArray::kVertexFormatSInt16(decoded) 273 + }, 274 + 275 + 10 => { 276 + let mut decoded: Vec<Vec<_>> = vec![]; 277 + let item_size = get_channel_component_size(component_dtype); 278 + let vec_size = item_size * dimension; 279 + 280 + for i in 0..( bytes.len() / vec_size ){ 281 + let indx = i * vec_size; 282 + let mut vec = vec![0; vec_size]; 283 + 284 + for j in 0..vec_size{ 285 + vec[j] = u32::from_le_bytes([ 286 + bytes[indx + (j * item_size)], 287 + bytes[indx + (j * item_size) + 1], 288 + bytes[indx + (j * item_size) + 2], 289 + bytes[indx + (j * item_size) + 3] 290 + ]); 291 + } 292 + decoded.push(vec); 293 + } 294 + 295 + UnityDataTypeArray::kVertexFormatUInt32(decoded) 296 + }, 297 + 298 + 11 => { 299 + let mut decoded: Vec<Vec<_>> = vec![]; 300 + let item_size = get_channel_component_size(component_dtype); 301 + let vec_size = item_size * dimension; 302 + 303 + for i in 0..( bytes.len() / vec_size ){ 304 + let indx = i * vec_size; 305 + let mut vec = vec![0; vec_size]; 306 + 307 + for j in 0..vec_size{ 308 + vec[j] = i32::from_le_bytes([ 309 + bytes[indx + (j * item_size)], 310 + bytes[indx + (j * item_size) + 1], 311 + bytes[indx + (j * item_size) + 2], 312 + bytes[indx + (j * item_size) + 3] 313 + ]); 314 + } 315 + decoded.push(vec); 316 + } 317 + 318 + UnityDataTypeArray::kVertexFormatSInt32(decoded) 319 + }, 320 + 321 + _ => { UnityDataTypeArray::Unknown } 322 + } 323 + } 324 + 325 + struct StreamInfo{ 326 + channel_mask: usize, 327 + offset: usize, 328 + stride: usize 329 + } 330 + 331 + pub fn try_unity_mesh_to_bevy( mesh_obj: UnityObject, mesh_parser: &MeshParser ) -> Option<Mesh>{ 332 + let mut vertices = None; 333 + let mut normals = None; 334 + let mut tangents = None; 335 + let mut uv0 = None; 336 + 337 + let mesh = mesh_parser.parse_from_unity_object(&mesh_obj).unwrap().mesh; 338 + let vertex_data = mesh.vertex_data; 339 + 340 + dbg!(&vertex_data.channels); 341 + 342 + let mut streams = vec![]; 343 + let stream_count = 1 + vertex_data.channels.iter().map(|x| x.stream).max()? as usize; 344 + 345 + let mut offset = 0; 346 + 347 + for s in 0..stream_count{ 348 + let mut chn_mask = 0; 349 + let mut stride = 0; 350 + 351 + let mut i = 0; 352 + for channel in &vertex_data.channels{ 353 + if channel.stream == s as u8 && channel.dimension > 0{ 354 + chn_mask |= 1 << i; 355 + 356 + let component_size = get_channel_component_size(channel.format); 357 + stride += channel.dimension as usize * component_size; 358 + } 359 + 360 + i += 1; 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); 371 + } 372 + 373 + let mut i = 0; 374 + for channel in vertex_data.channels{ 375 + let stream = &streams[channel.stream as usize]; 376 + 377 + if stream.channel_mask >> i & 1 == 1{ 378 + let component_byte_size = get_channel_component_size(channel.format); 379 + dbg!(&component_byte_size, &channel.format); 380 + let channel_dimension = channel.dimension as usize; 381 + 382 + let mut bytes = 383 + vec![0; vertex_data.vertex_count as usize * channel_dimension * component_byte_size]; 384 + 385 + for v in 0..vertex_data.vertex_count{ 386 + let vertex_offset = stream.offset + channel.offset as usize + stream.stride * v as usize; 387 + 388 + for d in 0..channel_dimension{ 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); 391 + 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); 394 + } 395 + } 396 + 397 + let component_data = unpack_struct(channel_dimension, channel.format, bytes); 398 + 399 + match i{ 400 + 0 => vertices = Some(component_data), 401 + 1 => normals = Some(component_data), 402 + 2 => tangents = Some(component_data), 403 + 4 => uv0 = Some(component_data), 404 + 405 + _ => {} 406 + } 407 + } 408 + 409 + i += 1; 410 + } 411 + 412 + let mut triangles = vec![]; 413 + if mesh.index_buffer.len() > 0{ 414 + for submesh in mesh.sub_meshes{ 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 + _ => {} 444 + } 445 + } 446 + } else{ 447 + return None; 448 + } 449 + 450 + if vertices.is_none(){ panic!("Cannot parse vertices") } 451 + let vertices = vertices.unwrap().as_vec3_array(); 452 + 453 + let mut file = File::create("test.obj").unwrap(); 454 + 455 + for vec in &vertices{ 456 + writeln!(file, "v {} {} {}", vec.x, vec.y, vec.z).unwrap(); 457 + } 458 + 459 + for i in 0..(triangles.len() / 3){ 460 + let indx = i * 3; 461 + writeln!(file, "f {} {} {}", triangles[indx] + 1, triangles[indx + 1] + 1, triangles[indx + 2] + 1).unwrap(); 462 + } 463 + 464 + Some(Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD) 465 + .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices) 466 + .with_inserted_indices(Indices::U16(triangles))) 467 + }
+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); }
+36 -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 { 73 - channels: 1, 78 + channels: 2, 74 79 buffer_size: BufferSize::Fixed(BUFFER_SIZE as u32), 75 80 sample_rate: SampleRate(SAMPLE_RATE as u32) 76 81 }, ··· 81 86 buffer_indx = 0; 82 87 } else{ 83 88 for i in 0..data.len(){ 89 + if i % 2 == 1{ continue; } 90 + 84 91 buffer[buffer_indx] = data[i]; 85 92 buffer_indx += 1; 86 93 87 94 if buffer_indx >= MONO_20MS{ 88 95 buffer_indx = 0; 89 96 90 - let mut total = 0; 91 - for sample in buffer{ total += (sample as isize).pow(2) } 92 - *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) } 93 99 94 - let len = encoder.encode(&buffer, &mut output).unwrap(); 95 - 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; 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(); 96 106 97 - let packet = PlayerVoicePacket { 98 - 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. 99 - packet: buf_to_send 100 - }; 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 + }; 101 111 102 - let packet: Vec<u8> = packet.to_buf().into(); 103 - udp.send_to(&packet, addr).unwrap(); 112 + let packet: Vec<u8> = packet.to_buf().into(); 113 + udp.send_to(&packet, addr).unwrap(); 114 + } 104 115 } 105 116 } 106 117 } ··· 115 126 Ok(()) 116 127 } 117 128 118 - pub fn get_rms_of_last_packet( &self ) -> f32{ 119 - *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 ) 120 134 } 121 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)
+4
client/Cargo.toml
··· 10 10 dotenvy = "0.15.7" 11 11 felix-net = { path = '../net' } 12 12 felix-audio = { path = '../audio' } 13 + felix-assets = { path = '../assets' } 13 14 tokio = { version = "1.48.0", features = ["full"] } 15 + bevy_mod_openxr = "0.4.0" 16 + openxr = "0.19.0" 17 + bevy_mod_xr = "0.4.0"
+17 -7
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 5 use felix_audio::voice::microphone::VoiceChatMicrophone; 5 6 6 7 use crate::net::connection::Connection; ··· 33 34 mut debug_text: Query<(&mut Text, &DebugText)>, 34 35 voice: Query<&VoiceChatMicrophone>, 35 36 networking: Query<&Connection>, 37 + xr_state: Res<XrState> 36 38 ){ 37 39 let net_manager = networking.single().unwrap(); 38 40 ··· 76 78 77 79 let mut remote_rms = "".to_owned(); 78 80 for id in net_manager.get_remote_player_voice_ids(){ 79 - 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(); 80 86 } 81 87 82 - let rms = if let Ok(mic) = voice.single(){ 88 + let ( rms, mic_open ) = if let Ok(mic) = voice.single(){ 83 89 mic.get_rms_of_last_packet() 84 90 } else{ 85 - -1.0 91 + ( -1.0, false ) 86 92 }; 87 93 88 94 let ( mut text, _ ) = debug_text.single_mut().unwrap(); 89 95 text.0 = format!( 90 - "Microphone RMS: {:.4} 91 - {}Position: {:.2} {:.2} {:.2} 92 - 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} 99 + XR State: {:#?}", 93 100 rms, 101 + mic_open, 94 102 95 103 remote_rms, 96 104 ··· 101 109 transform.rotation.x, 102 110 transform.rotation.y, 103 111 transform.rotation.z, 104 - transform.rotation.w 112 + transform.rotation.w, 113 + 114 + xr_state.into_inner() 105 115 ); 106 116 } 107 117
+24 -4
client/src/main.rs
··· 1 - use bevy::{DefaultPlugins, app::{App, FixedUpdate, Startup, Update}, ecs::system::Commands}; 1 + use std::{env, net::ToSocketAddrs}; 2 + 3 + use bevy::{app::{App, FixedUpdate, Startup, Update}, ecs::system::Commands, prelude::*, render::pipelined_rendering::PipelinedRenderingPlugin}; 4 + use bevy_mod_openxr::{add_xr_plugins, resources::OxrSessionConfig}; 5 + use bevy_mod_xr::session::{XrSessionCreated, XrSessionPlugin}; 2 6 use felix_audio::{FelixAudio, voice}; 7 + use openxr::EnvironmentBlendMode; 3 8 4 9 use crate::{components::{debug_camera, network_interface, remote_player}, setup::setup}; 5 10 ··· 12 17 13 18 App::new() 14 19 .add_plugins(( 15 - DefaultPlugins, 20 + add_xr_plugins( 21 + DefaultPlugins 22 + .build() 23 + .disable::<PipelinedRenderingPlugin>(), 24 + ).set(XrSessionPlugin { auto_handle: true }), 16 25 FelixAudio 17 26 )) 27 + .insert_resource(OxrSessionConfig { 28 + blend_mode_preference: vec![ 29 + EnvironmentBlendMode::ALPHA_BLEND, 30 + EnvironmentBlendMode::ADDITIVE, 31 + EnvironmentBlendMode::OPAQUE 32 + ], 33 + ..Default::default() 34 + }) 35 + .add_systems(XrSessionCreated, || { println!("[INFO] Started VR") }) 18 36 .add_systems(Startup, setup) 19 37 .add_systems(Startup, move | mut commands: Commands |{ 20 - 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"); 21 41 commands.spawn(comp); 22 42 23 - match voice::init_microphone(voice){ 43 + match voice::init_microphone(addr, voice){ 24 44 Ok(voice) => { commands.spawn(voice); }, 25 45 Err(err) => println!("[WARN] Failed to start microphone: {}", err) 26 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
+29 -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_assets::load("test.flx").unwrap(); 49 + for go in gos{ 50 + commands.spawn(( 51 + Mesh3d(meshes.add(go.mesh)), 52 + MeshMaterial3d(materials.add(Color::WHITE)), 53 + go.transform 54 + )); 55 + } 47 56 }