Compare commits

..

9 Commits

Author SHA1 Message Date
b05f29da23 runnable without vr 2026-02-24 03:04:00 +01:00
17a0de5903 hasn't been that in a while 2026-02-23 22:16:55 +01:00
485ea056a7 lets use embedded_graphics lol 2026-02-23 22:14:22 +01:00
4296a6488c crosshair is nice 2026-02-23 01:54:44 +01:00
b82a0eb7d4 feat: drawing and stuff 2026-02-22 20:39:23 +01:00
bb05dc5161 head lookat move mouse test 2026-02-21 19:08:35 +01:00
a152eeadc6 no need for this 2026-02-21 14:34:43 +01:00
99c06d3419 window size and color 2026-02-21 14:28:47 +01:00
a727ce60ca remove avian, we don't actually need it 2026-02-21 14:23:09 +01:00
11 changed files with 1302 additions and 572 deletions

622
Cargo.lock generated
View File

@@ -103,12 +103,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "alsa"
version = "0.9.1"
@@ -225,9 +219,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.101"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "approx"
@@ -389,42 +383,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "avian3d"
version = "0.5.0"
name = "az"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b82608b43397affac139547dfa9beb3208034c53e1a3cfe5d4079db1af362104"
dependencies = [
"approx",
"avian_derive",
"bevy",
"bevy_heavy",
"bevy_math",
"bevy_transform_interpolation",
"bitflags 2.11.0",
"derive_more",
"disqualified",
"glam_matrix_extras",
"itertools 0.13.0",
"nalgebra",
"parry3d",
"parry3d-f64",
"slab",
"smallvec",
"thiserror 2.0.18",
"thread_local",
]
[[package]]
name = "avian_derive"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b257f601a1535e0d4a7a7796f535e3a13de62fd422b16dff7c14d27f0d4048"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
]
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]]
name = "base64"
@@ -942,17 +904,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "bevy_heavy"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc496d1d43b890896cf561d8ce3dcf7b7b8e4c03c4e837a49a83a530abccbc5e"
dependencies = [
"bevy_math",
"bevy_reflect",
"glam_matrix_extras",
]
[[package]]
name = "bevy_image"
version = "0.18.0"
@@ -1131,7 +1082,7 @@ dependencies = [
"arrayvec",
"bevy_reflect",
"derive_more",
"glam 0.30.10",
"glam",
"itertools 0.14.0",
"libm",
"rand",
@@ -1175,7 +1126,6 @@ checksum = "7ef8e4b7e61dfe7719bb03c884dc270cd46a82efb40f93e9933b990c5c190c59"
[[package]]
name = "bevy_mod_openxr"
version = "0.5.0"
source = "git+https://git.avii.nl/Avii/bevy_oxr.git#7936b53a024b993c53fc7427b5b290bf210978a9"
dependencies = [
"android_system_properties",
"ash",
@@ -1200,7 +1150,6 @@ dependencies = [
[[package]]
name = "bevy_mod_xr"
version = "0.5.0"
source = "git+https://git.avii.nl/Avii/bevy_oxr.git#7936b53a024b993c53fc7427b5b290bf210978a9"
dependencies = [
"bevy_app",
"bevy_camera",
@@ -1366,7 +1315,7 @@ dependencies = [
"downcast-rs 2.0.2",
"erased-serde",
"foldhash 0.2.0",
"glam 0.30.10",
"glam",
"indexmap",
"inventory",
"petgraph",
@@ -1451,7 +1400,7 @@ dependencies = [
"downcast-rs 2.0.2",
"encase",
"fixedbitset",
"glam 0.30.10",
"glam",
"image",
"indexmap",
"js-sys",
@@ -1619,7 +1568,7 @@ dependencies = [
"crossbeam-queue",
"derive_more",
"futures-lite",
"heapless 0.9.2",
"heapless",
"pin-project",
]
@@ -1682,15 +1631,6 @@ dependencies = [
"thiserror 2.0.18",
]
[[package]]
name = "bevy_transform_interpolation"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88545c8b0fa8f3502b9a439c71fa6b596ee9e808bfb16f27a51c8c6f7405a657"
dependencies = [
"bevy",
]
[[package]]
name = "bevy_ui"
version = "0.18.0"
@@ -2518,25 +2458,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
@@ -2589,9 +2510,9 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "deranged"
version = "0.5.6"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
@@ -2619,6 +2540,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "deunicode"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
[[package]]
name = "directories"
version = "6.0.0"
@@ -2628,6 +2555,15 @@ dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
@@ -2735,12 +2671,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "ena"
version = "0.14.4"
name = "embedded-graphics"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1"
checksum = "4e8da660bb0c829b34a56a965490597f82a55e767b91f9543be80ce8ccb416fe"
dependencies = [
"log",
"az",
"byteorder",
"embedded-graphics-core",
"float-cmp",
"micromath",
]
[[package]]
name = "embedded-graphics-core"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95743bef3ff70fcba3930246c4e6872882bbea0dcc6da2ca860112e0cd4bd09f"
dependencies = [
"az",
"byteorder",
]
[[package]]
@@ -2918,6 +2868,15 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
dependencies = [
"num-traits",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -3174,103 +3133,12 @@ dependencies = [
"xml-rs",
]
[[package]]
name = "glam"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
[[package]]
name = "glam"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b"
[[package]]
name = "glam"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16"
[[package]]
name = "glam"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776"
[[package]]
name = "glam"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34"
[[package]]
name = "glam"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca"
[[package]]
name = "glam"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f"
[[package]]
name = "glam"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815"
[[package]]
name = "glam"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774"
[[package]]
name = "glam"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
[[package]]
name = "glam"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945"
[[package]]
name = "glam"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
[[package]]
name = "glam"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
[[package]]
name = "glam"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
[[package]]
name = "glam"
version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
[[package]]
name = "glam"
version = "0.30.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
dependencies = [
"approx",
"bytemuck",
"encase",
"libm",
@@ -3278,16 +3146,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "glam_matrix_extras"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4664468a60479272b880a8bfc00ad2915229b93d2b2d585556fb33f9ba80e72"
dependencies = [
"bevy_reflect",
"glam 0.30.10",
]
[[package]]
name = "glob"
version = "0.3.3"
@@ -3458,8 +3316,6 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.1.5",
]
@@ -3475,16 +3331,6 @@ dependencies = [
"serde_core",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "heapless"
version = "0.9.2"
@@ -3515,7 +3361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29a164ceff4500f2a72b1d21beaa8aa8ad83aec2b641844c659b190cb3ea2e0b"
dependencies = [
"constgebra",
"glam 0.30.10",
"glam",
"tinyvec",
]
@@ -3861,9 +3707,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.85"
version = "0.3.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -3890,15 +3736,19 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
name = "kneeboard"
version = "0.1.0"
dependencies = [
"avian3d",
"bevy",
"bevy_cef",
"bevy_mod_openxr",
"bevy_mod_xr",
"bevy_pkv",
"crossbeam-channel",
"embedded-graphics",
"embedded-graphics-core",
"openxr",
"otd-ipc",
"rdev",
"serde",
"triple_buffer",
]
[[package]]
@@ -4064,16 +3914,6 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "matrixmultiply"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memchr"
version = "2.8.0"
@@ -4104,6 +3944,12 @@ dependencies = [
"paste",
]
[[package]]
name = "micromath"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -4180,49 +4026,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "nalgebra"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f"
dependencies = [
"approx",
"glam 0.14.0",
"glam 0.15.2",
"glam 0.16.0",
"glam 0.17.3",
"glam 0.18.0",
"glam 0.19.0",
"glam 0.20.5",
"glam 0.21.3",
"glam 0.22.0",
"glam 0.23.0",
"glam 0.24.2",
"glam 0.25.0",
"glam 0.27.0",
"glam 0.28.0",
"glam 0.29.3",
"glam 0.30.10",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ndk"
version = "0.8.0"
@@ -4334,25 +4137,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.0"
@@ -4370,26 +4154,6 @@ dependencies = [
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -4765,6 +4529,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "otd-ipc"
version = "0.1.0"
dependencies = [
"bytes",
"dirs",
"serde",
"serde_bytes",
"slug",
]
[[package]]
name = "owned_ttf_parser"
version = "0.25.1"
@@ -4803,64 +4578,6 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "parry3d"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e99471b7b6870f7fe406d5611dd4b4c9b07aa3e5436b1d27e1515f9832bb0c6b"
dependencies = [
"approx",
"arrayvec",
"bitflags 2.11.0",
"downcast-rs 2.0.2",
"either",
"ena",
"foldhash 0.2.0",
"glam 0.30.10",
"hashbrown 0.16.1",
"log",
"nalgebra",
"num-derive",
"num-traits",
"ordered-float",
"rayon",
"rstar",
"simba",
"slab",
"smallvec",
"spade",
"static_assertions",
"thiserror 2.0.18",
]
[[package]]
name = "parry3d-f64"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38fe282b81b60a2aee7f24db25ea73b3c82f6451888eeb5936b621adb87aa653"
dependencies = [
"approx",
"arrayvec",
"bitflags 2.11.0",
"downcast-rs 2.0.2",
"either",
"ena",
"foldhash 0.2.0",
"hashbrown 0.16.1",
"log",
"nalgebra",
"num-derive",
"num-traits",
"ordered-float",
"rayon",
"rstar",
"simba",
"slab",
"smallvec",
"spade",
"thiserror 2.0.18",
]
[[package]]
name = "paste"
version = "1.0.15"
@@ -5048,28 +4765,6 @@ dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -5181,32 +4876,6 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "rayon"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "rdev"
version = "0.5.3"
@@ -5368,12 +5037,6 @@ dependencies = [
"serde",
]
[[package]]
name = "robust"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e27ee8bb91ca0adcf0ecb116293afa12d393f9c2b9b9cd54d33e8078fe19839"
[[package]]
name = "rodio"
version = "0.20.1"
@@ -5404,17 +5067,6 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rstar"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb"
dependencies = [
"heapless 0.8.0",
"num-traits",
"smallvec",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -5446,7 +5098,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -5512,15 +5164,6 @@ dependencies = [
"twox-hash",
]
[[package]]
name = "safe_arch"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
dependencies = [
"bytemuck",
]
[[package]]
name = "same-file"
version = "1.0.6"
@@ -5587,6 +5230,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_bytes"
version = "0.11.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
@@ -5641,19 +5294,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simba"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
name = "simd-adler32"
version = "0.3.8"
@@ -5695,6 +5335,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "slug"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
dependencies = [
"deunicode",
"wasm-bindgen",
]
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -5759,18 +5409,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "spade"
version = "2.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb313e1c8afee5b5647e00ee0fe6855e3d529eb863a0fdae1d60006c4d1e9990"
dependencies = [
"hashbrown 0.15.5",
"num-traits",
"robust",
"smallvec",
]
[[package]]
name = "spin"
version = "0.10.0"
@@ -5847,9 +5485,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.116"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -6175,6 +5813,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "triple_buffer"
version = "8.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420466259f9fa5decc654c490b9ab538400e5420df8237f84ecbe20368bcf72b"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "ttf-parser"
version = "0.25.1"
@@ -6196,12 +5843,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "typewit"
version = "1.14.2"
@@ -6402,9 +6043,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.108"
version = "0.2.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac"
dependencies = [
"cfg-if",
"once_cell",
@@ -6415,9 +6056,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.58"
version = "0.4.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
checksum = "fe88540d1c934c4ec8e6db0afa536876c5441289d7f9f9123d4f065ac1250a6b"
dependencies = [
"cfg-if",
"futures-util",
@@ -6429,9 +6070,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.108"
version = "0.2.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -6439,9 +6080,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.108"
version = "0.2.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -6452,9 +6093,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.108"
version = "0.2.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41"
dependencies = [
"unicode-ident",
]
@@ -6603,9 +6244,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.85"
version = "0.3.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -6792,16 +6433,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wide"
version = "0.7.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -7125,15 +6756,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.61.2"

View File

@@ -6,13 +6,21 @@ description = "Bevy powered Kneeboard OpenXR Overlay"
license = "MIT/Apache-2.0"
[dependencies]
avian3d = { version = "0.5.0", features = ["simd"] }
bevy = { version = "0.18", features = ["debug"] }
bevy_cef = { path = "../bevy_cef" }
bevy_mod_xr = { git = "https://git.avii.nl/Avii/bevy_oxr.git", version = "0.5.0" }
bevy_mod_openxr = { git = "https://git.avii.nl/Avii/bevy_oxr.git", version = "0.5.0" }
# bevy_mod_xr = { git = "https://git.avii.nl/Avii/bevy_oxr.git", version = "0.5.0" }
# bevy_mod_openxr = { git = "https://git.avii.nl/Avii/bevy_oxr.git", version = "0.5.0" }
bevy_mod_xr = { path = "../bevy_oxr/crates/bevy_xr" }
bevy_mod_openxr = { path = "../bevy_oxr/crates/bevy_openxr" }
bevy_pkv = "0.15.0"
# bevy_xr_utils.workspace = true
openxr = "0.21.1"
rdev = { version = "0.5.3", features = ["unstable_grab"] }
serde = { version = "1.0.228", features = ["derive"] }
otd-ipc = { path = "../../otd-ipc-client-rs" }
crossbeam-channel = "0.5.15"
triple_buffer = "8.1.1"
embedded-graphics = "0.8.2"
embedded-graphics-core = "0.4.1"

233
src/drawingplugin.rs Normal file
View File

@@ -0,0 +1,233 @@
mod drawtarget;
mod rgba;
mod uiplugin;
use bevy_cef::prelude::WebviewExtendStandardMaterial;
use bevy::{
asset::RenderAssetUsages,
color::palettes::css,
prelude::*,
render::render_resource::{Extent3d, TextureDimension, TextureFormat},
};
use embedded_graphics::prelude::DrawTarget;
use crate::{
drawingplugin::drawtarget::{DrawableLayer, Visible},
otdipcplugin::OtdIpcPlugin,
};
use crate::{
drawingplugin::uiplugin::UiPlugin,
otdipcplugin::{PenButtons, PenPosition, PenPressure},
};
const IMAGE_WIDTH: usize = (210.0 * 3.5) as usize;
const IMAGE_HEIGHT: usize = (279.0 * 3.5) as usize;
#[derive(Resource)]
pub struct MyProcGenImage(pub(crate) Handle<Image>);
#[derive(Component)]
struct CursorBuffer;
#[derive(Component)]
struct DrawingBuffer;
#[derive(Resource)]
struct PenSize(f32);
#[derive(Resource)]
struct PenColor(Color);
#[derive(Resource)]
pub struct LastPenPos(pub Option<(i32, i32)>);
pub struct DrawingPlugin;
impl Plugin for DrawingPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(OtdIpcPlugin);
app.add_plugins(UiPlugin);
app.insert_resource(ClearColor(Color::NONE))
.insert_resource(LastPenPos(None))
.insert_resource(PenColor(css::BLACK.into()))
.insert_resource(PenSize(5.0));
app.add_systems(Startup, setup)
.add_systems(Update, (cursor, draw, plot));
}
}
pub fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
let image = Image::new_fill(
Extent3d {
width: IMAGE_WIDTH as u32,
height: IMAGE_HEIGHT as u32,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&(Srgba::new(1.0, 1.0, 1.0, 0.0).to_u8_array()),
TextureFormat::Rgba8UnormSrgb,
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
);
let handle = images.add(image);
commands.insert_resource(MyProcGenImage(handle));
commands.spawn((
CursorBuffer,
DrawableLayer::new(IMAGE_WIDTH, IMAGE_HEIGHT, drawtarget::Layer::Drawing),
Visible,
));
commands.spawn((
DrawingBuffer,
DrawableLayer::new(IMAGE_WIDTH, IMAGE_HEIGHT, drawtarget::Layer::Cursor),
Visible,
));
}
// fn color_changer(mut pen_color: ResMut<PenColor>, mut pen_buttons: MessageReader<PenButtons>) {
// let Some(buttons) = pen_buttons.read().next() else {
// return;
// };
// pen_color.0 = css::BLACK.into();
// if buttons.a() {
// let mut t = LinearRgba::from(pen_color.0);
// t.alpha = 0.0;
// pen_color.0 = t.into();
// }
// }
fn cursor(
pen_color: Res<PenColor>,
mut buffer: Query<&mut DrawableLayer, With<CursorBuffer>>,
mut pen_size: ResMut<PenSize>,
mut pen_buttons: MessageReader<PenButtons>,
mut pen_position: MessageReader<PenPosition>,
mut pen_pressure: MessageReader<PenPressure>,
) {
let Ok(mut buffer) = buffer.single_mut() else {
return;
};
let mut size: f32 = 1.0;
let mut offset = 20.0;
let c = pen_color.0;
if let Some(buttons) = pen_buttons.read().next()
&& buttons.a()
{
offset *= 4.;
};
for penpres in pen_pressure.read() {
let _in = penpres.pressure;
let _out = penpres.pressure.powi(4);
size *= 1. + (_out * offset);
}
pen_size.0 = size.clamp(1.0, 200.0);
let s = pen_size.0 as i32;
let b = css::BLACK.into();
let cs = 10;
let cd = 3;
for penpos in pen_position.read() {
let x = (penpos.x * (buffer.width as f32)) as i32;
let y = (penpos.y * (buffer.height as f32)) as i32;
buffer.clear(Srgba::new(0.0, 0.0, 0.0, 0.0).into()).ok();
buffer.draw_stroke(x - (cs + cd) - s, y, x - (cd) - s, y, 4.0, b);
buffer.draw_stroke(x - (cs + cd) - s, y, x - (cd) - s, y, 1.0, c);
buffer.draw_stroke(x + (cd) + s, y, x + (cs + cd) + s, y, 4.0, b);
buffer.draw_stroke(x + (cd) + s, y, x + (cs + cd) + s, y, 1.0, c);
buffer.draw_stroke(x, y - (cs + cd) - s, x, y - (cd) - s, 4.0, b);
buffer.draw_stroke(x, y - (cs + cd) - s, x, y - (cd) - s, 1.0, c);
buffer.draw_stroke(x, y + (cs + cd) + s, x, y + (cd) + s, 4.0, b);
buffer.draw_stroke(x, y + (cs + cd) + s, x, y + (cd) + s, 1.0, c);
}
}
fn draw(
pen_size: Res<PenSize>,
pen_color: Res<PenColor>,
ui: Query<&Visible, With<uiplugin::UiBuffer>>,
mut lastloc: ResMut<LastPenPos>,
mut buffer: Query<&mut DrawableLayer, With<DrawingBuffer>>,
mut pen_buttons: MessageReader<PenButtons>,
mut pen_position: MessageReader<PenPosition>,
) {
if ui.single().is_ok() {
lastloc.0 = None;
return; // if the ui layer is visible, dont draw
}
let Ok(mut buffer) = buffer.single_mut() else {
return;
};
let Some(buttons) = pen_buttons.read().next() else {
return;
};
for penpos in pen_position.read() {
let x = (penpos.x * (buffer.width as f32)) as i32;
let y = (penpos.y * (buffer.height as f32)) as i32;
let Some(ll) = lastloc.0 else {
lastloc.0 = Some((x, y));
return;
};
if buttons.tip() {
buffer.draw_stroke(ll.0, ll.1, x, y, pen_size.0, pen_color.0);
}
lastloc.0 = Some((x, y));
}
}
fn plot(
buffers: Query<&DrawableLayer, With<Visible>>,
mut images: ResMut<Assets<Image>>,
mut materials: ResMut<Assets<WebviewExtendStandardMaterial>>,
webviews: Query<&MeshMaterial3d<WebviewExtendStandardMaterial>>,
my_handle: Res<MyProcGenImage>,
) {
let image = images.get_mut(&my_handle.0).expect("Image not found");
image.clear(&[0, 0, 0, 0]);
let sorted = buffers
.iter()
.sort_by::<&DrawableLayer>(|a, b| b.order.cmp(&a.order));
for layer in sorted {
for (i, c) in layer.buffer.chunks(4).enumerate() {
let x = i % layer.width;
let y = i / layer.width;
let red: u8 = c[0];
let green = c[1];
let blue = c[2];
let alpha = c[3];
if alpha > 0 {
let color = Color::srgba_u8(red, green, blue, alpha);
image.set_color_at(x as u32, y as u32, color).unwrap();
}
}
}
// Poke redraw
for handle in webviews {
if let Some(material) = materials.get_mut(handle.id()) {
material.extension.overlay = Some(my_handle.0.clone());
}
}
}

View File

@@ -0,0 +1,342 @@
use bevy::{color::Color, ecs::component::Component};
use embedded_graphics::{pixelcolor::Rgb888, prelude::*, primitives::*};
use crate::drawingplugin::rgba::Rgba;
#[allow(unused)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Layer {
Drawing,
Ui,
Cursor,
}
#[derive(Component)]
pub struct Visible;
#[derive(Component)]
pub struct DrawableLayer {
pub order: Layer,
pub width: usize,
pub height: usize,
pub buffer: Vec<u8>,
}
impl DrawableLayer {
pub fn new(width: usize, height: usize, order: Layer) -> Self {
let buffer = vec![0; width * height * 4];
Self {
order,
width,
height,
buffer,
}
}
}
#[allow(unused)]
impl DrawableLayer {
pub fn draw_stroke(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
thickness: f32,
color: Color,
) {
let (mut x0, mut y0) = (x1, y1);
let (x1, y1) = (x2, y2);
let dx = (x1 - x0).abs();
let dy = -(y1 - y0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx + dy;
let half_t = (thickness / 2.0).ceil() as i32;
loop {
self.draw_filled_circle(x0, y0, half_t, color);
if x0 == x1 && y0 == y1 {
break;
}
let e2 = 2 * err;
if e2 >= dy {
err += dy;
x0 += sx;
}
if e2 <= dx {
err += dx;
y0 += sy;
}
}
}
pub fn set_color_at(&mut self, x: i32, y: i32, color: Color) {
Pixel(Point::new(x, y), super::rgba::Rgba::from(color.to_srgba()))
.draw(self)
.ok();
}
pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, thickness: f32, color: Color) {
Line::new(Point::new(x1, y1), Point::new(x2, y2))
.into_styled(PrimitiveStyle::with_stroke(
Rgba::from(color.to_srgba()),
thickness as u32,
))
.draw(self)
.ok();
}
pub fn draw_circle(&mut self, cx: i32, cy: i32, radius: i32, thickness: f32, color: Color) {
Circle::new(Point::new(cx - radius, cy - radius), (radius * 2) as u32)
.into_styled(PrimitiveStyle::with_stroke(
Rgba::from(color.to_srgba()),
thickness as u32,
))
.draw(self)
.ok();
}
pub fn draw_filled_circle(&mut self, cx: i32, cy: i32, radius: i32, color: Color) {
Circle::new(Point::new(cx - radius, cy - radius), (radius * 2) as u32)
.into_styled(PrimitiveStyle::with_fill(Rgba::from(color.to_srgba())))
.draw(self)
.ok();
}
pub fn draw_rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, thickness: f32, color: Color) {
Rectangle::new(Point::new(x, y), Size::new(w, h))
.into_styled(PrimitiveStyle::with_stroke(
Rgba::from(color.to_srgba()),
thickness as u32,
))
.draw(self)
.ok();
}
pub fn draw_filled_rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
Rectangle::new(Point::new(x, y), Size::new(w, h))
.into_styled(PrimitiveStyle::with_fill(Rgba::from(color.to_srgba())))
.draw(self)
.ok();
}
}
impl Dimensions for DrawableLayer {
fn bounding_box(&self) -> embedded_graphics::primitives::Rectangle {
Rectangle::new(
Point::new(0, 0),
Size::new(self.width as u32, self.height as u32),
)
}
}
impl DrawTarget for DrawableLayer {
type Color = Rgba<Rgb888>;
type Error = Box<dyn std::error::Error>;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
{
for p in pixels {
let x = p.0.x as usize;
let y = p.0.y as usize;
if !(0..=self.width).contains(&x) || !(0..=self.height).contains(&y) {
continue;
}
let index = (x + (self.width * y)) * 4;
if index >= self.buffer.len() {
continue;
}
self.buffer[index] = p.1.r();
self.buffer[index + 1] = p.1.g();
self.buffer[index + 2] = p.1.b();
self.buffer[index + 3] = p.1.a();
}
Ok(())
}
}
// pub fn set_color_at(&mut self, x: i32, y: i32, color: Color) {
// let x: usize = x as usize;
// let y: usize = y as usize;
// if !(0..=self.width).contains(&x) || !(0..=self.height).contains(&y) {
// return;
// }
// let index = (x + (self.width * y)) * 4;
// let srgba = Srgba::from(color);
// if index >= self.buffer.len() {
// return;
// }
// self.buffer[index] = (srgba.red * u8::MAX as f32) as u8;
// self.buffer[index + 1] = (srgba.green * u8::MAX as f32) as u8;
// self.buffer[index + 2] = (srgba.blue * u8::MAX as f32) as u8;
// self.buffer[index + 3] = (srgba.alpha * u8::MAX as f32) as u8;
// }
// pub fn clear(&mut self) {
// self.buffer.fill(0);
// }
// pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, thickness: f32, color: Color) {
// let (mut x0, mut y0) = (x1, y1);
// let (x1, y1) = (x2, y2);
// let dx = (x1 - x0).abs();
// let dy = -(y1 - y0).abs();
// let sx = if x0 < x1 { 1 } else { -1 };
// let sy = if y0 < y1 { 1 } else { -1 };
// let mut err = dx + dy;
// let half_t = (thickness / 2.0).ceil() as i32;
// loop {
// self.draw_thick_point(x0, y0, half_t, color);
// if x0 == x1 && y0 == y1 {
// break;
// }
// let e2 = 2 * err;
// if e2 >= dy {
// err += dy;
// x0 += sx;
// }
// if e2 <= dx {
// err += dx;
// y0 += sy;
// }
// }
// }
// // Draw a small square brush (fast, branch-free inner loop)
// pub fn draw_thick_point(&mut self, cx: i32, cy: i32, radius: i32, color: Color) {
// self.draw_filled_circle(cx, cy, radius, color);
// }
// pub fn draw_filled_circle(&mut self, cx: i32, cy: i32, radius: i32, color: Color) {
// let r2 = radius * radius;
// for dy in -radius..=radius {
// let y = cy + dy;
// if y < 0 {
// continue;
// }
// let dx_limit = ((r2 - dy * dy) as f32).sqrt() as i32;
// let start_x = cx - dx_limit;
// let end_x = cx + dx_limit;
// for x in start_x..=end_x {
// if x >= 0 {
// self.set_color_at(x, y, color);
// }
// }
// }
// }
// pub fn draw_circle(&mut self, cx: i32, cy: i32, radius: i32, thickness: f32, color: Color) {
// if radius <= 0 {
// return;
// }
// let half_t = (thickness / 2.0).max(0.5);
// let outer_r = radius as f32 + half_t;
// let inner_r = (radius as f32 - half_t).max(0.0);
// let outer_r2 = (outer_r * outer_r) as i32;
// let inner_r2 = (inner_r * inner_r) as i32;
// let max_r = outer_r.ceil() as i32;
// for dy in -max_r..=max_r {
// let y = cy + dy;
// if y < 0 {
// continue;
// }
// let dy2 = dy * dy;
// if dy2 > outer_r2 {
// continue;
// }
// let outer_dx = ((outer_r2 - dy2) as f32).sqrt() as i32;
// let inner_dx = if dy2 < inner_r2 {
// ((inner_r2 - dy2) as f32).sqrt() as i32
// } else {
// -1
// };
// let left_outer = cx - outer_dx;
// let right_outer = cx + outer_dx;
// if inner_dx >= 0 {
// let left_inner = cx - inner_dx;
// let right_inner = cx + inner_dx;
// // left segment
// for x in left_outer..left_inner {
// if x >= 0 {
// self.set_color_at(x, y, color);
// }
// }
// // right segment
// for x in (right_inner + 1)..=right_outer {
// if x >= 0 {
// self.set_color_at(x, y, color);
// }
// }
// } else {
// // fully filled span (very thin or small radius)
// for x in left_outer..=right_outer {
// if x >= 0 {
// self.set_color_at(x, y, color);
// }
// }
// }
// }
// }
// pub fn draw_rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, thickness: f32, color: Color) {
// let w = w as i32;
// let h = h as i32;
// self.draw_line(x, y, x + w, y, thickness, color);
// self.draw_line(x + w, y, x + w, y + h, thickness, color);
// self.draw_line(x, y + h, x + w, y + h, thickness, color);
// self.draw_line(x, y, x, y + h, thickness, color);
// }
// pub fn draw_filled_rectangle(
// &mut self,
// x: i32,
// y: i32,
// w: u32,
// h: u32,
// thickness: f32,
// color: Color,
// ) {
// let w = w as i32;
// let h = h as i32;
// for i in 0..h {
// self.draw_line(x, y + i, x + w, y + i, thickness, color);
// }
// }
// }

94
src/drawingplugin/rgba.rs Normal file
View File

@@ -0,0 +1,94 @@
use bevy::color::Srgba;
use embedded_graphics_core::pixelcolor::*;
/// Simple RGBA color wrapper.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Rgba<C: RgbColor>(C, u8);
#[allow(unused)]
#[inline(always)]
fn mul_blend_u8(delta: u32, a: u32) -> u32 {
// Exact (delta * a) / 255 using the div255 trick (no slow integer division).
// Valid for 0..=65535 inputs; see Hacker's Delight 10-16.
let t = delta * a + 128;
(t + (t >> 8)) >> 8
}
#[allow(unused)]
impl<C: RgbColor> Rgba<C> {
/// Create a new RGBA color.
pub const fn new(color: C, alpha: u8) -> Self {
Self(color, alpha)
}
/// Get the color component.
pub const fn rgb(&self) -> C {
self.0
}
pub fn r(&self) -> u8 {
self.0.r()
}
pub fn g(&self) -> u8 {
self.0.g()
}
pub fn b(&self) -> u8 {
self.0.b()
}
/// Get the alpha component (0..=255).
pub const fn a(&self) -> u8 {
self.1
}
}
impl<C: RgbColor> PixelColor for Rgba<C> {
type Raw = C::Raw;
}
#[allow(unused)]
pub trait Blend<T> {
fn blend(&self, bg: T) -> T;
}
impl Blend<Rgb888> for Rgba<Rgb888> {
#[inline(always)]
fn blend(&self, bg: Rgb888) -> Rgb888 {
let a = self.a() as u32;
if a == 0 {
return bg;
}
if a == 255 {
return self.rgb();
}
let fr = self.rgb().r() as u32;
let fg = self.rgb().g() as u32;
let fb = self.rgb().b() as u32;
let br = bg.r() as u32;
let bgc = bg.g() as u32;
let bb = bg.b() as u32;
let r = (br + mul_blend_u8(fr.wrapping_sub(br), a)) as u8;
let g = (bgc + mul_blend_u8(fg.wrapping_sub(bgc), a)) as u8;
let b = (bb + mul_blend_u8(fb.wrapping_sub(bb), a)) as u8;
Rgb888::new(r, g, b)
}
}
impl From<Srgba> for Rgba<Rgb888> {
fn from(value: Srgba) -> Self {
Self(
Rgb888::new(
(value.red * 255.) as u8,
(value.green * 255.) as u8,
(value.blue * 255.) as u8,
),
(value.alpha * 255.) as u8,
)
}
}

View File

@@ -0,0 +1,270 @@
use std::f32;
use bevy::{color::palettes::css, prelude::*};
use embedded_graphics::prelude::DrawTarget;
use crate::{
drawingplugin::{DrawingBuffer, IMAGE_HEIGHT, IMAGE_WIDTH, PenColor, drawtarget::Visible},
otdipcplugin::{PenButtons, PenPosition},
};
use super::drawtarget::{DrawableLayer, Layer};
#[derive(Component)]
pub struct UiBuffer;
#[derive(Component, Deref)]
pub struct UiPosition(Vec2);
pub struct UiPlugin;
impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup).add_systems(
Update,
(
show_hide,
ui_on_show,
ui_on_hide,
color_changer,
clear,
color_picker,
),
);
}
}
fn setup(mut commands: Commands) {
commands.spawn((
UiBuffer,
DrawableLayer::new(IMAGE_WIDTH, IMAGE_HEIGHT, Layer::Ui),
));
}
fn show_hide(
mut commands: Commands,
mut pen_buttons: MessageReader<PenButtons>,
ent: Query<Entity, With<UiBuffer>>,
) {
let Ok(ent) = ent.single() else {
return;
};
let Some(buttons) = pen_buttons.read().next() else {
return;
};
if buttons.b() {
commands.entity(ent).insert(Visible);
} else {
commands.entity(ent).remove::<Visible>();
}
}
fn ui_on_show(
mut commands: Commands,
ent: Query<Entity, Added<Visible>>,
mut pen_position: MessageReader<PenPosition>,
) {
let Ok(ent) = ent.single() else {
return;
};
let Some(pos) = pen_position.read().next() else {
return;
};
commands.entity(ent).insert(UiPosition(**pos));
}
fn ui_on_hide(mut commands: Commands, mut removed: RemovedComponents<Visible>) {
let Some(ent) = removed.read().next() else {
return;
};
commands
.entity(ent)
.insert(UiPosition(Vec2::new(-1000., -1000.)));
}
fn color_picker(mut buffer: Query<(&mut DrawableLayer, &UiPosition), With<UiBuffer>>) {
let Ok((mut buffer, base_pos)) = buffer.single_mut() else {
return;
};
let w = buffer.width as f32;
let h = buffer.height as f32;
let x = (base_pos.x * w) as i32;
let y = (base_pos.y * h) as i32;
buffer.clear(Srgba::new(0.0, 0.0, 0.0, 0.0).into()).ok();
color_wheel(&mut buffer, x, y, 100);
buffer.draw_circle(x, y, 100, 5.0, css::BLACK.into());
buffer.draw_circle(x, y, 125, 3.0, css::BLACK.into());
}
#[allow(clippy::type_complexity)]
fn clear(
buffer: Query<
(&DrawableLayer, &UiPosition),
(With<UiBuffer>, With<Visible>, Without<DrawingBuffer>),
>,
mut draw_buffer: Query<&mut DrawableLayer, (With<DrawingBuffer>, Without<UiBuffer>)>,
mut pen_position: MessageReader<PenPosition>,
mut pen_buttons: MessageReader<PenButtons>,
) {
let Ok((buffer, base_pos)) = buffer.single() else {
return;
};
let Ok(mut draw_buffer) = draw_buffer.single_mut() else {
return;
};
let Some(pos) = pen_position.read().next() else {
return;
};
let Some(buttons) = pen_buttons.read().next() else {
return;
};
if !buttons.tip() {
return;
}
let w = buffer.width as f32;
let h = buffer.height as f32;
let ui_x = base_pos.x * w;
let ui_y = base_pos.y * h;
let pen_x = pos.x * w;
let pen_y = pos.y * h;
let delta = Vec2::new(pen_x - ui_x, pen_y - ui_y);
let radius = 100.;
let r_sq = radius * radius;
let dist_sq = delta.length_squared();
if dist_sq <= (r_sq * 1.5) {
return;
}
draw_buffer.clear(Srgba::rgba_u8(0, 0, 0, 0).into()).ok();
}
#[allow(clippy::type_complexity)]
fn color_changer(
buffer: Query<(&DrawableLayer, &UiPosition), (With<UiBuffer>, With<Visible>)>,
mut pen_color: ResMut<PenColor>,
mut pen_position: MessageReader<PenPosition>,
mut pen_buttons: MessageReader<PenButtons>,
) {
let Ok((buffer, base_pos)) = buffer.single() else {
return;
};
let Some(pos) = pen_position.read().next() else {
return;
};
let Some(buttons) = pen_buttons.read().next() else {
return;
};
if !buttons.tip() {
return;
}
let w = buffer.width as f32;
let h = buffer.height as f32;
let ui_x = base_pos.x * w;
let ui_y = base_pos.y * h;
let pen_x = pos.x * w;
let pen_y = pos.y * h;
let delta = Vec2::new(pen_x - ui_x, pen_y - ui_y);
let radius = 100.;
let r_sq = radius * radius;
let dist_sq = delta.length_squared();
if dist_sq > (r_sq * 1.5) {
return;
}
pen_color.0 = color_from_pos(delta.x, delta.y, radius);
}
fn color_wheel(buffer: &mut DrawableLayer, cx: i32, cy: i32, radius: i32) {
for dy in -radius..=radius {
for dx in -radius..=radius {
let x = cx + dx;
let y = cy + dy;
buffer.set_color_at(x, y, color_from_pos(dx as f32, dy as f32, radius as f32));
}
}
}
fn color_from_pos(x: f32, y: f32, radius: f32) -> Color {
let r_sq = radius * radius;
let dist_sq = x * x + y * y;
if dist_sq > r_sq {
return Color::srgba(0.0, 0.0, 0.0, 0.0);
}
// Distance normalized (0.0 → 1.0)
let dist = dist_sq.sqrt() / radius;
// Angle in radians → [0, 1]
let angle = y.atan2(x); // -PI..PI
let hue = (angle + std::f32::consts::PI) / (2.0 * std::f32::consts::PI);
let (r, g, b) = hsl_to_rgb(hue, 1.0, dist);
Color::srgba(r, g, b, 1.0)
}
fn hsl_to_rgb(h: f32, s: f32, l: f32) -> (f32, f32, f32) {
if s == 0.0 {
return (l, l, l);
}
fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
if t < 0.0 {
t += 1.0;
}
if t > 1.0 {
t -= 1.0;
}
if t < 1.0 / 6.0 {
return p + (q - p) * 6.0 * t;
}
if t < 1.0 / 2.0 {
return q;
}
if t < 2.0 / 3.0 {
return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
}
p
}
let q = if l < 0.5 {
l * (1.0 + s)
} else {
l + s - l * s
};
let p = 2.0 * l - q;
let r = hue_to_rgb(p, q, h + 1.0 / 3.0);
let g = hue_to_rgb(p, q, h);
let b = hue_to_rgb(p, q, h - 1.0 / 3.0);
(r, g, b)
}

View File

@@ -1,21 +1,22 @@
use std::f32::consts::PI;
use avian3d::{math::FRAC_PI_2, prelude::*};
use bevy::prelude::*;
use bevy_cef::prelude::*;
use bevy_mod_openxr::prelude::*;
use bevy_mod_xr::session::XrSessionCreated;
use bevy_pkv::{PersistentResourceAppExtensions, PkvStore};
use openxr::Path;
use serde::{Deserialize, Serialize};
use std::f32::consts::{FRAC_PI_2, PI};
use crate::{
MainCamera,
drawingplugin::{self, MyProcGenImage},
vrcontrollerplugin::{
LeftController, LeftControllerActions, RightController, RightControllerActions,
},
vrplugin::{Headset, MainCamera, create_view_space},
vrplugin::Headset,
};
// use bevy_mod_xr::session::XrSessionCreated;
#[derive(Component)]
pub struct LookedAt;
@@ -41,7 +42,6 @@ pub struct KneeboardPlugin;
impl Plugin for KneeboardPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(PhysicsPlugins::default());
app.add_plugins(CefPlugin {
command_line_config: CommandLineConfig {
switches: ["--no-zygote", "--no-sandbox"].to_vec(),
@@ -51,7 +51,7 @@ impl Plugin for KneeboardPlugin {
});
app.insert_resource(PkvStore::new("Avii", "Kneeboard"))
.init_persistent_resource::<KneeboardPosition>();
app.add_systems(XrSessionCreated, spawn_kneeboard.after(create_view_space));
app.add_systems(Startup, spawn_kneeboard.after(drawingplugin::setup));
app.add_systems(Update, gaze.run_if(openxr_session_running));
app.add_systems(Update, move_keyboard.run_if(openxr_session_running));
app.add_systems(Update, position_kneeboard.after(move_keyboard));
@@ -108,7 +108,9 @@ fn position_kneeboard(
return;
};
let head = head.single().expect("a head to exist");
let Ok(head) = head.single() else {
return;
};
transform.translation = kneeboard.position;
transform.rotation = kneeboard.rotation;
@@ -160,21 +162,22 @@ fn spawn_kneeboard(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<WebviewExtendStandardMaterial>>,
image_handle: Res<MyProcGenImage>,
position: Res<KneeboardPosition>,
) {
commands.spawn((
RigidBody::Static,
WebviewSource::new("http://localhost:7878/"),
// WebviewSource::new("http://localhost:7878/MOSRPRPETASPCLORWTSURDEP"),
WebviewSize(Vec2::new(210.0 * 3.5, 279.0 * 3.5)),
Collider::cuboid(0.210, 0.279, 0.01),
CollidingEntities::default(),
Mesh3d(meshes.add(Cuboid::new(0.210, 0.279, 0.01))),
MeshMaterial3d(materials.add(WebviewExtendStandardMaterial {
base: StandardMaterial {
unlit: true,
..default()
},
..Default::default()
extension: WebviewMaterial {
surface: None,
overlay: Some(image_handle.0.clone()),
},
})),
Transform::from_translation(position.position).rotate(position.rotation),
Kneeboard,

View File

@@ -1,25 +1,59 @@
//! A simple 3D scene with light shining over a cube sitting on a plane.
mod drawingplugin;
mod kneeboardplugin;
mod otdipcplugin;
#[allow(unused)]
mod vrcontrollerplugin;
#[allow(unused)]
mod vrplugin;
#[allow(unused)]
use vrcontrollerplugin::VrControllersPlugin;
#[allow(unused)]
use vrplugin::VrPlugin;
use bevy::prelude::*;
use bevy::{
prelude::*,
window::{PresentMode, WindowResolution},
};
use crate::{kneeboardplugin::KneeboardPlugin, vrcontrollerplugin::VrControllersPlugin};
use crate::{
drawingplugin::DrawingPlugin, kneeboardplugin::KneeboardPlugin, otdipcplugin::TabletRotation,
};
#[derive(Component)]
#[require(Camera3d)]
pub struct MainCamera;
fn main() {
App::new()
.insert_resource(ClearColor(Color::linear_rgb(1.0, 0.0, 1.0)))
.add_plugins(VrPlugin)
.add_plugins(VrControllersPlugin)
// .add_plugins(VrPlugin)
// .add_plugins(VrControllersPlugin)
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Kneeboard".into(),
resolution: WindowResolution::new(550, 720),
present_mode: PresentMode::AutoNoVsync,
fit_canvas_to_parent: true,
prevent_default_event_handling: false,
..default()
}),
..default()
}))
.add_systems(Startup, setup)
.add_plugins(DrawingPlugin)
.add_plugins(KneeboardPlugin)
.insert_resource(ClearColor(Color::NONE))
// .insert_resource(GlobalAmbientLight {
// brightness: 1000.0,
// ..GlobalAmbientLight::default()
// })
.insert_resource(TabletRotation::CW)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn((
Camera {
clear_color: ClearColorConfig::Custom(Color::linear_rgb(0.3, 0.3, 0.3)),
..default()
},
MainCamera,
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}

131
src/otdipcplugin.rs Normal file
View File

@@ -0,0 +1,131 @@
use bevy::{
app::{App, Plugin, PreUpdate},
ecs::{
message::MessageWriter,
resource::Resource,
system::{Res, ResMut},
},
math::Vec2,
prelude::Deref,
};
use otd_ipc::{Message, OtdIpc};
use triple_buffer::{Output, triple_buffer};
#[derive(Resource)]
struct OtdChannel(Output<Option<Message>>);
#[derive(Resource)]
struct TabletSize(Option<Vec2>);
#[derive(Resource)]
struct MaxPenPressure(Option<u32>);
#[derive(bevy::ecs::message::Message, Deref)]
pub struct PenPosition(Vec2);
#[derive(bevy::ecs::message::Message, Deref)]
pub struct PenButtons(u32);
impl PenButtons {
pub fn tip(&self) -> bool {
self.0 & 1 == 1
}
pub fn a(&self) -> bool {
self.0 & 2 == 2
}
pub fn b(&self) -> bool {
self.0 & 4 == 4
}
}
#[derive(bevy::ecs::message::Message)]
pub struct PenPressure {
pub pressure: f32,
}
#[allow(clippy::upper_case_acronyms)]
#[allow(unused)]
#[derive(Resource)]
pub enum TabletRotation {
Default,
CW,
UpsideDown,
CCW,
}
pub struct OtdIpcPlugin;
impl Plugin for OtdIpcPlugin {
fn build(&self, app: &mut App) {
let (mut tx, rx) = triple_buffer(&None);
std::thread::spawn(move || {
let otd_ipc = OtdIpc::new("Kneeboard", "master").unwrap();
for msg in otd_ipc {
tx.write(Some(msg));
}
});
app.add_message::<PenPosition>();
app.add_message::<PenPressure>();
app.add_message::<PenButtons>();
app.insert_resource(TabletRotation::Default);
app.insert_resource(OtdChannel(rx));
app.insert_resource(MaxPenPressure(None));
app.insert_resource(TabletSize(None));
app.add_systems(PreUpdate, reader);
}
}
#[allow(clippy::too_many_arguments)]
fn reader(
rotation: Res<TabletRotation>,
mut channel: ResMut<OtdChannel>,
mut size: ResMut<TabletSize>,
mut pressure: ResMut<MaxPenPressure>,
mut pressure_writer: MessageWriter<PenPressure>,
mut position_writer: MessageWriter<PenPosition>,
mut button_writer: MessageWriter<PenButtons>,
) {
let Some(msg) = channel.0.read() else {
return;
};
match msg {
otd_ipc::Message::DeviceInfo(info) => {
size.0 = Some(Vec2::new(info.max_x, info.max_y));
pressure.0 = Some(info.max_pressure);
}
otd_ipc::Message::State(state) => {
let Some(size) = size.0 else {
return;
};
let Some(pressure) = pressure.0 else {
return;
};
button_writer.write(PenButtons(state.pen_buttons()));
let p = state.pressure() as f32 / pressure as f32;
pressure_writer.write(PenPressure { pressure: p });
let x = state.x();
let y = state.y();
let loc = match *rotation {
TabletRotation::Default => Vec2::new(x / size.x, y / size.y),
TabletRotation::CCW => Vec2::new(y / size.y, (size.x - x) / size.x),
TabletRotation::CW => Vec2::new((size.y - y) / size.y, x / size.x),
TabletRotation::UpsideDown => {
Vec2::new((size.x - x) / size.x, (size.y - y) / size.y)
}
};
position_writer.write(PenPosition(loc));
}
_ => {}
}
}

View File

@@ -46,11 +46,17 @@ impl Plugin for VrControllersPlugin {
.run_if(openxr_session_running),
);
app.add_systems(OxrSendActionBindings, suggest_action_bindings_left);
app.add_systems(OxrSendActionBindings, suggest_action_bindings_right);
app.add_systems(Startup, create_actions_left.run_if(session_available));
app.add_systems(Startup, create_actions_right.run_if(session_available));
app.add_systems(
OxrSendActionBindings,
suggest_action_bindings_left.after(create_actions_left),
);
app.add_systems(
OxrSendActionBindings,
suggest_action_bindings_right.after(create_actions_right),
);
}
}

View File

@@ -1,11 +1,11 @@
use bevy::prelude::*;
use bevy::{
prelude::*,
render::render_resource::TextureFormat,
window::{PresentMode, WindowResolution},
};
use bevy_mod_openxr::prelude::*;
use bevy_mod_xr::session::XrSessionCreated;
#[derive(Component)]
#[require(Camera3d)]
pub struct MainCamera;
#[derive(Component)]
pub struct Headset;
@@ -14,7 +14,17 @@ pub struct VrPlugin;
impl Plugin for VrPlugin {
fn build(&self, app: &mut bevy::app::App) {
app.add_plugins(
add_xr_plugins(DefaultPlugins)
add_xr_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Kneeboard".into(),
resolution: WindowResolution::new(550, 720),
present_mode: PresentMode::AutoNoVsync,
fit_canvas_to_parent: true,
prevent_default_event_handling: false,
..default()
}),
..default()
}))
.disable::<HandTrackingPlugin>()
.build()
.set(OxrInitPlugin {
@@ -28,18 +38,12 @@ impl Plugin for VrPlugin {
);
app.insert_resource(OxrSessionConfig {
blend_mode_preference: {
vec![
EnvironmentBlendMode::ALPHA_BLEND,
EnvironmentBlendMode::ADDITIVE,
EnvironmentBlendMode::OPAQUE,
]
},
..OxrSessionConfig::default()
blend_mode_preference: { vec![EnvironmentBlendMode::ALPHA_BLEND] },
formats: Some(vec![TextureFormat::Rgba8UnormSrgb]),
..Default::default()
});
app.add_systems(XrSessionCreated, create_view_space);
// .add_systems(Update, sync_head_with_camera.run_if(openxr_session_running));
}
}
@@ -49,21 +53,4 @@ pub fn create_view_space(session: Res<OxrSession>, mut commands: Commands) {
.unwrap();
commands.spawn((Headset, space.0));
commands.spawn((
Camera {
clear_color: ClearColorConfig::Custom(Color::linear_rgb(1.0, 0.0, 1.0)),
..default()
},
MainCamera,
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}
// fn sync_head_with_camera(
// heads: Query<&Transform, With<Headset>>,
// cameras: Query<&mut Transform, (With<MainCamera>, Without<Headset>)>,
// ) {
// for mut camera in cameras {
// *camera = *heads.single().unwrap();
// }
// }