forked from StrafesNET/strafe-project
Compare commits
152 Commits
load-textu
...
timers
Author | SHA1 | Date | |
---|---|---|---|
64e1e762a1 | |||
ad862ae8c9 | |||
0ee17ac3d9 | |||
e2af6fc4ed | |||
bdc0dd1b3b | |||
95fb316a23 | |||
9dec53d764 | |||
3552491a9a | |||
dd13a066d0 | |||
f3dd43b171 | |||
82d71df94e | |||
684dbda73a | |||
e398da3aa6 | |||
944393dabe | |||
4adce7acd3 | |||
5b935c32fe | |||
436706bc4d | |||
bde24d35a2 | |||
fc91d644e6 | |||
2b47827383 | |||
a942e10554 | |||
5d1e38c36c | |||
e78cabf0f5 | |||
4e90da2228 | |||
9fa4ea6716 | |||
aedef03e7c | |||
6a9af0441f | |||
8cf66f3446 | |||
1cb0d6e586 | |||
12a4bf7948 | |||
f2e4286a08 | |||
bd6cd5eacc | |||
f2dfb438d0 | |||
7c8bc8d647 | |||
4943bc6a7f | |||
55eebba1c5 | |||
b8f13539db | |||
fb2e2afeb9 | |||
f30f246e5f | |||
0ac49308a0 | |||
30cbbbca1b | |||
66fa8fd637 | |||
f2c71caae3 | |||
c8ec1f05d1 | |||
b102319b33 | |||
50e9152ee2 | |||
7a8de938af | |||
696f383aee | |||
bfd6f4493f | |||
ed96572a24 | |||
5914db3599 | |||
f72acaf2d4 | |||
734ce661f2 | |||
bb8c53aee2 | |||
de0eb0790a | |||
9e9550885f | |||
58be446297 | |||
d16404167b | |||
79262ce3b4 | |||
c47020c149 | |||
5854171619 | |||
616b09a857 | |||
6ff2620bbc | |||
d3e4918d3e | |||
6c2eb5ff29 | |||
02a509868a | |||
af750151f7 | |||
bf4560193d | |||
514c45fc21 | |||
95d16271de | |||
355d391ea5 | |||
d8c6444af3 | |||
fddd4576bd | |||
c7538869b4 | |||
923889d956 | |||
215ac47fcb | |||
d86aed5ae1 | |||
92bbbce1c3 | |||
5cd40afa56 | |||
602816a618 | |||
d7010956b3 | |||
b3f7802046 | |||
977c8e565c | |||
4ee29911a3 | |||
9ce9eb50be | |||
ccc94839e5 | |||
c85a84a52e | |||
2df76f020b | |||
7e3bfeb59e | |||
402def667f | |||
d4835187a8 | |||
f36b681614 | |||
a618f305e1 | |||
575d343276 | |||
ac4ba19ed3 | |||
ed712933e5 | |||
665a83d174 | |||
ba21ce262a | |||
5b770fc8a9 | |||
23a1a8690b | |||
60be7f14e5 | |||
37e9299f7d | |||
099865c682 | |||
c65354c23f | |||
1b29db0daf | |||
aa3e717f36 | |||
a06a28c595 | |||
0d6e989812 | |||
da3d0ca254 | |||
e685ef7388 | |||
b5c689f8ff | |||
0913063a00 | |||
f492a09377 | |||
b404908a55 | |||
f0d9c219b6 | |||
8fc87a59ce | |||
f0b3e87abb | |||
836749df47 | |||
31156aadfb | |||
ff7b12e90e | |||
e70dc9ad0f | |||
c5deef8753 | |||
7c2666fdf5 | |||
6da4c81826 | |||
c868a91a06 | |||
b513e4037d | |||
a803ada0e4 | |||
eafcbae677 | |||
2e786b090f | |||
70e8f7a0ad | |||
48091fc15d | |||
23857d38d9 | |||
1c9bc347f6 | |||
c9afa2d059 | |||
1a66dfbaf7 | |||
847209aac4 | |||
42ba757ec0 | |||
1cee3b52ac | |||
e27ce3b507 | |||
bc8f2bd566 | |||
eed932212d | |||
73edb9ff95 | |||
ae0c9e73ee | |||
953d424a57 | |||
ca919b92fd | |||
1de3501e89 | |||
0135b17917 | |||
1878528a4f | |||
25e80a7c17 | |||
21835d13f6 | |||
91f6a5261f | |||
fb4a5efa14 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/textures
|
312
Cargo.lock
generated
312
Cargo.lock
generated
@ -157,6 +157,12 @@ dependencies = [
|
|||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@ -184,12 +190,35 @@ version = "2.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake3"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"constant_time_eq",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-sys"
|
name = "block-sys"
|
||||||
version = "0.1.0-beta.1"
|
version = "0.1.0-beta.1"
|
||||||
@ -302,6 +331,18 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "configparser"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@ -360,6 +401,16 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "d3d12"
|
name = "d3d12"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -382,6 +433,17 @@ dependencies = [
|
|||||||
"enum_primitive",
|
"enum_primitive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dispatch"
|
name = "dispatch"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -555,6 +617,16 @@ dependencies = [
|
|||||||
"waker-fn",
|
"waker-fn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
@ -768,6 +840,29 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e723bd417b2df60a0f6a2b6825f297ea04b245d4ba52b5a22cb679bdf58b05fa"
|
||||||
|
dependencies = [
|
||||||
|
"lazy-regex-proc_macros",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex-proc_macros"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f0a1d9139f0ee2e862e08a9c5d0ba0470f2aa21cd1e1aa1b1562f83116c725f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -822,6 +917,26 @@ version = "0.4.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lz4"
|
||||||
|
version = "1.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"lz4-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lz4-sys"
|
||||||
|
version = "1.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "malloc_buf"
|
name = "malloc_buf"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
@ -1198,6 +1313,12 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -1222,6 +1343,19 @@ name = "profiling"
|
|||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46b2164ebdb1dfeec5e337be164292351e11daf63a05174c6776b2f47460f0c9"
|
checksum = "46b2164ebdb1dfeec5e337be164292351e11daf63a05174c6776b2f47460f0c9"
|
||||||
|
dependencies = [
|
||||||
|
"profiling-procmacros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "profiling-procmacros"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74c55e9e629af5298a40e0fa106435b2da30484c4ec76b41d19bc4d00dd8b903"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
@ -1232,6 +1366,36 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "range-alloc"
|
name = "range-alloc"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -1244,6 +1408,83 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbx_binary"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e50573021d04b680018955662eba7dc4aac3de92219231798f6c9b41e38ab01"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"lz4",
|
||||||
|
"profiling",
|
||||||
|
"rbx_dom_weak",
|
||||||
|
"rbx_reflection",
|
||||||
|
"rbx_reflection_database",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbx_dom_weak"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "161729449bbb0cfa657ce7bcca6a160d0af06d8b8d9efdc9abe14735dccacdb9"
|
||||||
|
dependencies = [
|
||||||
|
"rbx_types",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbx_reflection"
|
||||||
|
version = "4.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08bd48487192046fec8f805f3fa29f3d7d5beb9890b0859b1a92bd8aff580343"
|
||||||
|
dependencies = [
|
||||||
|
"rbx_types",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbx_reflection_database"
|
||||||
|
version = "0.2.7+roblox-588"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1be6cf674182806f11ad4899dd1feafe977591f1ae035ae05a58d4b74e487276"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"rbx_reflection",
|
||||||
|
"rmp-serde",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbx_types"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "070106e926b8ae54c7bc443e5db4d868d7f0af51c1d7cfd7efe1364c1753d8a3"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"blake3",
|
||||||
|
"lazy_static",
|
||||||
|
"rand",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbx_xml"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bc65b70827519fdc4ab47416d1085b912f087fadab9ed415471b6daba635574"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"log",
|
||||||
|
"rbx_dom_weak",
|
||||||
|
"rbx_reflection",
|
||||||
|
"rbx_reflection_database",
|
||||||
|
"xml-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -1255,9 +1496,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.9.4"
|
version = "1.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
|
checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1267,9 +1508,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.3.7"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
|
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1288,6 +1529,28 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
|
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"num-traits 0.2.16",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp-serde"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"rmp",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
@ -1338,6 +1601,26 @@ dependencies = [
|
|||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.188"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.188"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@ -1405,16 +1688,23 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strafe-client"
|
name = "strafe-client"
|
||||||
version = "0.3.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-executor",
|
"async-executor",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
|
"configparser",
|
||||||
"ddsfile",
|
"ddsfile",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"glam",
|
"glam",
|
||||||
|
"lazy-regex",
|
||||||
"log",
|
"log",
|
||||||
"obj",
|
"obj",
|
||||||
|
"parking_lot",
|
||||||
"pollster",
|
"pollster",
|
||||||
|
"rbx_binary",
|
||||||
|
"rbx_dom_weak",
|
||||||
|
"rbx_reflection_database",
|
||||||
|
"rbx_xml",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
@ -1425,6 +1715,12 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@ -1524,6 +1820,12 @@ version = "0.19.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a464a4b34948a5f67fddd2b823c62d9d92e44be75058b99939eae6c5b6960b33"
|
checksum = "a464a4b34948a5f67fddd2b823c62d9d92e44be75058b99939eae6c5b6960b33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
17
Cargo.toml
17
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "strafe-client"
|
name = "strafe-client"
|
||||||
version = "0.3.0"
|
version = "0.8.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@ -8,16 +8,23 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-executor = "1.5.1"
|
async-executor = "1.5.1"
|
||||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||||
|
configparser = "3.0.2"
|
||||||
ddsfile = "0.5.1"
|
ddsfile = "0.5.1"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
glam = "0.24.1"
|
glam = "0.24.1"
|
||||||
|
lazy-regex = "3.0.2"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
obj = "0.10.2"
|
obj = "0.10.2"
|
||||||
|
parking_lot = "0.12.1"
|
||||||
pollster = "0.3.0"
|
pollster = "0.3.0"
|
||||||
|
rbx_binary = "0.7.1"
|
||||||
|
rbx_dom_weak = "2.5.0"
|
||||||
|
rbx_reflection_database = "0.2.7"
|
||||||
|
rbx_xml = "0.13.1"
|
||||||
wgpu = "0.17.0"
|
wgpu = "0.17.0"
|
||||||
winit = "0.28.6"
|
winit = "0.28.6"
|
||||||
|
|
||||||
[profile.release]
|
#[profile.release]
|
||||||
lto = true
|
#lto = true
|
||||||
strip = true
|
#strip = true
|
||||||
codegen-units = 1
|
#codegen-units = 1
|
||||||
|
91
src/aabb.rs
Normal file
91
src/aabb.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||||
|
pub enum AabbFace{
|
||||||
|
Right,//+X
|
||||||
|
Top,
|
||||||
|
Back,
|
||||||
|
Left,
|
||||||
|
Bottom,
|
||||||
|
Front,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Aabb {
|
||||||
|
pub min: glam::Vec3,
|
||||||
|
pub max: glam::Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Aabb {
|
||||||
|
fn default() -> Self {
|
||||||
|
Aabb::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aabb {
|
||||||
|
const VERTEX_DATA: [glam::Vec3; 8] = [
|
||||||
|
glam::vec3(1., -1., -1.),
|
||||||
|
glam::vec3(1., 1., -1.),
|
||||||
|
glam::vec3(1., 1., 1.),
|
||||||
|
glam::vec3(1., -1., 1.),
|
||||||
|
glam::vec3(-1., -1., 1.),
|
||||||
|
glam::vec3(-1., 1., 1.),
|
||||||
|
glam::vec3(-1., 1., -1.),
|
||||||
|
glam::vec3(-1., -1., -1.),
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {min: glam::Vec3::INFINITY,max: glam::Vec3::NEG_INFINITY}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(&mut self, point:glam::Vec3){
|
||||||
|
self.min=self.min.min(point);
|
||||||
|
self.max=self.max.max(point);
|
||||||
|
}
|
||||||
|
pub fn join(&mut self, aabb:&Aabb){
|
||||||
|
self.min=self.min.min(aabb.min);
|
||||||
|
self.max=self.max.max(aabb.max);
|
||||||
|
}
|
||||||
|
pub fn inflate(&mut self, hs:glam::Vec3){
|
||||||
|
self.min-=hs;
|
||||||
|
self.max+=hs;
|
||||||
|
}
|
||||||
|
pub fn intersects(&self,aabb:&Aabb)->bool{
|
||||||
|
(self.min.cmplt(aabb.max)&aabb.min.cmplt(self.max)).all()
|
||||||
|
}
|
||||||
|
pub fn normal(face:AabbFace) -> glam::Vec3 {
|
||||||
|
match face {
|
||||||
|
AabbFace::Right => glam::vec3(1.,0.,0.),
|
||||||
|
AabbFace::Top => glam::vec3(0.,1.,0.),
|
||||||
|
AabbFace::Back => glam::vec3(0.,0.,1.),
|
||||||
|
AabbFace::Left => glam::vec3(-1.,0.,0.),
|
||||||
|
AabbFace::Bottom => glam::vec3(0.,-1.,0.),
|
||||||
|
AabbFace::Front => glam::vec3(0.,0.,-1.),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn unit_vertices() -> [glam::Vec3;8] {
|
||||||
|
return Self::VERTEX_DATA;
|
||||||
|
}
|
||||||
|
pub fn face(&self,face:AabbFace) -> Aabb {
|
||||||
|
let mut aabb=self.clone();
|
||||||
|
//in this implementation face = worldspace aabb face
|
||||||
|
match face {
|
||||||
|
AabbFace::Right => aabb.min.x=aabb.max.x,
|
||||||
|
AabbFace::Top => aabb.min.y=aabb.max.y,
|
||||||
|
AabbFace::Back => aabb.min.z=aabb.max.z,
|
||||||
|
AabbFace::Left => aabb.max.x=aabb.min.x,
|
||||||
|
AabbFace::Bottom => aabb.max.y=aabb.min.y,
|
||||||
|
AabbFace::Front => aabb.max.z=aabb.min.z,
|
||||||
|
}
|
||||||
|
return aabb;
|
||||||
|
}
|
||||||
|
pub fn center(&self)->glam::Vec3{
|
||||||
|
return (self.min+self.max)/2.0
|
||||||
|
}
|
||||||
|
//probably use floats for area & volume because we don't care about precision
|
||||||
|
pub fn area_weight(&self)->f32{
|
||||||
|
let d=self.max-self.min;
|
||||||
|
d.x*d.y+d.y*d.z+d.z*d.x
|
||||||
|
}
|
||||||
|
pub fn volume(&self)->f32{
|
||||||
|
let d=self.max-self.min;
|
||||||
|
d.x*d.y*d.z
|
||||||
|
}
|
||||||
|
}
|
857
src/body.rs
857
src/body.rs
@ -1,857 +0,0 @@
|
|||||||
use crate::{instruction::{InstructionEmitter, InstructionConsumer, TimedInstruction}, zeroes::zeroes2};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PhysicsInstruction {
|
|
||||||
CollisionStart(RelativeCollision),
|
|
||||||
CollisionEnd(RelativeCollision),
|
|
||||||
SetControlDir(glam::Vec3),
|
|
||||||
StrafeTick,
|
|
||||||
Jump,
|
|
||||||
SetWalkTargetVelocity(glam::Vec3),
|
|
||||||
RefreshWalkTarget,
|
|
||||||
ReachWalkTargetVelocity,
|
|
||||||
// Water,
|
|
||||||
// Spawn(
|
|
||||||
// Option<SpawnId>,
|
|
||||||
// bool,//true = Trigger; false = teleport
|
|
||||||
// bool,//true = Force
|
|
||||||
// )
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Body {
|
|
||||||
position: glam::Vec3,//I64 where 2^32 = 1 u
|
|
||||||
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
|
|
||||||
acceleration: glam::Vec3,//I64 where 2^32 = 1 u/s/s
|
|
||||||
time: TIME,//nanoseconds x xxxxD!
|
|
||||||
}
|
|
||||||
trait MyHash{
|
|
||||||
fn hash(&self) -> u64;
|
|
||||||
}
|
|
||||||
impl MyHash for Body {
|
|
||||||
fn hash(&self) -> u64 {
|
|
||||||
let mut hasher=std::collections::hash_map::DefaultHasher::new();
|
|
||||||
for &el in self.position.as_ref().iter() {
|
|
||||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
|
||||||
}
|
|
||||||
for &el in self.velocity.as_ref().iter() {
|
|
||||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
|
||||||
}
|
|
||||||
for &el in self.acceleration.as_ref().iter() {
|
|
||||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
|
||||||
}
|
|
||||||
std::hash::Hasher::write(&mut hasher, self.time.to_ne_bytes().as_slice());
|
|
||||||
return std::hash::Hasher::finish(&hasher);//hash check to see if walk target is valid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MoveRestriction {
|
|
||||||
Air,
|
|
||||||
Water,
|
|
||||||
Ground,
|
|
||||||
Ladder,//multiple ladders how
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MouseInterpolation {
|
|
||||||
First,//just checks the last value
|
|
||||||
Lerp,//lerps between
|
|
||||||
}
|
|
||||||
|
|
||||||
enum InputInstruction {
|
|
||||||
MoveMouse(glam::IVec2),
|
|
||||||
Jump(bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InputState {
|
|
||||||
controls: u32,
|
|
||||||
mouse_interpolation: MouseInterpolation,
|
|
||||||
time: TIME,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputState {
|
|
||||||
pub fn get_control(&self,control:u32) -> bool {
|
|
||||||
self.controls&control!=0
|
|
||||||
}
|
|
||||||
pub fn process_instruction(&mut self,ins:InputInstruction){
|
|
||||||
match ins {
|
|
||||||
InputInstruction::MoveMouse(m) => todo!("set mouse_interpolation"),
|
|
||||||
InputInstruction::Jump(b) => todo!("how does info about style modifiers get here"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MouseInterpolationState {
|
|
||||||
interpolation: MouseInterpolation,
|
|
||||||
time0: TIME,
|
|
||||||
time1: TIME,
|
|
||||||
mouse0: glam::IVec2,
|
|
||||||
mouse1: glam::IVec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MouseInterpolationState {
|
|
||||||
pub fn move_mouse(&mut self,time:TIME,pos:glam::IVec2){
|
|
||||||
self.time0=self.time1;
|
|
||||||
self.mouse0=self.mouse1;
|
|
||||||
self.time1=time;
|
|
||||||
self.mouse1=pos;
|
|
||||||
}
|
|
||||||
pub fn interpolated_position(&self,time:TIME) -> glam::IVec2 {
|
|
||||||
match self.interpolation {
|
|
||||||
MouseInterpolation::First => self.mouse0,
|
|
||||||
MouseInterpolation::Lerp => {
|
|
||||||
let m0=self.mouse0.as_i64vec2();
|
|
||||||
let m1=self.mouse1.as_i64vec2();
|
|
||||||
//these are deltas
|
|
||||||
let t1t=(self.time1-time) as i64;
|
|
||||||
let tt0=(time-self.time0) as i64;
|
|
||||||
let dt=(self.time1-self.time0) as i64;
|
|
||||||
((m0*t1t+m1*tt0)/dt).as_ivec2()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum WalkEnum{
|
|
||||||
Reached,
|
|
||||||
Transient,
|
|
||||||
Invalid,
|
|
||||||
}
|
|
||||||
pub struct WalkState {
|
|
||||||
pub target_velocity: glam::Vec3,
|
|
||||||
pub target_time: TIME,
|
|
||||||
pub state: WalkEnum,
|
|
||||||
}
|
|
||||||
impl WalkState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self{
|
|
||||||
target_velocity:glam::Vec3::ZERO,
|
|
||||||
target_time:0,
|
|
||||||
state:WalkEnum::Invalid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PhysicsState {
|
|
||||||
pub body: Body,
|
|
||||||
pub hitbox_halfsize: glam::Vec3,
|
|
||||||
pub contacts: std::collections::HashSet::<RelativeCollision>,
|
|
||||||
//pub intersections: Vec<ModelId>,
|
|
||||||
//temp
|
|
||||||
pub models_cringe_clone: Vec<Model>,
|
|
||||||
pub temp_control_dir: glam::Vec3,
|
|
||||||
//camera must exist in state because wormholes modify the camera, also camera punch
|
|
||||||
//pub camera: Camera,
|
|
||||||
//pub mouse_interpolation: MouseInterpolationState,
|
|
||||||
pub time: TIME,
|
|
||||||
pub strafe_tick_num: TIME,
|
|
||||||
pub strafe_tick_den: TIME,
|
|
||||||
pub tick: u32,
|
|
||||||
pub mv: f32,
|
|
||||||
pub walk: WalkState,
|
|
||||||
pub walkspeed: f32,
|
|
||||||
pub friction: f32,
|
|
||||||
pub walk_accel: f32,
|
|
||||||
pub gravity: glam::Vec3,
|
|
||||||
pub grounded: bool,
|
|
||||||
pub jump_trying: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
|
||||||
pub enum AabbFace{
|
|
||||||
Right,//+X
|
|
||||||
Top,
|
|
||||||
Back,
|
|
||||||
Left,
|
|
||||||
Bottom,
|
|
||||||
Front,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Aabb {
|
|
||||||
min: glam::Vec3,
|
|
||||||
max: glam::Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Aabb {
|
|
||||||
// const FACE_DATA: [[f32; 3]; 6] = [
|
|
||||||
// [0.0f32, 0., 1.],
|
|
||||||
// [0.0f32, 0., -1.],
|
|
||||||
// [1.0f32, 0., 0.],
|
|
||||||
// [-1.0f32, 0., 0.],
|
|
||||||
// [0.0f32, 1., 0.],
|
|
||||||
// [0.0f32, -1., 0.],
|
|
||||||
// ];
|
|
||||||
const VERTEX_DATA: [glam::Vec3; 8] = [
|
|
||||||
glam::vec3(1., -1., -1.),
|
|
||||||
glam::vec3(1., 1., -1.),
|
|
||||||
glam::vec3(1., 1., 1.),
|
|
||||||
glam::vec3(1., -1., 1.),
|
|
||||||
glam::vec3(-1., -1., 1.),
|
|
||||||
glam::vec3(-1., 1., 1.),
|
|
||||||
glam::vec3(-1., 1., -1.),
|
|
||||||
glam::vec3(-1., -1., -1.),
|
|
||||||
];
|
|
||||||
const VERTEX_DATA_RIGHT: [glam::Vec3; 4] = [
|
|
||||||
glam::vec3(1., -1., -1.),
|
|
||||||
glam::vec3(1., 1., -1.),
|
|
||||||
glam::vec3(1., 1., 1.),
|
|
||||||
glam::vec3(1., -1., 1.),
|
|
||||||
];
|
|
||||||
const VERTEX_DATA_TOP: [glam::Vec3; 4] = [
|
|
||||||
glam::vec3(1., 1., -1.),
|
|
||||||
glam::vec3(-1., 1., -1.),
|
|
||||||
glam::vec3(-1., 1., 1.),
|
|
||||||
glam::vec3(1., 1., 1.),
|
|
||||||
];
|
|
||||||
const VERTEX_DATA_BACK: [glam::Vec3; 4] = [
|
|
||||||
glam::vec3(-1., -1., 1.),
|
|
||||||
glam::vec3(1., -1., 1.),
|
|
||||||
glam::vec3(1., 1., 1.),
|
|
||||||
glam::vec3(-1., 1., 1.),
|
|
||||||
];
|
|
||||||
const VERTEX_DATA_LEFT: [glam::Vec3; 4] = [
|
|
||||||
glam::vec3(-1., -1., 1.),
|
|
||||||
glam::vec3(-1., 1., 1.),
|
|
||||||
glam::vec3(-1., 1., -1.),
|
|
||||||
glam::vec3(-1., -1., -1.),
|
|
||||||
];
|
|
||||||
const VERTEX_DATA_BOTTOM: [glam::Vec3; 4] = [
|
|
||||||
glam::vec3(1., -1., 1.),
|
|
||||||
glam::vec3(-1., -1., 1.),
|
|
||||||
glam::vec3(-1., -1., -1.),
|
|
||||||
glam::vec3(1., -1., -1.),
|
|
||||||
];
|
|
||||||
const VERTEX_DATA_FRONT: [glam::Vec3; 4] = [
|
|
||||||
glam::vec3(-1., 1., -1.),
|
|
||||||
glam::vec3(1., 1., -1.),
|
|
||||||
glam::vec3(1., -1., -1.),
|
|
||||||
glam::vec3(-1., -1., -1.),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {min: glam::Vec3::INFINITY,max: glam::Vec3::NEG_INFINITY}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn grow(&mut self, point:glam::Vec3){
|
|
||||||
self.min=self.min.min(point);
|
|
||||||
self.max=self.max.max(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn normal(face:AabbFace) -> glam::Vec3 {
|
|
||||||
match face {
|
|
||||||
AabbFace::Right => glam::vec3(1.,0.,0.),
|
|
||||||
AabbFace::Top => glam::vec3(0.,1.,0.),
|
|
||||||
AabbFace::Back => glam::vec3(0.,0.,1.),
|
|
||||||
AabbFace::Left => glam::vec3(-1.,0.,0.),
|
|
||||||
AabbFace::Bottom => glam::vec3(0.,-1.,0.),
|
|
||||||
AabbFace::Front => glam::vec3(0.,0.,-1.),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn unit_vertices() -> [glam::Vec3;8] {
|
|
||||||
return Self::VERTEX_DATA;
|
|
||||||
}
|
|
||||||
pub fn unit_face_vertices(face:AabbFace) -> [glam::Vec3;4] {
|
|
||||||
match face {
|
|
||||||
AabbFace::Right => Self::VERTEX_DATA_RIGHT,
|
|
||||||
AabbFace::Top => Self::VERTEX_DATA_TOP,
|
|
||||||
AabbFace::Back => Self::VERTEX_DATA_BACK,
|
|
||||||
AabbFace::Left => Self::VERTEX_DATA_LEFT,
|
|
||||||
AabbFace::Bottom => Self::VERTEX_DATA_BOTTOM,
|
|
||||||
AabbFace::Front => Self::VERTEX_DATA_FRONT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//pretend to be using what we want to eventually do
|
|
||||||
type TreyMeshFace = AabbFace;
|
|
||||||
type TreyMesh = Aabb;
|
|
||||||
|
|
||||||
pub struct Model {
|
|
||||||
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
|
|
||||||
//in this iteration, all it needs is extents.
|
|
||||||
transform: glam::Mat4,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Model {
|
|
||||||
pub fn new(transform:glam::Mat4) -> Self {
|
|
||||||
Self{transform}
|
|
||||||
}
|
|
||||||
pub fn unit_vertices(&self) -> [glam::Vec3;8] {
|
|
||||||
Aabb::unit_vertices()
|
|
||||||
}
|
|
||||||
pub fn mesh(&self) -> TreyMesh {
|
|
||||||
let mut aabb=Aabb::new();
|
|
||||||
for &vertex in self.unit_vertices().iter() {
|
|
||||||
aabb.grow(glam::Vec4Swizzles::xyz(self.transform*vertex.extend(1.0)));
|
|
||||||
}
|
|
||||||
return aabb;
|
|
||||||
}
|
|
||||||
pub fn unit_face_vertices(&self,face:TreyMeshFace) -> [glam::Vec3;4] {
|
|
||||||
Aabb::unit_face_vertices(face)
|
|
||||||
}
|
|
||||||
pub fn face_mesh(&self,face:TreyMeshFace) -> TreyMesh {
|
|
||||||
let mut aabb=self.mesh();
|
|
||||||
//in this implementation face = worldspace aabb face
|
|
||||||
match face {
|
|
||||||
AabbFace::Right => aabb.min.x=aabb.max.x,
|
|
||||||
AabbFace::Top => aabb.min.y=aabb.max.y,
|
|
||||||
AabbFace::Back => aabb.min.z=aabb.max.z,
|
|
||||||
AabbFace::Left => aabb.max.x=aabb.min.x,
|
|
||||||
AabbFace::Bottom => aabb.max.y=aabb.min.y,
|
|
||||||
AabbFace::Front => aabb.max.z=aabb.min.z,
|
|
||||||
}
|
|
||||||
return aabb;
|
|
||||||
}
|
|
||||||
pub fn face_normal(&self,face:TreyMeshFace) -> glam::Vec3 {
|
|
||||||
glam::Vec4Swizzles::xyz(Aabb::normal(face).extend(0.0))//this is wrong for scale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//need non-face (full model) variant for CanCollide false objects
|
|
||||||
//OR have a separate list from contacts for model intersection
|
|
||||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
|
||||||
pub struct RelativeCollision {
|
|
||||||
face: TreyMeshFace,//just an id
|
|
||||||
model: u32,//using id to avoid lifetimes
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RelativeCollision {
|
|
||||||
pub fn mesh(&self,models:&Vec<Model>) -> TreyMesh {
|
|
||||||
return models.get(self.model as usize).unwrap().face_mesh(self.face)
|
|
||||||
}
|
|
||||||
pub fn normal(&self,models:&Vec<Model>) -> glam::Vec3 {
|
|
||||||
return models.get(self.model as usize).unwrap().face_normal(self.face)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type TIME = i64;
|
|
||||||
|
|
||||||
impl Body {
|
|
||||||
pub fn with_pva(position:glam::Vec3,velocity:glam::Vec3,acceleration:glam::Vec3) -> Self {
|
|
||||||
Self{
|
|
||||||
position,
|
|
||||||
velocity,
|
|
||||||
acceleration,
|
|
||||||
time: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn extrapolated_position(&self,time: TIME)->glam::Vec3{
|
|
||||||
let dt=(time-self.time) as f64/1_000_000_000f64;
|
|
||||||
self.position+self.velocity*(dt as f32)+self.acceleration*((0.5*dt*dt) as f32)
|
|
||||||
}
|
|
||||||
pub fn extrapolated_velocity(&self,time: TIME)->glam::Vec3{
|
|
||||||
let dt=(time-self.time) as f64/1_000_000_000f64;
|
|
||||||
self.velocity+self.acceleration*(dt as f32)
|
|
||||||
}
|
|
||||||
pub fn advance_time(&mut self, time: TIME){
|
|
||||||
self.position=self.extrapolated_position(time);
|
|
||||||
self.velocity=self.extrapolated_velocity(time);
|
|
||||||
self.time=time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhysicsState {
|
|
||||||
//tickless gaming
|
|
||||||
pub fn run(&mut self, time_limit:TIME){
|
|
||||||
//prepare is ommitted - everything is done via instructions.
|
|
||||||
while let Some(instruction) = self.next_instruction(time_limit) {//collect
|
|
||||||
//advance
|
|
||||||
//self.advance_time(instruction.time);
|
|
||||||
//process
|
|
||||||
self.process_instruction(instruction);
|
|
||||||
//write hash lol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn advance_time(&mut self, time: TIME){
|
|
||||||
self.body.advance_time(time);
|
|
||||||
self.time=time;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contact_constrain_velocity(&self,velocity:&mut glam::Vec3){
|
|
||||||
for contact in self.contacts.iter() {
|
|
||||||
let n=contact.normal(&self.models_cringe_clone);
|
|
||||||
let d=velocity.dot(n);
|
|
||||||
if d<0f32{
|
|
||||||
(*velocity)-=d/n.length_squared()*n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn contact_constrain_acceleration(&self,acceleration:&mut glam::Vec3){
|
|
||||||
for contact in self.contacts.iter() {
|
|
||||||
let n=contact.normal(&self.models_cringe_clone);
|
|
||||||
let d=acceleration.dot(n);
|
|
||||||
if d<0f32{
|
|
||||||
(*acceleration)-=d/n.length_squared()*n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_strafe_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
return Some(TimedInstruction{
|
|
||||||
time:(self.time*self.strafe_tick_num/self.strafe_tick_den+1)*self.strafe_tick_den/self.strafe_tick_num,
|
|
||||||
//only poll the physics if there is a before and after mouse event
|
|
||||||
instruction:PhysicsInstruction::StrafeTick
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//state mutated on collision:
|
|
||||||
//Accelerator
|
|
||||||
//stair step-up
|
|
||||||
|
|
||||||
//state mutated on instruction
|
|
||||||
//change fly acceleration (fly_sustain)
|
|
||||||
//change fly velocity
|
|
||||||
|
|
||||||
//generic event emmiters
|
|
||||||
//PlatformStandTime
|
|
||||||
//walk/swim/air/ladder sounds
|
|
||||||
//VState?
|
|
||||||
|
|
||||||
//falling under the map
|
|
||||||
// fn next_respawn_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
// if self.body.position<self.world.min_y {
|
|
||||||
// return Some(TimedInstruction{
|
|
||||||
// time:self.time,
|
|
||||||
// instruction:PhysicsInstruction::Trigger(None)
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn next_water_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
// return Some(TimedInstruction{
|
|
||||||
// time:(self.time*self.strafe_tick_num/self.strafe_tick_den+1)*self.strafe_tick_den/self.strafe_tick_num,
|
|
||||||
// //only poll the physics if there is a before and after mouse event
|
|
||||||
// instruction:PhysicsInstruction::Water
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn next_walk_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
//check if you have a valid walk state and create an instruction
|
|
||||||
if self.grounded{
|
|
||||||
match self.walk.state{
|
|
||||||
WalkEnum::Transient=>Some(TimedInstruction{
|
|
||||||
time:self.walk.target_time,
|
|
||||||
instruction:PhysicsInstruction::ReachWalkTargetVelocity
|
|
||||||
}),
|
|
||||||
WalkEnum::Invalid=>Some(TimedInstruction{
|
|
||||||
time:self.time,
|
|
||||||
instruction:PhysicsInstruction::RefreshWalkTarget,
|
|
||||||
}),
|
|
||||||
WalkEnum::Reached=>None,
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn mesh(&self) -> TreyMesh {
|
|
||||||
let mut aabb=Aabb::new();
|
|
||||||
for vertex in Aabb::unit_vertices(){
|
|
||||||
aabb.grow(self.body.position+self.hitbox_halfsize*vertex);
|
|
||||||
}
|
|
||||||
aabb
|
|
||||||
}
|
|
||||||
fn predict_collision_end(&self,time:TIME,time_limit:TIME,collision_data:&RelativeCollision) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
//must treat cancollide false objects differently: you may not exit through the same face you entered.
|
|
||||||
//RelativeCollsion must reference the full model instead of a particular face
|
|
||||||
//this is Ctrl+C Ctrl+V of predict_collision_start but with v=-v before the calc and t=-t after the calc
|
|
||||||
//find best t
|
|
||||||
let mut best_time=time_limit;
|
|
||||||
let mut exit_face:Option<TreyMeshFace>=None;
|
|
||||||
let mesh0=self.mesh();
|
|
||||||
let mesh1=self.models_cringe_clone.get(collision_data.model as usize).unwrap().mesh();
|
|
||||||
let (v,a)=(-self.body.velocity,self.body.acceleration);
|
|
||||||
//collect x
|
|
||||||
match collision_data.face {
|
|
||||||
AabbFace::Top|AabbFace::Back|AabbFace::Bottom|AabbFace::Front=>{
|
|
||||||
for t in zeroes2(mesh0.max.x-mesh1.min.x,v.x,0.5*a.x) {
|
|
||||||
//negative t = back in time
|
|
||||||
//must be moving towards surface to collide
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&0f32<v.x+a.x*-t{
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
exit_face=Some(TreyMeshFace::Left);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for t in zeroes2(mesh0.min.x-mesh1.max.x,v.x,0.5*a.x) {
|
|
||||||
//negative t = back in time
|
|
||||||
//must be moving towards surface to collide
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&v.x+a.x*-t<0f32{
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
exit_face=Some(TreyMeshFace::Right);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AabbFace::Left=>{
|
|
||||||
//generate event if v.x<0||a.x<0
|
|
||||||
if -v.x<0f32{
|
|
||||||
best_time=time;
|
|
||||||
exit_face=Some(TreyMeshFace::Left);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AabbFace::Right=>{
|
|
||||||
//generate event if 0<v.x||0<a.x
|
|
||||||
if 0f32<(-v.x){
|
|
||||||
best_time=time;
|
|
||||||
exit_face=Some(TreyMeshFace::Right);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
//collect y
|
|
||||||
match collision_data.face {
|
|
||||||
AabbFace::Left|AabbFace::Back|AabbFace::Right|AabbFace::Front=>{
|
|
||||||
for t in zeroes2(mesh0.max.y-mesh1.min.y,v.y,0.5*a.y) {
|
|
||||||
//negative t = back in time
|
|
||||||
//must be moving towards surface to collide
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&0f32<v.y+a.y*-t{
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
exit_face=Some(TreyMeshFace::Bottom);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for t in zeroes2(mesh0.min.y-mesh1.max.y,v.y,0.5*a.y) {
|
|
||||||
//negative t = back in time
|
|
||||||
//must be moving towards surface to collide
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&v.y+a.y*-t<0f32{
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
exit_face=Some(TreyMeshFace::Top);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AabbFace::Bottom=>{
|
|
||||||
//generate event if v.y<0||a.y<0
|
|
||||||
if -v.y<0f32{
|
|
||||||
best_time=time;
|
|
||||||
exit_face=Some(TreyMeshFace::Bottom);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AabbFace::Top=>{
|
|
||||||
//generate event if 0<v.y||0<a.y
|
|
||||||
if 0f32<(-v.y){
|
|
||||||
best_time=time;
|
|
||||||
exit_face=Some(TreyMeshFace::Top);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
//collect z
|
|
||||||
match collision_data.face {
|
|
||||||
AabbFace::Left|AabbFace::Bottom|AabbFace::Right|AabbFace::Top=>{
|
|
||||||
for t in zeroes2(mesh0.max.z-mesh1.min.z,v.z,0.5*a.z) {
|
|
||||||
//negative t = back in time
|
|
||||||
//must be moving towards surface to collide
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&0f32<v.z+a.z*-t{
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
exit_face=Some(TreyMeshFace::Front);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for t in zeroes2(mesh0.min.z-mesh1.max.z,v.z,0.5*a.z) {
|
|
||||||
//negative t = back in time
|
|
||||||
//must be moving towards surface to collide
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&v.z+a.z*-t<0f32{
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
exit_face=Some(TreyMeshFace::Back);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AabbFace::Front=>{
|
|
||||||
//generate event if v.z<0||a.z<0
|
|
||||||
if -v.z<0f32{
|
|
||||||
best_time=time;
|
|
||||||
exit_face=Some(TreyMeshFace::Front);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AabbFace::Back=>{
|
|
||||||
//generate event if 0<v.z||0<a.z
|
|
||||||
if 0f32<(-v.z){
|
|
||||||
best_time=time;
|
|
||||||
exit_face=Some(TreyMeshFace::Back);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
//generate instruction
|
|
||||||
if let Some(face) = exit_face{
|
|
||||||
return Some(TimedInstruction {
|
|
||||||
time: best_time,
|
|
||||||
instruction: PhysicsInstruction::CollisionEnd(collision_data.clone())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn predict_collision_start(&self,time:TIME,time_limit:TIME,model_id:u32) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
//find best t
|
|
||||||
let mut best_time=time_limit;
|
|
||||||
let mut best_face:Option<TreyMeshFace>=None;
|
|
||||||
let mesh0=self.mesh();
|
|
||||||
let mesh1=self.models_cringe_clone.get(model_id as usize).unwrap().mesh();
|
|
||||||
let (p,v,a)=(self.body.position,self.body.velocity,self.body.acceleration);
|
|
||||||
//collect x
|
|
||||||
for t in zeroes2(mesh0.max.x-mesh1.min.x,v.x,0.5*a.x) {
|
|
||||||
//must collide now or in the future
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&0f32<v.x+a.x*t{
|
|
||||||
let dp=self.body.extrapolated_position(t_time)-p;
|
|
||||||
//faces must be overlapping
|
|
||||||
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
best_face=Some(TreyMeshFace::Left);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for t in zeroes2(mesh0.min.x-mesh1.max.x,v.x,0.5*a.x) {
|
|
||||||
//must collide now or in the future
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&v.x+a.x*t<0f32{
|
|
||||||
let dp=self.body.extrapolated_position(t_time)-p;
|
|
||||||
//faces must be overlapping
|
|
||||||
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
best_face=Some(TreyMeshFace::Right);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//collect y
|
|
||||||
for t in zeroes2(mesh0.max.y-mesh1.min.y,v.y,0.5*a.y) {
|
|
||||||
//must collide now or in the future
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&0f32<v.y+a.y*t{
|
|
||||||
let dp=self.body.extrapolated_position(t_time)-p;
|
|
||||||
//faces must be overlapping
|
|
||||||
if mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
best_face=Some(TreyMeshFace::Bottom);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for t in zeroes2(mesh0.min.y-mesh1.max.y,v.y,0.5*a.y) {
|
|
||||||
//must collide now or in the future
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&v.y+a.y*t<0f32{
|
|
||||||
let dp=self.body.extrapolated_position(t_time)-p;
|
|
||||||
//faces must be overlapping
|
|
||||||
if mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
best_face=Some(TreyMeshFace::Top);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//collect z
|
|
||||||
for t in zeroes2(mesh0.max.z-mesh1.min.z,v.z,0.5*a.z) {
|
|
||||||
//must collide now or in the future
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&0f32<v.z+a.z*t{
|
|
||||||
let dp=self.body.extrapolated_position(t_time)-p;
|
|
||||||
//faces must be overlapping
|
|
||||||
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x {
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
best_face=Some(TreyMeshFace::Front);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for t in zeroes2(mesh0.min.z-mesh1.max.z,v.z,0.5*a.z) {
|
|
||||||
//must collide now or in the future
|
|
||||||
//must beat the current soonest collision time
|
|
||||||
//must be moving towards surface
|
|
||||||
let t_time=self.body.time+((t as f64)*1_000_000_000f64) as TIME;
|
|
||||||
if time<=t_time&&t_time<best_time&&v.z+a.z*t<0f32{
|
|
||||||
let dp=self.body.extrapolated_position(t_time)-p;
|
|
||||||
//faces must be overlapping
|
|
||||||
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x {
|
|
||||||
//collect valid t
|
|
||||||
best_time=t_time;
|
|
||||||
best_face=Some(TreyMeshFace::Back);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//generate instruction
|
|
||||||
if let Some(face) = best_face{
|
|
||||||
return Some(TimedInstruction {
|
|
||||||
time: best_time,
|
|
||||||
instruction: PhysicsInstruction::CollisionStart(RelativeCollision {
|
|
||||||
face,
|
|
||||||
model: model_id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState {
|
|
||||||
//this little next instruction function can cache its return value and invalidate the cached value by watching the State.
|
|
||||||
fn next_instruction(&self,time_limit:TIME) -> Option<TimedInstruction<PhysicsInstruction>> {
|
|
||||||
//JUST POLLING!!! NO MUTATION
|
|
||||||
let mut collector = crate::instruction::InstructionCollector::new(time_limit);
|
|
||||||
//check for collision stop instructions with curent contacts
|
|
||||||
for collision_data in self.contacts.iter() {
|
|
||||||
collector.collect(self.predict_collision_end(self.time,time_limit,collision_data));
|
|
||||||
}
|
|
||||||
//check for collision start instructions (against every part in the game with no optimization!!)
|
|
||||||
for i in 0..self.models_cringe_clone.len() {
|
|
||||||
collector.collect(self.predict_collision_start(self.time,time_limit,i as u32));
|
|
||||||
}
|
|
||||||
if self.grounded {
|
|
||||||
//walk maintenance
|
|
||||||
collector.collect(self.next_walk_instruction());
|
|
||||||
}else{
|
|
||||||
//check to see when the next strafe tick is
|
|
||||||
collector.collect(self.next_strafe_instruction());
|
|
||||||
}
|
|
||||||
collector.instruction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
|
|
||||||
fn process_instruction(&mut self, ins:TimedInstruction<PhysicsInstruction>) {
|
|
||||||
match &ins.instruction {
|
|
||||||
PhysicsInstruction::StrafeTick => (),
|
|
||||||
_=>println!("{:?}",ins),
|
|
||||||
}
|
|
||||||
//selectively update body
|
|
||||||
match &ins.instruction {
|
|
||||||
PhysicsInstruction::SetWalkTargetVelocity(_)
|
|
||||||
|PhysicsInstruction::SetControlDir(_) => self.time=ins.time,//TODO: queue instructions
|
|
||||||
PhysicsInstruction::RefreshWalkTarget
|
|
||||||
|PhysicsInstruction::ReachWalkTargetVelocity
|
|
||||||
|PhysicsInstruction::CollisionStart(_)
|
|
||||||
|PhysicsInstruction::CollisionEnd(_)
|
|
||||||
|PhysicsInstruction::StrafeTick
|
|
||||||
|PhysicsInstruction::Jump => self.advance_time(ins.time),
|
|
||||||
}
|
|
||||||
match ins.instruction {
|
|
||||||
PhysicsInstruction::CollisionStart(c) => {
|
|
||||||
//check ground
|
|
||||||
match &c.face {
|
|
||||||
AabbFace::Top => {
|
|
||||||
//ground
|
|
||||||
self.grounded=true;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
self.contacts.insert(c);
|
|
||||||
//flatten v
|
|
||||||
let mut v=self.body.velocity;
|
|
||||||
self.contact_constrain_velocity(&mut v);
|
|
||||||
self.body.velocity=v;
|
|
||||||
self.walk.state=WalkEnum::Invalid;
|
|
||||||
},
|
|
||||||
PhysicsInstruction::CollisionEnd(c) => {
|
|
||||||
self.contacts.remove(&c);//remove contact before calling contact_constrain_acceleration
|
|
||||||
let mut a=self.gravity;
|
|
||||||
self.contact_constrain_acceleration(&mut a);
|
|
||||||
self.body.acceleration=a;
|
|
||||||
self.walk.state=WalkEnum::Invalid;
|
|
||||||
//check ground
|
|
||||||
match &c.face {
|
|
||||||
AabbFace::Top => {
|
|
||||||
self.grounded=false;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PhysicsInstruction::SetControlDir(control_dir)=>{
|
|
||||||
self.temp_control_dir=control_dir;
|
|
||||||
self.walk.state=WalkEnum::Invalid;
|
|
||||||
},
|
|
||||||
PhysicsInstruction::StrafeTick => {
|
|
||||||
//let control_dir=self.get_control_dir();//this should respect your mouse interpolation settings
|
|
||||||
let d=self.body.velocity.dot(self.temp_control_dir);
|
|
||||||
if d<self.mv {
|
|
||||||
let mut v=self.body.velocity+(self.mv-d)*self.temp_control_dir;
|
|
||||||
self.contact_constrain_velocity(&mut v);
|
|
||||||
self.body.velocity=v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PhysicsInstruction::Jump => {
|
|
||||||
self.grounded=false;//do I need this?
|
|
||||||
let mut v=self.body.velocity+glam::Vec3::new(0.0,0.715588/2.0*100.0,0.0);
|
|
||||||
self.contact_constrain_velocity(&mut v);
|
|
||||||
self.body.velocity=v;
|
|
||||||
self.walk.state=WalkEnum::Invalid;
|
|
||||||
},
|
|
||||||
PhysicsInstruction::ReachWalkTargetVelocity => {
|
|
||||||
//precisely set velocity
|
|
||||||
let mut a=glam::Vec3::ZERO;
|
|
||||||
self.contact_constrain_acceleration(&mut a);
|
|
||||||
self.body.acceleration=a;
|
|
||||||
let mut v=self.walk.target_velocity;
|
|
||||||
self.contact_constrain_velocity(&mut v);
|
|
||||||
self.body.velocity=v;
|
|
||||||
self.walk.state=WalkEnum::Reached;
|
|
||||||
},
|
|
||||||
PhysicsInstruction::RefreshWalkTarget => {
|
|
||||||
//calculate acceleration yada yada
|
|
||||||
if self.grounded{
|
|
||||||
let mut v=self.walk.target_velocity;
|
|
||||||
self.contact_constrain_velocity(&mut v);
|
|
||||||
let mut target_diff=v-self.body.velocity;
|
|
||||||
target_diff.y=0f32;
|
|
||||||
if target_diff==glam::Vec3::ZERO{
|
|
||||||
let mut a=glam::Vec3::ZERO;
|
|
||||||
self.contact_constrain_acceleration(&mut a);
|
|
||||||
self.body.acceleration=a;
|
|
||||||
self.walk.state=WalkEnum::Reached;
|
|
||||||
}else{
|
|
||||||
let accel=self.walk_accel.min(self.gravity.length()*self.friction);
|
|
||||||
let time_delta=target_diff.length()/accel;
|
|
||||||
let mut a=target_diff/time_delta;
|
|
||||||
self.contact_constrain_acceleration(&mut a);
|
|
||||||
self.body.acceleration=a;
|
|
||||||
self.walk.target_time=self.body.time+((time_delta as f64)*1_000_000_000f64) as TIME;
|
|
||||||
self.walk.state=WalkEnum::Transient;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PhysicsInstruction::SetWalkTargetVelocity(v) => {
|
|
||||||
self.walk.target_velocity=v;
|
|
||||||
self.walk.state=WalkEnum::Invalid;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
107
src/bvh.rs
Normal file
107
src/bvh.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use crate::aabb::Aabb;
|
||||||
|
|
||||||
|
//da algaritum
|
||||||
|
//lista boxens
|
||||||
|
//sort by {minx,maxx,miny,maxy,minz,maxz} (6 lists)
|
||||||
|
//find the sets that minimizes the sum of surface areas
|
||||||
|
//splitting is done when the minimum split sum of surface areas is larger than the node's own surface area
|
||||||
|
|
||||||
|
//start with bisection into octrees because a bad bvh is still 1000x better than no bvh
|
||||||
|
//sort the centerpoints on each axis (3 lists)
|
||||||
|
//bv is put into octant based on whether it is upper or lower in each list
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BvhNode{
|
||||||
|
children:Vec<Self>,
|
||||||
|
models:Vec<u32>,
|
||||||
|
aabb:Aabb,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BvhNode{
|
||||||
|
pub fn the_tester<F:FnMut(u32)>(&self,aabb:&Aabb,f:&mut F){
|
||||||
|
for &model in &self.models{
|
||||||
|
f(model);
|
||||||
|
}
|
||||||
|
for child in &self.children{
|
||||||
|
if aabb.intersects(&child.aabb){
|
||||||
|
child.the_tester(aabb,f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_bvh(boxen:Vec<Aabb>)->BvhNode{
|
||||||
|
generate_bvh_node(boxen.into_iter().enumerate().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
|
||||||
|
let n=boxen.len();
|
||||||
|
if n<20{
|
||||||
|
let mut aabb=Aabb::new();
|
||||||
|
let models=boxen.into_iter().map(|b|{aabb.join(&b.1);b.0 as u32}).collect();
|
||||||
|
BvhNode{
|
||||||
|
children:Vec::new(),
|
||||||
|
models,
|
||||||
|
aabb,
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let mut octant=std::collections::HashMap::with_capacity(n);//this ids which octant the boxen is put in
|
||||||
|
let mut sort_x=Vec::with_capacity(n);
|
||||||
|
let mut sort_y=Vec::with_capacity(n);
|
||||||
|
let mut sort_z=Vec::with_capacity(n);
|
||||||
|
for (i,aabb) in boxen.iter(){
|
||||||
|
let center=aabb.center();
|
||||||
|
octant.insert(*i,0);
|
||||||
|
sort_x.push((*i,center.x));
|
||||||
|
sort_y.push((*i,center.y));
|
||||||
|
sort_z.push((*i,center.z));
|
||||||
|
}
|
||||||
|
sort_x.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
|
||||||
|
sort_y.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
|
||||||
|
sort_z.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
|
||||||
|
let h=n/2;
|
||||||
|
let median_x=sort_x[h].1;
|
||||||
|
let median_y=sort_y[h].1;
|
||||||
|
let median_z=sort_z[h].1;
|
||||||
|
for (i,c) in sort_x{
|
||||||
|
if median_x<c{
|
||||||
|
octant.insert(i,octant[&i]+1<<0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i,c) in sort_y{
|
||||||
|
if median_y<c{
|
||||||
|
octant.insert(i,octant[&i]+1<<1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i,c) in sort_z{
|
||||||
|
if median_z<c{
|
||||||
|
octant.insert(i,octant[&i]+1<<2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//generate lists for unique octant values
|
||||||
|
let mut list_list=Vec::with_capacity(8);
|
||||||
|
let mut octant_list=Vec::with_capacity(8);
|
||||||
|
for (i,aabb) in boxen.into_iter(){
|
||||||
|
let octant_id=octant[&i];
|
||||||
|
let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){
|
||||||
|
list_id
|
||||||
|
}else{
|
||||||
|
let list_id=list_list.len();
|
||||||
|
octant_list.push(octant_id);
|
||||||
|
list_list.push(Vec::new());
|
||||||
|
list_id
|
||||||
|
};
|
||||||
|
list_list[list_id].push((i,aabb));
|
||||||
|
}
|
||||||
|
let mut aabb=Aabb::new();
|
||||||
|
let children=list_list.into_iter().map(|b|{
|
||||||
|
let node=generate_bvh_node(b);
|
||||||
|
aabb.join(&node.aabb);
|
||||||
|
node
|
||||||
|
}).collect();
|
||||||
|
BvhNode{
|
||||||
|
children,
|
||||||
|
models:Vec::new(),
|
||||||
|
aabb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use std::time::Instant;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas};
|
use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas};
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{self, WindowEvent},
|
event::{self, WindowEvent, DeviceEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,8 +51,9 @@ pub trait Example: 'static + Sized {
|
|||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
);
|
);
|
||||||
fn update(&mut self, event: WindowEvent);
|
fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: WindowEvent);
|
||||||
fn move_mouse(&mut self, delta: (f64,f64));
|
fn device_event(&mut self, window: &winit::window::Window, event: DeviceEvent);
|
||||||
|
fn load_file(&mut self, path:std::path::PathBuf, device: &wgpu::Device, queue: &wgpu::Queue);
|
||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &wgpu::TextureView,
|
view: &wgpu::TextureView,
|
||||||
@ -181,9 +180,41 @@ async fn setup<E: Example>(title: &str) -> Setup {
|
|||||||
|
|
||||||
(size, surface)
|
(size, surface)
|
||||||
};
|
};
|
||||||
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, Some(&surface))
|
|
||||||
.await
|
let adapter;
|
||||||
.expect("No suitable GPU adapters found on the system!");
|
|
||||||
|
let optional_features = E::optional_features();
|
||||||
|
let required_features = E::required_features();
|
||||||
|
|
||||||
|
//no helper function smh gotta write it myself
|
||||||
|
let adapters = instance.enumerate_adapters(backends);
|
||||||
|
|
||||||
|
let mut chosen_adapter = None;
|
||||||
|
let mut chosen_adapter_score=0;
|
||||||
|
for adapter in adapters {
|
||||||
|
if !adapter.is_surface_supported(&surface) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let score=match adapter.get_info().device_type{
|
||||||
|
wgpu::DeviceType::IntegratedGpu=>3,
|
||||||
|
wgpu::DeviceType::DiscreteGpu=>4,
|
||||||
|
wgpu::DeviceType::VirtualGpu=>2,
|
||||||
|
wgpu::DeviceType::Other|wgpu::DeviceType::Cpu=>1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let adapter_features = adapter.features();
|
||||||
|
if chosen_adapter_score<score&&adapter_features.contains(required_features) {
|
||||||
|
chosen_adapter_score=score;
|
||||||
|
chosen_adapter=Some(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(maybe_chosen_adapter) = chosen_adapter{
|
||||||
|
adapter=maybe_chosen_adapter;
|
||||||
|
}else{
|
||||||
|
panic!("No suitable GPU adapters found on the system!");
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
@ -191,15 +222,6 @@ async fn setup<E: Example>(title: &str) -> Setup {
|
|||||||
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
|
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
let optional_features = E::optional_features();
|
|
||||||
let required_features = E::required_features();
|
|
||||||
let adapter_features = adapter.features();
|
|
||||||
assert!(
|
|
||||||
adapter_features.contains(required_features),
|
|
||||||
"Adapter does not support required features for this example: {:?}",
|
|
||||||
required_features - adapter_features
|
|
||||||
);
|
|
||||||
|
|
||||||
let required_downlevel_capabilities = E::required_downlevel_capabilities();
|
let required_downlevel_capabilities = E::required_downlevel_capabilities();
|
||||||
let downlevel_capabilities = adapter.get_downlevel_capabilities();
|
let downlevel_capabilities = adapter.get_downlevel_capabilities();
|
||||||
assert!(
|
assert!(
|
||||||
@ -223,7 +245,7 @@ async fn setup<E: Example>(title: &str) -> Setup {
|
|||||||
.request_device(
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
features: (optional_features & adapter_features) | required_features,
|
features: (optional_features & adapter.features()) | required_features,
|
||||||
limits: needed_limits,
|
limits: needed_limits,
|
||||||
},
|
},
|
||||||
trace_dir.ok().as_ref().map(std::path::Path::new),
|
trace_dir.ok().as_ref().map(std::path::Path::new),
|
||||||
@ -337,7 +359,7 @@ fn start<E: Example>(
|
|||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
input:
|
input:
|
||||||
event::KeyboardInput {
|
event::KeyboardInput {
|
||||||
virtual_keycode: Some(event::VirtualKeyCode::R),
|
virtual_keycode: Some(event::VirtualKeyCode::Scroll),
|
||||||
state: event::ElementState::Pressed,
|
state: event::ElementState::Pressed,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
@ -346,17 +368,14 @@ fn start<E: Example>(
|
|||||||
println!("{:#?}", instance.generate_report());
|
println!("{:#?}", instance.generate_report());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
example.update(event);
|
example.update(&window,&device,&queue,event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
event::Event::DeviceEvent {
|
event::Event::DeviceEvent {
|
||||||
event:
|
event,
|
||||||
winit::event::DeviceEvent::MouseMotion {
|
|
||||||
delta,
|
|
||||||
},
|
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
example.move_mouse(delta);
|
example.device_event(&window,event);
|
||||||
},
|
},
|
||||||
event::Event::RedrawRequested(_) => {
|
event::Event::RedrawRequested(_) => {
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TimedInstruction<I> {
|
pub struct TimedInstruction<I> {
|
||||||
pub time: crate::body::TIME,
|
pub time: crate::physics::TIME,
|
||||||
pub instruction: I,
|
pub instruction: I,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InstructionEmitter<I> {
|
pub trait InstructionEmitter<I> {
|
||||||
fn next_instruction(&self, time_limit:crate::body::TIME) -> Option<TimedInstruction<I>>;
|
fn next_instruction(&self, time_limit:crate::physics::TIME) -> Option<TimedInstruction<I>>;
|
||||||
}
|
}
|
||||||
pub trait InstructionConsumer<I> {
|
pub trait InstructionConsumer<I> {
|
||||||
fn process_instruction(&mut self, instruction:TimedInstruction<I>);
|
fn process_instruction(&mut self, instruction:TimedInstruction<I>);
|
||||||
@ -13,11 +13,11 @@ pub trait InstructionConsumer<I> {
|
|||||||
|
|
||||||
//PROPER PRIVATE FIELDS!!!
|
//PROPER PRIVATE FIELDS!!!
|
||||||
pub struct InstructionCollector<I> {
|
pub struct InstructionCollector<I> {
|
||||||
time: crate::body::TIME,
|
time: crate::physics::TIME,
|
||||||
instruction: Option<I>,
|
instruction: Option<I>,
|
||||||
}
|
}
|
||||||
impl<I> InstructionCollector<I> {
|
impl<I> InstructionCollector<I> {
|
||||||
pub fn new(time:crate::body::TIME) -> Self {
|
pub fn new(time:crate::physics::TIME) -> Self {
|
||||||
Self{
|
Self{
|
||||||
time,
|
time,
|
||||||
instruction:None
|
instruction:None
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
pub mod framework;
|
|
||||||
pub mod body;
|
|
||||||
pub mod zeroes;
|
|
||||||
pub mod instruction;
|
|
478
src/load_roblox.rs
Normal file
478
src/load_roblox.rs
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
use crate::primitives;
|
||||||
|
|
||||||
|
fn class_is_a(class: &str, superclass: &str) -> bool {
|
||||||
|
if class==superclass {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
let class_descriptor=rbx_reflection_database::get().classes.get(class);
|
||||||
|
if let Some(descriptor) = &class_descriptor {
|
||||||
|
if let Some(class_super) = &descriptor.superclass {
|
||||||
|
return class_is_a(&class_super, superclass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fn recursive_collect_superclass(objects: &mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom: &rbx_dom_weak::WeakDom, instance: &rbx_dom_weak::Instance, superclass: &str){
|
||||||
|
for &referent in instance.children() {
|
||||||
|
if let Some(c) = dom.get_by_ref(referent) {
|
||||||
|
if class_is_a(c.class.as_str(), superclass) {
|
||||||
|
objects.push(c.referent());//copy ref
|
||||||
|
}
|
||||||
|
recursive_collect_superclass(objects,dom,c,superclass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>{
|
||||||
|
let mut objects = std::vec::Vec::new();
|
||||||
|
recursive_collect_superclass(&mut objects, dom, dom.root(),"Decal");
|
||||||
|
//get ids
|
||||||
|
//clear vec
|
||||||
|
//next class
|
||||||
|
objects
|
||||||
|
}
|
||||||
|
fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersecting:bool)->crate::model::CollisionAttributes{
|
||||||
|
let mut general=crate::model::GameMechanicAttributes::default();
|
||||||
|
let mut intersecting=crate::model::IntersectingAttributes::default();
|
||||||
|
let mut contacting=crate::model::ContactingAttributes::default();
|
||||||
|
let mut force_can_collide=can_collide;
|
||||||
|
match name{
|
||||||
|
//"Water"=>intersecting.water=Some(crate::model::IntersectingWater{density:1.0,drag:1.0}),
|
||||||
|
"Accelerator"=>{force_can_collide=false;intersecting.accelerator=Some(crate::model::IntersectingAccelerator{acceleration:velocity})},
|
||||||
|
"MapFinish"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Finish})},
|
||||||
|
"MapAnticheat"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Anitcheat})},
|
||||||
|
"Platform"=>general.stage_element=Some(crate::model::GameMechanicStageElement{
|
||||||
|
mode_id:0,
|
||||||
|
stage_id:0,
|
||||||
|
force:false,
|
||||||
|
behaviour:crate::model::StageElementBehaviour::Platform,
|
||||||
|
}),
|
||||||
|
other=>{
|
||||||
|
if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
|
||||||
|
.captures(other){
|
||||||
|
general.stage_element=Some(crate::model::GameMechanicStageElement{
|
||||||
|
mode_id:0,
|
||||||
|
stage_id:captures[3].parse::<u32>().unwrap(),
|
||||||
|
force:match captures.get(1){
|
||||||
|
Some(m)=>m.as_str()=="Force",
|
||||||
|
None=>false,
|
||||||
|
},
|
||||||
|
behaviour:match &captures[2]{
|
||||||
|
"Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
|
||||||
|
"Trigger"=>{force_can_collide=false;crate::model::StageElementBehaviour::Trigger},
|
||||||
|
"Teleport"=>{force_can_collide=false;crate::model::StageElementBehaviour::Teleport},
|
||||||
|
"Platform"=>crate::model::StageElementBehaviour::Platform,
|
||||||
|
_=>panic!("regex1[2] messed up bad"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
|
||||||
|
.captures(other){
|
||||||
|
force_can_collide=false;
|
||||||
|
match &captures[1]{
|
||||||
|
"Finish"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Finish}),
|
||||||
|
"Anticheat"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Anitcheat}),
|
||||||
|
_=>panic!("regex2[1] messed up bad"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//need some way to skip this
|
||||||
|
if velocity!=glam::Vec3::ZERO{
|
||||||
|
general.booster=Some(crate::model::GameMechanicBooster{velocity});
|
||||||
|
}
|
||||||
|
match force_can_collide{
|
||||||
|
true=>{
|
||||||
|
match name{
|
||||||
|
//"Bounce"=>(),
|
||||||
|
"Surf"=>contacting.surf=Some(crate::model::ContactingSurf{}),
|
||||||
|
"Ladder"=>contacting.ladder=Some(crate::model::ContactingLadder{sticky:true}),
|
||||||
|
other=>{
|
||||||
|
//REGEX!!!!
|
||||||
|
//Jump#
|
||||||
|
//WormholeIn#
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::model::CollisionAttributes::Contact{contacting,general}
|
||||||
|
},
|
||||||
|
false=>if force_intersecting
|
||||||
|
||general.jump_limit.is_some()
|
||||||
|
||general.booster.is_some()
|
||||||
|
||general.zone.is_some()
|
||||||
|
||general.stage_element.is_some()
|
||||||
|
||general.wormhole.is_some()
|
||||||
|
||intersecting.water.is_some()
|
||||||
|
||intersecting.accelerator.is_some()
|
||||||
|
{
|
||||||
|
crate::model::CollisionAttributes::Intersect{intersecting,general}
|
||||||
|
}else{
|
||||||
|
crate::model::CollisionAttributes::Decoration
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RobloxAssetId(u64);
|
||||||
|
struct RobloxAssetIdParseErr;
|
||||||
|
impl std::str::FromStr for RobloxAssetId {
|
||||||
|
type Err=RobloxAssetIdParseErr;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err>{
|
||||||
|
let regman=lazy_regex::regex!(r"(\d+)$");
|
||||||
|
if let Some(captures) = regman.captures(s) {
|
||||||
|
if captures.len()==2{//captures[0] is all captures concatenated, and then each individual capture
|
||||||
|
if let Ok(id) = captures[0].parse::<u64>() {
|
||||||
|
return Ok(Self(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(RobloxAssetIdParseErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone,Copy,PartialEq)]
|
||||||
|
struct RobloxTextureTransform{
|
||||||
|
offset_u:f32,
|
||||||
|
offset_v:f32,
|
||||||
|
scale_u:f32,
|
||||||
|
scale_v:f32,
|
||||||
|
}
|
||||||
|
impl std::cmp::Eq for RobloxTextureTransform{}//????
|
||||||
|
impl std::default::Default for RobloxTextureTransform{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self{offset_u:0.0,offset_v:0.0,scale_u:1.0,scale_v:1.0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::hash::Hash for RobloxTextureTransform {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.offset_u.to_ne_bytes().hash(state);
|
||||||
|
self.offset_v.to_ne_bytes().hash(state);
|
||||||
|
self.scale_u.to_ne_bytes().hash(state);
|
||||||
|
self.scale_v.to_ne_bytes().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone,PartialEq)]
|
||||||
|
struct RobloxFaceTextureDescription{
|
||||||
|
texture:u32,
|
||||||
|
color:glam::Vec4,
|
||||||
|
transform:RobloxTextureTransform,
|
||||||
|
}
|
||||||
|
impl std::cmp::Eq for RobloxFaceTextureDescription{}//????
|
||||||
|
impl std::hash::Hash for RobloxFaceTextureDescription {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.texture.hash(state);
|
||||||
|
self.transform.hash(state);
|
||||||
|
for &el in self.color.as_ref().iter() {
|
||||||
|
el.to_ne_bytes().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl RobloxFaceTextureDescription{
|
||||||
|
fn to_face_description(&self)->primitives::FaceDescription{
|
||||||
|
primitives::FaceDescription{
|
||||||
|
texture:Some(self.texture),
|
||||||
|
transform:glam::Affine2::from_translation(
|
||||||
|
glam::vec2(self.transform.offset_u,self.transform.offset_v)
|
||||||
|
)
|
||||||
|
*glam::Affine2::from_scale(
|
||||||
|
glam::vec2(self.transform.scale_u,self.transform.scale_v)
|
||||||
|
),
|
||||||
|
color:self.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type RobloxPartDescription=[Option<RobloxFaceTextureDescription>;6];
|
||||||
|
type RobloxWedgeDescription=[Option<RobloxFaceTextureDescription>;5];
|
||||||
|
type RobloxCornerWedgeDescription=[Option<RobloxFaceTextureDescription>;5];
|
||||||
|
#[derive(Clone,Eq,Hash,PartialEq)]
|
||||||
|
enum RobloxBasePartDescription{
|
||||||
|
Sphere,
|
||||||
|
Part(RobloxPartDescription),
|
||||||
|
Cylinder,
|
||||||
|
Wedge(RobloxWedgeDescription),
|
||||||
|
CornerWedge(RobloxCornerWedgeDescription),
|
||||||
|
}
|
||||||
|
pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::IndexedModelInstances{
|
||||||
|
//IndexedModelInstances includes textures
|
||||||
|
let mut spawn_point=glam::Vec3::ZERO;
|
||||||
|
|
||||||
|
let mut indexed_models=Vec::new();
|
||||||
|
let mut model_id_from_description=std::collections::HashMap::<RobloxBasePartDescription,usize>::new();
|
||||||
|
|
||||||
|
let mut texture_id_from_asset_id=std::collections::HashMap::<u64,u32>::new();
|
||||||
|
let mut asset_id_from_texture_id=Vec::new();
|
||||||
|
|
||||||
|
let mut object_refs=Vec::new();
|
||||||
|
let mut temp_objects=Vec::new();
|
||||||
|
recursive_collect_superclass(&mut object_refs, &dom, dom.root(),"BasePart");
|
||||||
|
for object_ref in object_refs {
|
||||||
|
if let Some(object)=dom.get_by_ref(object_ref){
|
||||||
|
if let (
|
||||||
|
Some(rbx_dom_weak::types::Variant::CFrame(cf)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Vector3(size)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Vector3(velocity)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(transparency)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Bool(can_collide)),
|
||||||
|
) = (
|
||||||
|
object.properties.get("CFrame"),
|
||||||
|
object.properties.get("Size"),
|
||||||
|
object.properties.get("Velocity"),
|
||||||
|
object.properties.get("Transparency"),
|
||||||
|
object.properties.get("Color"),
|
||||||
|
object.properties.get("CanCollide"),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let model_transform=glam::Affine3A::from_translation(
|
||||||
|
glam::Vec3::new(cf.position.x,cf.position.y,cf.position.z)
|
||||||
|
)
|
||||||
|
* glam::Affine3A::from_mat3(
|
||||||
|
glam::Mat3::from_cols(
|
||||||
|
glam::Vec3::new(cf.orientation.x.x,cf.orientation.y.x,cf.orientation.z.x),
|
||||||
|
glam::Vec3::new(cf.orientation.x.y,cf.orientation.y.y,cf.orientation.z.y),
|
||||||
|
glam::Vec3::new(cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
* glam::Affine3A::from_scale(
|
||||||
|
glam::Vec3::new(size.x,size.y,size.z)/2.0
|
||||||
|
);
|
||||||
|
|
||||||
|
//push TempIndexedAttributes
|
||||||
|
let mut force_intersecting=false;
|
||||||
|
let mut temp_indexing_attributes=Vec::new();
|
||||||
|
if let Some(attr)=match &object.name[..]{
|
||||||
|
"MapStart"=>{
|
||||||
|
spawn_point=model_transform.transform_point3(glam::Vec3::ZERO)+glam::vec3(0.0,2.5,0.0);
|
||||||
|
Some(crate::model::TempIndexedAttributes::Start{mode_id:0})
|
||||||
|
},
|
||||||
|
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}),
|
||||||
|
other=>{
|
||||||
|
let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|OrderedCheckpoint)(\d+)$");
|
||||||
|
if let Some(captures) = regman.captures(other) {
|
||||||
|
match &captures[1]{
|
||||||
|
"BonusStart"=>Some(crate::model::TempIndexedAttributes::Start{mode_id:captures[2].parse::<u32>().unwrap()}),
|
||||||
|
"Spawn"|"ForceSpawn"=>Some(crate::model::TempIndexedAttributes::Spawn{mode_id:0,stage_id:captures[2].parse::<u32>().unwrap()}),
|
||||||
|
"OrderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::OrderedCheckpoint{mode_id:0,checkpoint_id:captures[2].parse::<u32>().unwrap()}),
|
||||||
|
_=>None,
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
force_intersecting=true;
|
||||||
|
temp_indexing_attributes.push(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: also detect "CylinderMesh" etc here
|
||||||
|
let shape=match &object.class[..]{
|
||||||
|
"Part"=>{
|
||||||
|
if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){
|
||||||
|
match shape.to_u32(){
|
||||||
|
0=>primitives::Primitives::Sphere,
|
||||||
|
1=>primitives::Primitives::Cube,
|
||||||
|
2=>primitives::Primitives::Cylinder,
|
||||||
|
3=>primitives::Primitives::Wedge,
|
||||||
|
4=>primitives::Primitives::CornerWedge,
|
||||||
|
_=>panic!("Funky roblox PartType={};",shape.to_u32()),
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
panic!("Part has no Shape!");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"WedgePart"=>primitives::Primitives::Wedge,
|
||||||
|
"CornerWedgePart"=>primitives::Primitives::CornerWedge,
|
||||||
|
_=>{
|
||||||
|
println!("Unsupported BasePart ClassName={}; defaulting to cube",object.class);
|
||||||
|
primitives::Primitives::Cube
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//use the biggest one and cut it down later...
|
||||||
|
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
||||||
|
temp_objects.clear();
|
||||||
|
recursive_collect_superclass(&mut temp_objects, &dom, object,"Decal");
|
||||||
|
for &decal_ref in &temp_objects{
|
||||||
|
if let Some(decal)=dom.get_by_ref(decal_ref){
|
||||||
|
if let (
|
||||||
|
Some(rbx_dom_weak::types::Variant::Content(content)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Enum(normalid)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
|
||||||
|
) = (
|
||||||
|
decal.properties.get("Texture"),
|
||||||
|
decal.properties.get("Face"),
|
||||||
|
decal.properties.get("Color3"),
|
||||||
|
decal.properties.get("Transparency"),
|
||||||
|
) {
|
||||||
|
if let Ok(asset_id)=content.clone().into_string().parse::<RobloxAssetId>(){
|
||||||
|
let texture_id=if let Some(&texture_id)=texture_id_from_asset_id.get(&asset_id.0){
|
||||||
|
texture_id
|
||||||
|
}else{
|
||||||
|
let texture_id=asset_id_from_texture_id.len() as u32;
|
||||||
|
texture_id_from_asset_id.insert(asset_id.0,texture_id);
|
||||||
|
asset_id_from_texture_id.push(asset_id.0);
|
||||||
|
texture_id
|
||||||
|
};
|
||||||
|
let normal_id=normalid.to_u32();
|
||||||
|
if normal_id<6{
|
||||||
|
let (roblox_texture_color,roblox_texture_transform)=if decal.class=="Texture"{
|
||||||
|
//generate tranform
|
||||||
|
if let (
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(ox)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(oy)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(sx)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(sy)),
|
||||||
|
) = (
|
||||||
|
decal.properties.get("OffsetStudsU"),
|
||||||
|
decal.properties.get("OffsetStudsV"),
|
||||||
|
decal.properties.get("StudsPerTileU"),
|
||||||
|
decal.properties.get("StudsPerTileV"),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let (size_u,size_v)=match normal_id{
|
||||||
|
0=>(size.z,size.y),//right
|
||||||
|
1=>(size.x,size.z),//top
|
||||||
|
2=>(size.x,size.y),//back
|
||||||
|
3=>(size.z,size.y),//left
|
||||||
|
4=>(size.x,size.z),//bottom
|
||||||
|
5=>(size.x,size.y),//front
|
||||||
|
_=>panic!("unreachable"),
|
||||||
|
};
|
||||||
|
(
|
||||||
|
glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency),
|
||||||
|
RobloxTextureTransform{
|
||||||
|
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
||||||
|
scale_u:size_u/(*sx),scale_v:size_v/(*sy),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
||||||
|
};
|
||||||
|
part_texture_description[normal_id as usize]=Some(RobloxFaceTextureDescription{
|
||||||
|
texture:texture_id,
|
||||||
|
color:roblox_texture_color,
|
||||||
|
transform:roblox_texture_transform,
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
println!("NormalId={} unsupported for shape={:?}",normal_id,shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//obscure rust syntax "slice pattern"
|
||||||
|
let [
|
||||||
|
f0,//Cube::Right
|
||||||
|
f1,//Cube::Top
|
||||||
|
f2,//Cube::Back
|
||||||
|
f3,//Cube::Left
|
||||||
|
f4,//Cube::Bottom
|
||||||
|
f5,//Cube::Front
|
||||||
|
]=part_texture_description;
|
||||||
|
let basepart_texture_description=match shape{
|
||||||
|
primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere,
|
||||||
|
primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]),
|
||||||
|
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder,
|
||||||
|
//use front face texture first and use top face texture as a fallback
|
||||||
|
primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([
|
||||||
|
f0,//Cube::Right->Wedge::Right
|
||||||
|
if f5.is_some(){f5}else{f1},//Cube::Front|Cube::Top->Wedge::TopFront
|
||||||
|
f2,//Cube::Back->Wedge::Back
|
||||||
|
f3,//Cube::Left->Wedge::Left
|
||||||
|
f4,//Cube::Bottom->Wedge::Bottom
|
||||||
|
]),
|
||||||
|
//TODO: fix Left+Back texture coordinates to match roblox when not overwridden by Top
|
||||||
|
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([
|
||||||
|
f0,//Cube::Right->CornerWedge::Right
|
||||||
|
if f2.is_some(){f2}else{f1.clone()},//Cube::Back|Cube::Top->CornerWedge::TopBack
|
||||||
|
if f3.is_some(){f3}else{f1},//Cube::Left|Cube::Top->CornerWedge::TopLeft
|
||||||
|
f4,//Cube::Bottom->CornerWedge::Bottom
|
||||||
|
f5,//Cube::Front->CornerWedge::Front
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
//make new model if unit cube has not been created before
|
||||||
|
let model_id=if let Some(&model_id)=model_id_from_description.get(&basepart_texture_description){
|
||||||
|
//push to existing texture model
|
||||||
|
model_id
|
||||||
|
}else{
|
||||||
|
let model_id=indexed_models.len();
|
||||||
|
model_id_from_description.insert(basepart_texture_description.clone(),model_id);//borrow checker going crazy
|
||||||
|
indexed_models.push(match basepart_texture_description{
|
||||||
|
RobloxBasePartDescription::Sphere=>primitives::unit_sphere(),
|
||||||
|
RobloxBasePartDescription::Part(part_texture_description)=>{
|
||||||
|
let mut cube_face_description=primitives::CubeFaceDescription::new();
|
||||||
|
for (face_id,roblox_face_description) in part_texture_description.iter().enumerate(){
|
||||||
|
cube_face_description.insert(
|
||||||
|
match face_id{
|
||||||
|
0=>primitives::CubeFace::Right,
|
||||||
|
1=>primitives::CubeFace::Top,
|
||||||
|
2=>primitives::CubeFace::Back,
|
||||||
|
3=>primitives::CubeFace::Left,
|
||||||
|
4=>primitives::CubeFace::Bottom,
|
||||||
|
5=>primitives::CubeFace::Front,
|
||||||
|
_=>panic!("unreachable"),
|
||||||
|
},
|
||||||
|
match roblox_face_description{
|
||||||
|
Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(),
|
||||||
|
None=>primitives::FaceDescription::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
primitives::generate_partial_unit_cube(cube_face_description)
|
||||||
|
},
|
||||||
|
RobloxBasePartDescription::Cylinder=>primitives::unit_cylinder(),
|
||||||
|
RobloxBasePartDescription::Wedge(wedge_texture_description)=>{
|
||||||
|
let mut wedge_face_description=primitives::WedgeFaceDescription::new();
|
||||||
|
for (face_id,roblox_face_description) in wedge_texture_description.iter().enumerate(){
|
||||||
|
wedge_face_description.insert(
|
||||||
|
match face_id{
|
||||||
|
0=>primitives::WedgeFace::Right,
|
||||||
|
1=>primitives::WedgeFace::TopFront,
|
||||||
|
2=>primitives::WedgeFace::Back,
|
||||||
|
3=>primitives::WedgeFace::Left,
|
||||||
|
4=>primitives::WedgeFace::Bottom,
|
||||||
|
_=>panic!("unreachable"),
|
||||||
|
},
|
||||||
|
match roblox_face_description{
|
||||||
|
Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(),
|
||||||
|
None=>primitives::FaceDescription::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
primitives::generate_partial_unit_wedge(wedge_face_description)
|
||||||
|
},
|
||||||
|
RobloxBasePartDescription::CornerWedge(cornerwedge_texture_description)=>{
|
||||||
|
let mut cornerwedge_face_description=primitives::CornerWedgeFaceDescription::new();
|
||||||
|
for (face_id,roblox_face_description) in cornerwedge_texture_description.iter().enumerate(){
|
||||||
|
cornerwedge_face_description.insert(
|
||||||
|
match face_id{
|
||||||
|
0=>primitives::CornerWedgeFace::Right,
|
||||||
|
1=>primitives::CornerWedgeFace::TopBack,
|
||||||
|
2=>primitives::CornerWedgeFace::TopLeft,
|
||||||
|
3=>primitives::CornerWedgeFace::Bottom,
|
||||||
|
4=>primitives::CornerWedgeFace::Front,
|
||||||
|
_=>panic!("unreachable"),
|
||||||
|
},
|
||||||
|
match roblox_face_description{
|
||||||
|
Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(),
|
||||||
|
None=>primitives::FaceDescription::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
model_id
|
||||||
|
};
|
||||||
|
indexed_models[model_id].instances.push(crate::model::ModelInstance {
|
||||||
|
transform:model_transform,
|
||||||
|
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
|
||||||
|
attributes:get_attributes(&object.name,*can_collide,glam::vec3(velocity.x,velocity.y,velocity.z),force_intersecting),
|
||||||
|
temp_indexing:temp_indexing_attributes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::model::IndexedModelInstances{
|
||||||
|
textures:asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),
|
||||||
|
models:indexed_models,
|
||||||
|
spawn_point,
|
||||||
|
modes:Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
1079
src/main.rs
1079
src/main.rs
File diff suppressed because it is too large
Load Diff
269
src/model.rs
Normal file
269
src/model.rs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Vertex {
|
||||||
|
pub pos: [f32; 3],
|
||||||
|
pub tex: [f32; 2],
|
||||||
|
pub normal: [f32; 3],
|
||||||
|
pub color: [f32; 4],
|
||||||
|
}
|
||||||
|
#[derive(Clone,Hash,PartialEq,Eq)]
|
||||||
|
pub struct IndexedVertex{
|
||||||
|
pub pos:u32,
|
||||||
|
pub tex:u32,
|
||||||
|
pub normal:u32,
|
||||||
|
pub color:u32,
|
||||||
|
}
|
||||||
|
pub struct IndexedPolygon{
|
||||||
|
pub vertices:Vec<u32>,
|
||||||
|
}
|
||||||
|
pub struct IndexedGroup{
|
||||||
|
pub texture:Option<u32>,//RenderPattern? material/texture/shader/flat color
|
||||||
|
pub polys:Vec<IndexedPolygon>,
|
||||||
|
}
|
||||||
|
pub struct IndexedModel{
|
||||||
|
pub unique_pos:Vec<[f32; 3]>,
|
||||||
|
pub unique_tex:Vec<[f32; 2]>,
|
||||||
|
pub unique_normal:Vec<[f32; 3]>,
|
||||||
|
pub unique_color:Vec<[f32; 4]>,
|
||||||
|
pub unique_vertices:Vec<IndexedVertex>,
|
||||||
|
pub groups: Vec<IndexedGroup>,
|
||||||
|
pub instances:Vec<ModelInstance>,
|
||||||
|
}
|
||||||
|
pub struct IndexedGroupFixedTexture{
|
||||||
|
pub polys:Vec<IndexedPolygon>,
|
||||||
|
}
|
||||||
|
pub struct IndexedModelSingleTexture{
|
||||||
|
pub unique_pos:Vec<[f32; 3]>,
|
||||||
|
pub unique_tex:Vec<[f32; 2]>,
|
||||||
|
pub unique_normal:Vec<[f32; 3]>,
|
||||||
|
pub unique_color:Vec<[f32; 4]>,
|
||||||
|
pub unique_vertices:Vec<IndexedVertex>,
|
||||||
|
pub texture:Option<u32>,//RenderPattern? material/texture/shader/flat color
|
||||||
|
pub groups: Vec<IndexedGroupFixedTexture>,
|
||||||
|
pub instances:Vec<ModelGraphicsInstance>,
|
||||||
|
}
|
||||||
|
pub struct ModelSingleTexture{
|
||||||
|
pub instances: Vec<ModelGraphicsInstance>,
|
||||||
|
pub vertices: Vec<Vertex>,
|
||||||
|
pub entities: Vec<Vec<u16>>,
|
||||||
|
pub texture: Option<u32>,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModelGraphicsInstance{
|
||||||
|
pub transform:glam::Mat4,
|
||||||
|
pub normal_transform:glam::Mat3,
|
||||||
|
pub color:glam::Vec4,
|
||||||
|
}
|
||||||
|
pub struct ModelInstance{
|
||||||
|
//pub id:u64,//this does not actually help with map fixes resimulating bots, they must always be resimulated
|
||||||
|
pub transform:glam::Affine3A,
|
||||||
|
pub color:glam::Vec4,//transparency is in here
|
||||||
|
pub attributes:CollisionAttributes,
|
||||||
|
pub temp_indexing:Vec<TempIndexedAttributes>,
|
||||||
|
}
|
||||||
|
impl std::default::Default for ModelInstance{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self{
|
||||||
|
color:glam::Vec4::ONE,
|
||||||
|
transform:Default::default(),
|
||||||
|
attributes:Default::default(),
|
||||||
|
temp_indexing:Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct IndexedModelInstances{
|
||||||
|
pub textures:Vec<String>,//RenderPattern
|
||||||
|
pub models:Vec<IndexedModel>,
|
||||||
|
//may make this into an object later.
|
||||||
|
pub modes:Vec<ModeDescription>,
|
||||||
|
pub spawn_point:glam::Vec3,
|
||||||
|
}
|
||||||
|
//stage description referencing flattened ids is spooky, but the map loading is meant to be deterministic.
|
||||||
|
pub struct ModeDescription{
|
||||||
|
pub start:u32,//start=model_id
|
||||||
|
pub spawns:Vec<u32>,//spawns[spawn_id]=model_id
|
||||||
|
pub ordered_checkpoints:Vec<u32>,//ordered_checkpoints[checkpoint_id]=model_id
|
||||||
|
pub unordered_checkpoints:Vec<u32>,//unordered_checkpoints[checkpoint_id]=model_id
|
||||||
|
pub spawn_from_stage_id:std::collections::HashMap::<u32,usize>,
|
||||||
|
pub ordered_checkpoint_from_checkpoint_id:std::collections::HashMap::<u32,usize>,
|
||||||
|
}
|
||||||
|
impl ModeDescription{
|
||||||
|
pub fn get_spawn_model_id(&self,stage_id:u32)->Option<&u32>{
|
||||||
|
if let Some(&spawn)=self.spawn_from_stage_id.get(&stage_id){
|
||||||
|
self.spawns.get(spawn)
|
||||||
|
}else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_ordered_checkpoint_model_id(&self,checkpoint_id:u32)->Option<&u32>{
|
||||||
|
if let Some(&checkpoint)=self.ordered_checkpoint_from_checkpoint_id.get(&checkpoint_id){
|
||||||
|
self.ordered_checkpoints.get(checkpoint)
|
||||||
|
}else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub enum TempIndexedAttributes{
|
||||||
|
Start{
|
||||||
|
mode_id:u32,
|
||||||
|
},
|
||||||
|
Spawn{
|
||||||
|
mode_id:u32,
|
||||||
|
stage_id:u32,
|
||||||
|
},
|
||||||
|
OrderedCheckpoint{
|
||||||
|
mode_id:u32,
|
||||||
|
checkpoint_id:u32,
|
||||||
|
},
|
||||||
|
UnorderedCheckpoint{
|
||||||
|
mode_id:u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
//you have this effect while in contact
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ContactingSurf{}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ContactingLadder{
|
||||||
|
pub sticky:bool
|
||||||
|
}
|
||||||
|
//you have this effect while intersecting
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IntersectingWater{
|
||||||
|
pub viscosity:i64,
|
||||||
|
pub density:i64,
|
||||||
|
pub current:glam::Vec3,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IntersectingAccelerator{
|
||||||
|
pub acceleration:glam::Vec3
|
||||||
|
}
|
||||||
|
//All models can be given these attributes
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameMechanicJumpLimit{
|
||||||
|
pub count:u32,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameMechanicBooster{
|
||||||
|
pub velocity:glam::Vec3,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ZoneBehaviour{
|
||||||
|
//Start is indexed
|
||||||
|
//Checkpoints are indexed
|
||||||
|
Finish,
|
||||||
|
Anitcheat,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameMechanicZone{
|
||||||
|
pub mode_id:u32,
|
||||||
|
pub behaviour:ZoneBehaviour,
|
||||||
|
}
|
||||||
|
// enum TrapCondition{
|
||||||
|
// FasterThan(i64),
|
||||||
|
// SlowerThan(i64),
|
||||||
|
// InRange(i64,i64),
|
||||||
|
// OutsideRange(i64,i64),
|
||||||
|
// }
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum StageElementBehaviour{
|
||||||
|
//Spawn,//The behaviour of stepping on a spawn setting the spawnid
|
||||||
|
SpawnAt,
|
||||||
|
Trigger,
|
||||||
|
Teleport,
|
||||||
|
Platform,
|
||||||
|
//Speedtrap(TrapCondition),//Acts as a trigger with a speed condition
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameMechanicStageElement{
|
||||||
|
pub mode_id:u32,
|
||||||
|
pub stage_id:u32,//which spawn to send to
|
||||||
|
pub force:bool,//allow setting to lower spawn id i.e. 7->3
|
||||||
|
pub behaviour:StageElementBehaviour
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameMechanicWormhole{//(position,angles)*=origin.transform.inverse()*destination.transform
|
||||||
|
pub model_id:u32,
|
||||||
|
}
|
||||||
|
#[derive(Default,Clone)]
|
||||||
|
pub struct GameMechanicAttributes{
|
||||||
|
pub jump_limit:Option<GameMechanicJumpLimit>,
|
||||||
|
pub booster:Option<GameMechanicBooster>,
|
||||||
|
pub zone:Option<GameMechanicZone>,
|
||||||
|
pub stage_element:Option<GameMechanicStageElement>,
|
||||||
|
pub wormhole:Option<GameMechanicWormhole>,//stage_element and wormhole are in conflict
|
||||||
|
}
|
||||||
|
#[derive(Default,Clone)]
|
||||||
|
pub struct ContactingAttributes{
|
||||||
|
pub elasticity:Option<u32>,//[1/2^32,1] 0=None (elasticity+1)/2^32
|
||||||
|
//friction?
|
||||||
|
pub surf:Option<ContactingSurf>,
|
||||||
|
pub ladder:Option<ContactingLadder>,
|
||||||
|
}
|
||||||
|
#[derive(Default,Clone)]
|
||||||
|
pub struct IntersectingAttributes{
|
||||||
|
pub water:Option<IntersectingWater>,
|
||||||
|
pub accelerator:Option<IntersectingAccelerator>,
|
||||||
|
}
|
||||||
|
//Spawn(u32) NO! spawns are indexed in the map header instead of marked with attibutes
|
||||||
|
pub enum CollisionAttributes{
|
||||||
|
Decoration,//visual only
|
||||||
|
Contact{//track whether you are contacting the object
|
||||||
|
contacting:ContactingAttributes,
|
||||||
|
general:GameMechanicAttributes,
|
||||||
|
},
|
||||||
|
Intersect{//track whether you are intersecting the object
|
||||||
|
intersecting:IntersectingAttributes,
|
||||||
|
general:GameMechanicAttributes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
impl std::default::Default for CollisionAttributes{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Contact{
|
||||||
|
contacting:ContactingAttributes::default(),
|
||||||
|
general:GameMechanicAttributes::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:[f32;4]) -> Vec<IndexedModel>{
|
||||||
|
let mut unique_vertex_index = std::collections::HashMap::<obj::IndexTuple,u32>::new();
|
||||||
|
return data.objects.iter().map(|object|{
|
||||||
|
unique_vertex_index.clear();
|
||||||
|
let mut unique_vertices = Vec::new();
|
||||||
|
let groups = object.groups.iter().map(|group|{
|
||||||
|
IndexedGroup{
|
||||||
|
texture:None,
|
||||||
|
polys:group.polys.iter().map(|poly|{
|
||||||
|
IndexedPolygon{
|
||||||
|
vertices:poly.0.iter().map(|&tup|{
|
||||||
|
if let Some(&i)=unique_vertex_index.get(&tup){
|
||||||
|
i
|
||||||
|
}else{
|
||||||
|
let i=unique_vertices.len() as u32;
|
||||||
|
unique_vertices.push(IndexedVertex{
|
||||||
|
pos: tup.0 as u32,
|
||||||
|
tex: tup.1.unwrap() as u32,
|
||||||
|
normal: tup.2.unwrap() as u32,
|
||||||
|
color: 0,
|
||||||
|
});
|
||||||
|
unique_vertex_index.insert(tup,i);
|
||||||
|
i
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
IndexedModel{
|
||||||
|
unique_pos: data.position.clone(),
|
||||||
|
unique_tex: data.texture.clone(),
|
||||||
|
unique_normal: data.normal.clone(),
|
||||||
|
unique_color: vec![color],
|
||||||
|
unique_vertices,
|
||||||
|
groups,
|
||||||
|
instances:Vec::new(),
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
1288
src/physics.rs
Normal file
1288
src/physics.rs
Normal file
File diff suppressed because it is too large
Load Diff
506
src/primitives.rs
Normal file
506
src/primitives.rs
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
use crate::model::{IndexedModel, IndexedPolygon, IndexedGroup, IndexedVertex};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Primitives{
|
||||||
|
Sphere,
|
||||||
|
Cube,
|
||||||
|
Cylinder,
|
||||||
|
Wedge,
|
||||||
|
CornerWedge,
|
||||||
|
}
|
||||||
|
#[derive(Hash,PartialEq,Eq)]
|
||||||
|
pub enum CubeFace{
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
Back,
|
||||||
|
Left,
|
||||||
|
Bottom,
|
||||||
|
Front,
|
||||||
|
}
|
||||||
|
const CUBE_DEFAULT_TEXTURE_COORDS:[[f32;2];4]=[[0.0,0.0],[1.0,0.0],[1.0,1.0],[0.0,1.0]];
|
||||||
|
const CUBE_DEFAULT_VERTICES:[[f32;3];8]=[
|
||||||
|
[-1.,-1., 1.],//0 left bottom back
|
||||||
|
[ 1.,-1., 1.],//1 right bottom back
|
||||||
|
[ 1., 1., 1.],//2 right top back
|
||||||
|
[-1., 1., 1.],//3 left top back
|
||||||
|
[-1., 1.,-1.],//4 left top front
|
||||||
|
[ 1., 1.,-1.],//5 right top front
|
||||||
|
[ 1.,-1.,-1.],//6 right bottom front
|
||||||
|
[-1.,-1.,-1.],//7 left bottom front
|
||||||
|
];
|
||||||
|
const CUBE_DEFAULT_NORMALS:[[f32;3];6]=[
|
||||||
|
[ 1., 0., 0.],//CubeFace::Right
|
||||||
|
[ 0., 1., 0.],//CubeFace::Top
|
||||||
|
[ 0., 0., 1.],//CubeFace::Back
|
||||||
|
[-1., 0., 0.],//CubeFace::Left
|
||||||
|
[ 0.,-1., 0.],//CubeFace::Bottom
|
||||||
|
[ 0., 0.,-1.],//CubeFace::Front
|
||||||
|
];
|
||||||
|
const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[
|
||||||
|
// right (1, 0, 0)
|
||||||
|
[
|
||||||
|
[6,2,0],//[vertex,tex,norm]
|
||||||
|
[5,1,0],
|
||||||
|
[2,0,0],
|
||||||
|
[1,3,0],
|
||||||
|
],
|
||||||
|
// top (0, 1, 0)
|
||||||
|
[
|
||||||
|
[5,3,1],
|
||||||
|
[4,2,1],
|
||||||
|
[3,1,1],
|
||||||
|
[2,0,1],
|
||||||
|
],
|
||||||
|
// back (0, 0, 1)
|
||||||
|
[
|
||||||
|
[0,3,2],
|
||||||
|
[1,2,2],
|
||||||
|
[2,1,2],
|
||||||
|
[3,0,2],
|
||||||
|
],
|
||||||
|
// left (-1, 0, 0)
|
||||||
|
[
|
||||||
|
[0,2,3],
|
||||||
|
[3,1,3],
|
||||||
|
[4,0,3],
|
||||||
|
[7,3,3],
|
||||||
|
],
|
||||||
|
// bottom (0,-1, 0)
|
||||||
|
[
|
||||||
|
[1,1,4],
|
||||||
|
[0,0,4],
|
||||||
|
[7,3,4],
|
||||||
|
[6,2,4],
|
||||||
|
],
|
||||||
|
// front (0, 0,-1)
|
||||||
|
[
|
||||||
|
[4,1,5],
|
||||||
|
[5,0,5],
|
||||||
|
[6,3,5],
|
||||||
|
[7,2,5],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Hash,PartialEq,Eq)]
|
||||||
|
pub enum WedgeFace{
|
||||||
|
Right,
|
||||||
|
TopFront,
|
||||||
|
Back,
|
||||||
|
Left,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
const WEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
|
||||||
|
[ 1., 0., 0.],//Wedge::Right
|
||||||
|
[ 0., 1.,-1.],//Wedge::TopFront
|
||||||
|
[ 0., 0., 1.],//Wedge::Back
|
||||||
|
[-1., 0., 0.],//Wedge::Left
|
||||||
|
[ 0.,-1., 0.],//Wedge::Bottom
|
||||||
|
];
|
||||||
|
/*
|
||||||
|
local cornerWedgeVerticies = {
|
||||||
|
Vector3.new(-1/2,-1/2,-1/2),7
|
||||||
|
Vector3.new(-1/2,-1/2, 1/2),0
|
||||||
|
Vector3.new( 1/2,-1/2,-1/2),6
|
||||||
|
Vector3.new( 1/2,-1/2, 1/2),1
|
||||||
|
Vector3.new( 1/2, 1/2,-1/2),5
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#[derive(Hash,PartialEq,Eq)]
|
||||||
|
pub enum CornerWedgeFace{
|
||||||
|
Right,
|
||||||
|
TopBack,
|
||||||
|
TopLeft,
|
||||||
|
Bottom,
|
||||||
|
Front,
|
||||||
|
}
|
||||||
|
const CORNERWEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
|
||||||
|
[ 1., 0., 0.],//CornerWedge::Right
|
||||||
|
[ 0., 1., 1.],//CornerWedge::BackTop
|
||||||
|
[-1., 1., 0.],//CornerWedge::LeftTop
|
||||||
|
[ 0.,-1., 0.],//CornerWedge::Bottom
|
||||||
|
[ 0., 0.,-1.],//CornerWedge::Front
|
||||||
|
];
|
||||||
|
//HashMap fits this use case perfectly but feels like using a sledgehammer to drive a nail
|
||||||
|
pub fn unit_sphere()->crate::model::IndexedModel{
|
||||||
|
let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/suzanne.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()).remove(0);
|
||||||
|
for pos in indexed_model.unique_pos.iter_mut(){
|
||||||
|
pos[0]=pos[0]*0.5;
|
||||||
|
pos[1]=pos[1]*0.5;
|
||||||
|
pos[2]=pos[2]*0.5;
|
||||||
|
}
|
||||||
|
indexed_model
|
||||||
|
}
|
||||||
|
pub type CubeFaceDescription=std::collections::HashMap::<CubeFace,FaceDescription>;
|
||||||
|
pub fn unit_cube()->crate::model::IndexedModel{
|
||||||
|
let mut t=CubeFaceDescription::new();
|
||||||
|
t.insert(CubeFace::Right,FaceDescription::default());
|
||||||
|
t.insert(CubeFace::Top,FaceDescription::default());
|
||||||
|
t.insert(CubeFace::Back,FaceDescription::default());
|
||||||
|
t.insert(CubeFace::Left,FaceDescription::default());
|
||||||
|
t.insert(CubeFace::Bottom,FaceDescription::default());
|
||||||
|
t.insert(CubeFace::Front,FaceDescription::default());
|
||||||
|
generate_partial_unit_cube(t)
|
||||||
|
}
|
||||||
|
const TEAPOT_TRANSFORM:glam::Mat3=glam::mat3(glam::vec3(0.0,0.1,0.0),glam::vec3(-0.1,0.0,0.0),glam::vec3(0.0,0.0,0.1));
|
||||||
|
pub fn unit_cylinder()->crate::model::IndexedModel{
|
||||||
|
let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teapot.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()).remove(0);
|
||||||
|
for pos in indexed_model.unique_pos.iter_mut(){
|
||||||
|
[pos[0],pos[1],pos[2]]=*(TEAPOT_TRANSFORM*glam::Vec3::from_array(*pos)).as_ref();
|
||||||
|
}
|
||||||
|
indexed_model
|
||||||
|
}
|
||||||
|
pub type WedgeFaceDescription=std::collections::HashMap::<WedgeFace,FaceDescription>;
|
||||||
|
pub fn unit_wedge()->crate::model::IndexedModel{
|
||||||
|
let mut t=WedgeFaceDescription::new();
|
||||||
|
t.insert(WedgeFace::Right,FaceDescription::default());
|
||||||
|
t.insert(WedgeFace::TopFront,FaceDescription::default());
|
||||||
|
t.insert(WedgeFace::Back,FaceDescription::default());
|
||||||
|
t.insert(WedgeFace::Left,FaceDescription::default());
|
||||||
|
t.insert(WedgeFace::Bottom,FaceDescription::default());
|
||||||
|
generate_partial_unit_wedge(t)
|
||||||
|
}
|
||||||
|
pub type CornerWedgeFaceDescription=std::collections::HashMap::<CornerWedgeFace,FaceDescription>;
|
||||||
|
pub fn unit_cornerwedge()->crate::model::IndexedModel{
|
||||||
|
let mut t=CornerWedgeFaceDescription::new();
|
||||||
|
t.insert(CornerWedgeFace::Right,FaceDescription::default());
|
||||||
|
t.insert(CornerWedgeFace::TopBack,FaceDescription::default());
|
||||||
|
t.insert(CornerWedgeFace::TopLeft,FaceDescription::default());
|
||||||
|
t.insert(CornerWedgeFace::Bottom,FaceDescription::default());
|
||||||
|
t.insert(CornerWedgeFace::Front,FaceDescription::default());
|
||||||
|
generate_partial_unit_cornerwedge(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy,Clone)]
|
||||||
|
pub struct FaceDescription{
|
||||||
|
pub texture:Option<u32>,
|
||||||
|
pub transform:glam::Affine2,
|
||||||
|
pub color:glam::Vec4,
|
||||||
|
}
|
||||||
|
impl std::default::Default for FaceDescription{
|
||||||
|
fn default()->Self {
|
||||||
|
Self{
|
||||||
|
texture:None,
|
||||||
|
transform:glam::Affine2::IDENTITY,
|
||||||
|
color:glam::vec4(1.0,1.0,1.0,0.0),//zero alpha to hide the default texture
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FaceDescription{
|
||||||
|
pub fn new(texture:u32,transform:glam::Affine2,color:glam::Vec4)->Self{
|
||||||
|
Self{texture:Some(texture),transform,color}
|
||||||
|
}
|
||||||
|
pub fn from_texture(texture:u32)->Self{
|
||||||
|
Self{
|
||||||
|
texture:Some(texture),
|
||||||
|
transform:glam::Affine2::IDENTITY,
|
||||||
|
color:glam::Vec4::ONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: it's probably better to use a shared vertex buffer between all primitives and use indexed rendering instead of generating a unique vertex buffer for each primitive.
|
||||||
|
//implementation: put all roblox primitives into one model.groups <- this won't work but I forget why
|
||||||
|
pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate::model::IndexedModel{
|
||||||
|
let mut generated_pos=Vec::<[f32;3]>::new();
|
||||||
|
let mut generated_tex=Vec::new();
|
||||||
|
let mut generated_normal=Vec::new();
|
||||||
|
let mut generated_color=Vec::new();
|
||||||
|
let mut generated_vertices=Vec::new();
|
||||||
|
let mut groups=Vec::new();
|
||||||
|
let mut transforms=Vec::new();
|
||||||
|
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
||||||
|
for (face,face_description) in face_descriptions.into_iter(){
|
||||||
|
//assume that scanning short lists is faster than hashing.
|
||||||
|
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
||||||
|
transform_index
|
||||||
|
}else{
|
||||||
|
//create new transform_index
|
||||||
|
let transform_index=transforms.len();
|
||||||
|
transforms.push(face_description.transform);
|
||||||
|
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
||||||
|
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
|
||||||
|
}
|
||||||
|
transform_index
|
||||||
|
} as u32;
|
||||||
|
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
|
||||||
|
color_index
|
||||||
|
}else{
|
||||||
|
//create new color_index
|
||||||
|
let color_index=generated_color.len();
|
||||||
|
generated_color.push(*face_description.color.as_ref());
|
||||||
|
color_index
|
||||||
|
} as u32;
|
||||||
|
let face_id=match face{
|
||||||
|
CubeFace::Right => 0,
|
||||||
|
CubeFace::Top => 1,
|
||||||
|
CubeFace::Back => 2,
|
||||||
|
CubeFace::Left => 3,
|
||||||
|
CubeFace::Bottom => 4,
|
||||||
|
CubeFace::Front => 5,
|
||||||
|
};
|
||||||
|
//always push normal
|
||||||
|
let normal_index=generated_normal.len() as u32;
|
||||||
|
generated_normal.push(CUBE_DEFAULT_NORMALS[face_id]);
|
||||||
|
//push vertices as they are needed
|
||||||
|
groups.push(IndexedGroup{
|
||||||
|
texture:face_description.texture,
|
||||||
|
polys:vec![IndexedPolygon{
|
||||||
|
vertices:CUBE_DEFAULT_POLYS[face_id].map(|tup|{
|
||||||
|
let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize];
|
||||||
|
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
|
||||||
|
pos_index
|
||||||
|
}else{
|
||||||
|
//create new pos_index
|
||||||
|
let pos_index=generated_pos.len();
|
||||||
|
generated_pos.push(pos);
|
||||||
|
pos_index
|
||||||
|
} as u32;
|
||||||
|
//always push vertex
|
||||||
|
let vertex=IndexedVertex{
|
||||||
|
pos:pos_index,
|
||||||
|
tex:tup[1]+4*transform_index,
|
||||||
|
normal:normal_index,
|
||||||
|
color:color_index,
|
||||||
|
};
|
||||||
|
let vert_index=generated_vertices.len();
|
||||||
|
generated_vertices.push(vertex);
|
||||||
|
vert_index as u32
|
||||||
|
}).to_vec(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
IndexedModel{
|
||||||
|
unique_pos:generated_pos,
|
||||||
|
unique_tex:generated_tex,
|
||||||
|
unique_normal:generated_normal,
|
||||||
|
unique_color:generated_color,
|
||||||
|
unique_vertices:generated_vertices,
|
||||||
|
groups,
|
||||||
|
instances:Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//don't think too hard about the copy paste because this is all going into the map tool eventually...
|
||||||
|
pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crate::model::IndexedModel{
|
||||||
|
let wedge_default_polys=vec![
|
||||||
|
// right (1, 0, 0)
|
||||||
|
vec![
|
||||||
|
[6,2,0],//[vertex,tex,norm]
|
||||||
|
[2,0,0],
|
||||||
|
[1,3,0],
|
||||||
|
],
|
||||||
|
// FrontTop (0, 1, -1)
|
||||||
|
vec![
|
||||||
|
[3,1,1],
|
||||||
|
[2,0,1],
|
||||||
|
[6,3,1],
|
||||||
|
[7,2,1],
|
||||||
|
],
|
||||||
|
// back (0, 0, 1)
|
||||||
|
vec![
|
||||||
|
[0,3,2],
|
||||||
|
[1,2,2],
|
||||||
|
[2,1,2],
|
||||||
|
[3,0,2],
|
||||||
|
],
|
||||||
|
// left (-1, 0, 0)
|
||||||
|
vec![
|
||||||
|
[0,2,3],
|
||||||
|
[3,1,3],
|
||||||
|
[7,3,3],
|
||||||
|
],
|
||||||
|
// bottom (0,-1, 0)
|
||||||
|
vec![
|
||||||
|
[1,1,4],
|
||||||
|
[0,0,4],
|
||||||
|
[7,3,4],
|
||||||
|
[6,2,4],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
let mut generated_pos=Vec::<[f32;3]>::new();
|
||||||
|
let mut generated_tex=Vec::new();
|
||||||
|
let mut generated_normal=Vec::new();
|
||||||
|
let mut generated_color=Vec::new();
|
||||||
|
let mut generated_vertices=Vec::new();
|
||||||
|
let mut groups=Vec::new();
|
||||||
|
let mut transforms=Vec::new();
|
||||||
|
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
||||||
|
for (face,face_description) in face_descriptions.into_iter(){
|
||||||
|
//assume that scanning short lists is faster than hashing.
|
||||||
|
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
||||||
|
transform_index
|
||||||
|
}else{
|
||||||
|
//create new transform_index
|
||||||
|
let transform_index=transforms.len();
|
||||||
|
transforms.push(face_description.transform);
|
||||||
|
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
||||||
|
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
|
||||||
|
}
|
||||||
|
transform_index
|
||||||
|
} as u32;
|
||||||
|
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
|
||||||
|
color_index
|
||||||
|
}else{
|
||||||
|
//create new color_index
|
||||||
|
let color_index=generated_color.len();
|
||||||
|
generated_color.push(*face_description.color.as_ref());
|
||||||
|
color_index
|
||||||
|
} as u32;
|
||||||
|
let face_id=match face{
|
||||||
|
WedgeFace::Right => 0,
|
||||||
|
WedgeFace::TopFront => 1,
|
||||||
|
WedgeFace::Back => 2,
|
||||||
|
WedgeFace::Left => 3,
|
||||||
|
WedgeFace::Bottom => 4,
|
||||||
|
};
|
||||||
|
//always push normal
|
||||||
|
let normal_index=generated_normal.len() as u32;
|
||||||
|
generated_normal.push(WEDGE_DEFAULT_NORMALS[face_id]);
|
||||||
|
//push vertices as they are needed
|
||||||
|
groups.push(IndexedGroup{
|
||||||
|
texture:face_description.texture,
|
||||||
|
polys:vec![IndexedPolygon{
|
||||||
|
vertices:wedge_default_polys[face_id].iter().map(|tup|{
|
||||||
|
let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize];
|
||||||
|
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
|
||||||
|
pos_index
|
||||||
|
}else{
|
||||||
|
//create new pos_index
|
||||||
|
let pos_index=generated_pos.len();
|
||||||
|
generated_pos.push(pos);
|
||||||
|
pos_index
|
||||||
|
} as u32;
|
||||||
|
//always push vertex
|
||||||
|
let vertex=IndexedVertex{
|
||||||
|
pos:pos_index,
|
||||||
|
tex:tup[1]+4*transform_index,
|
||||||
|
normal:normal_index,
|
||||||
|
color:color_index,
|
||||||
|
};
|
||||||
|
let vert_index=generated_vertices.len();
|
||||||
|
generated_vertices.push(vertex);
|
||||||
|
vert_index as u32
|
||||||
|
}).collect(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
IndexedModel{
|
||||||
|
unique_pos:generated_pos,
|
||||||
|
unique_tex:generated_tex,
|
||||||
|
unique_normal:generated_normal,
|
||||||
|
unique_color:generated_color,
|
||||||
|
unique_vertices:generated_vertices,
|
||||||
|
groups,
|
||||||
|
instances:Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescription)->crate::model::IndexedModel{
|
||||||
|
let cornerwedge_default_polys=vec![
|
||||||
|
// right (1, 0, 0)
|
||||||
|
vec![
|
||||||
|
[6,2,0],//[vertex,tex,norm]
|
||||||
|
[5,1,0],
|
||||||
|
[1,3,0],
|
||||||
|
],
|
||||||
|
// BackTop (0, 1, 1)
|
||||||
|
vec![
|
||||||
|
[5,3,1],
|
||||||
|
[0,1,1],
|
||||||
|
[1,0,1],
|
||||||
|
],
|
||||||
|
// LeftTop (-1, 1, 0)
|
||||||
|
vec![
|
||||||
|
[5,3,2],
|
||||||
|
[7,2,2],
|
||||||
|
[0,1,2],
|
||||||
|
],
|
||||||
|
// bottom (0,-1, 0)
|
||||||
|
vec![
|
||||||
|
[1,1,3],
|
||||||
|
[0,0,3],
|
||||||
|
[7,3,3],
|
||||||
|
[6,2,3],
|
||||||
|
],
|
||||||
|
// front (0, 0,-1)
|
||||||
|
vec![
|
||||||
|
[5,0,4],
|
||||||
|
[6,3,4],
|
||||||
|
[7,2,4],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
let mut generated_pos=Vec::<[f32;3]>::new();
|
||||||
|
let mut generated_tex=Vec::new();
|
||||||
|
let mut generated_normal=Vec::new();
|
||||||
|
let mut generated_color=Vec::new();
|
||||||
|
let mut generated_vertices=Vec::new();
|
||||||
|
let mut groups=Vec::new();
|
||||||
|
let mut transforms=Vec::new();
|
||||||
|
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
||||||
|
for (face,face_description) in face_descriptions.into_iter(){
|
||||||
|
//assume that scanning short lists is faster than hashing.
|
||||||
|
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
||||||
|
transform_index
|
||||||
|
}else{
|
||||||
|
//create new transform_index
|
||||||
|
let transform_index=transforms.len();
|
||||||
|
transforms.push(face_description.transform);
|
||||||
|
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
||||||
|
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
|
||||||
|
}
|
||||||
|
transform_index
|
||||||
|
} as u32;
|
||||||
|
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
|
||||||
|
color_index
|
||||||
|
}else{
|
||||||
|
//create new color_index
|
||||||
|
let color_index=generated_color.len();
|
||||||
|
generated_color.push(*face_description.color.as_ref());
|
||||||
|
color_index
|
||||||
|
} as u32;
|
||||||
|
let face_id=match face{
|
||||||
|
CornerWedgeFace::Right => 0,
|
||||||
|
CornerWedgeFace::TopBack => 1,
|
||||||
|
CornerWedgeFace::TopLeft => 2,
|
||||||
|
CornerWedgeFace::Bottom => 3,
|
||||||
|
CornerWedgeFace::Front => 4,
|
||||||
|
};
|
||||||
|
//always push normal
|
||||||
|
let normal_index=generated_normal.len() as u32;
|
||||||
|
generated_normal.push(CORNERWEDGE_DEFAULT_NORMALS[face_id]);
|
||||||
|
//push vertices as they are needed
|
||||||
|
groups.push(IndexedGroup{
|
||||||
|
texture:face_description.texture,
|
||||||
|
polys:vec![IndexedPolygon{
|
||||||
|
vertices:cornerwedge_default_polys[face_id].iter().map(|tup|{
|
||||||
|
let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize];
|
||||||
|
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
|
||||||
|
pos_index
|
||||||
|
}else{
|
||||||
|
//create new pos_index
|
||||||
|
let pos_index=generated_pos.len();
|
||||||
|
generated_pos.push(pos);
|
||||||
|
pos_index
|
||||||
|
} as u32;
|
||||||
|
//always push vertex
|
||||||
|
let vertex=IndexedVertex{
|
||||||
|
pos:pos_index,
|
||||||
|
tex:tup[1]+4*transform_index,
|
||||||
|
normal:normal_index,
|
||||||
|
color:color_index,
|
||||||
|
};
|
||||||
|
let vert_index=generated_vertices.len();
|
||||||
|
generated_vertices.push(vertex);
|
||||||
|
vert_index as u32
|
||||||
|
}).collect(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
IndexedModel{
|
||||||
|
unique_pos:generated_pos,
|
||||||
|
unique_tex:generated_tex,
|
||||||
|
unique_normal:generated_normal,
|
||||||
|
unique_color:generated_color,
|
||||||
|
unique_vertices:generated_vertices,
|
||||||
|
groups,
|
||||||
|
instances:Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
123
src/settings.rs
Normal file
123
src/settings.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
struct Ratio{
|
||||||
|
ratio:f64,
|
||||||
|
}
|
||||||
|
enum DerivedFov{
|
||||||
|
FromScreenAspect,
|
||||||
|
FromAspect(Ratio),
|
||||||
|
}
|
||||||
|
enum Fov{
|
||||||
|
Exactly{x:f64,y:f64},
|
||||||
|
DeriveX{x:DerivedFov,y:f64},
|
||||||
|
DeriveY{x:f64,y:DerivedFov},
|
||||||
|
}
|
||||||
|
impl Default for Fov{
|
||||||
|
fn default() -> Self {
|
||||||
|
Fov::DeriveX{x:DerivedFov::FromScreenAspect,y:1.0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Sensitivity{
|
||||||
|
Exactly{x:f64,y:f64},
|
||||||
|
DeriveX{x:Ratio,y:f64},
|
||||||
|
DeriveY{x:f64,y:Ratio},
|
||||||
|
}
|
||||||
|
impl Default for Sensitivity{
|
||||||
|
fn default() -> Self {
|
||||||
|
Sensitivity::DeriveY{x:0.001,y:Ratio{ratio:1.0}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UserSettings{
|
||||||
|
fov:Fov,
|
||||||
|
sensitivity:Sensitivity,
|
||||||
|
}
|
||||||
|
impl UserSettings{
|
||||||
|
pub fn calculate_fov(&self,zoom:f64,screen_size:&glam::UVec2)->glam::DVec2{
|
||||||
|
zoom*match &self.fov{
|
||||||
|
&Fov::Exactly{x,y}=>glam::dvec2(x,y),
|
||||||
|
Fov::DeriveX{x,y}=>match x{
|
||||||
|
DerivedFov::FromScreenAspect=>glam::dvec2(y*(screen_size.x as f64/screen_size.y as f64),*y),
|
||||||
|
DerivedFov::FromAspect(ratio)=>glam::dvec2(y*ratio.ratio,*y),
|
||||||
|
},
|
||||||
|
Fov::DeriveY{x,y}=>match y{
|
||||||
|
DerivedFov::FromScreenAspect=>glam::dvec2(*x,x*(screen_size.y as f64/screen_size.x as f64)),
|
||||||
|
DerivedFov::FromAspect(ratio)=>glam::dvec2(*x,x*ratio.ratio),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn calculate_sensitivity(&self)->glam::DVec2{
|
||||||
|
match &self.sensitivity{
|
||||||
|
&Sensitivity::Exactly{x,y}=>glam::dvec2(x,y),
|
||||||
|
Sensitivity::DeriveX{x,y}=>glam::dvec2(y*x.ratio,*y),
|
||||||
|
Sensitivity::DeriveY{x,y}=>glam::dvec2(*x,x*y.ratio),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//sensitivity is raw input dots (i.e. dpi = dots per inch) to radians conversion factor
|
||||||
|
sensitivity_x=0.001
|
||||||
|
sensitivity_y_from_x_ratio=1
|
||||||
|
Sensitivity::DeriveY{x:0.0.001,y:DerivedSensitivity{ratio:1.0}}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn read_user_settings()->UserSettings{
|
||||||
|
let mut cfg=configparser::ini::Ini::new();
|
||||||
|
if let Ok(_)=cfg.load("settings.conf"){
|
||||||
|
let (cfg_fov_x,cfg_fov_y)=(cfg.getfloat("camera","fov_x"),cfg.getfloat("camera","fov_y"));
|
||||||
|
let fov=match(cfg_fov_x,cfg_fov_y){
|
||||||
|
(Ok(Some(fov_x)),Ok(Some(fov_y)))=>Fov::Exactly {
|
||||||
|
x:fov_x,
|
||||||
|
y:fov_y
|
||||||
|
},
|
||||||
|
(Ok(Some(fov_x)),Ok(None))=>Fov::DeriveY{
|
||||||
|
x:fov_x,
|
||||||
|
y:if let Ok(Some(fov_y_from_x_ratio))=cfg.getfloat("camera","fov_y_from_x_ratio"){
|
||||||
|
DerivedFov::FromAspect(Ratio{ratio:fov_y_from_x_ratio})
|
||||||
|
}else{
|
||||||
|
DerivedFov::FromScreenAspect
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Ok(None),Ok(Some(fov_y)))=>Fov::DeriveX{
|
||||||
|
x:if let Ok(Some(fov_x_from_y_ratio))=cfg.getfloat("camera","fov_x_from_y_ratio"){
|
||||||
|
DerivedFov::FromAspect(Ratio{ratio:fov_x_from_y_ratio})
|
||||||
|
}else{
|
||||||
|
DerivedFov::FromScreenAspect
|
||||||
|
},
|
||||||
|
y:fov_y,
|
||||||
|
},
|
||||||
|
_=>{
|
||||||
|
Fov::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let (cfg_sensitivity_x,cfg_sensitivity_y)=(cfg.getfloat("camera","sensitivity_x"),cfg.getfloat("camera","sensitivity_y"));
|
||||||
|
let sensitivity=match(cfg_sensitivity_x,cfg_sensitivity_y){
|
||||||
|
(Ok(Some(sensitivity_x)),Ok(Some(sensitivity_y)))=>Sensitivity::Exactly {
|
||||||
|
x:sensitivity_x,
|
||||||
|
y:sensitivity_y
|
||||||
|
},
|
||||||
|
(Ok(Some(sensitivity_x)),Ok(None))=>Sensitivity::DeriveY{
|
||||||
|
x:sensitivity_x,
|
||||||
|
y:Ratio{
|
||||||
|
ratio:if let Ok(Some(sensitivity_y_from_x_ratio))=cfg.getfloat("camera","sensitivity_y_from_x_ratio"){sensitivity_y_from_x_ratio}else{1.0}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Ok(None),Ok(Some(sensitivity_y)))=>Sensitivity::DeriveX{
|
||||||
|
x:Ratio{
|
||||||
|
ratio:if let Ok(Some(sensitivity_x_from_y_ratio))=cfg.getfloat("camera","sensitivity_x_from_y_ratio"){sensitivity_x_from_y_ratio}else{1.0}
|
||||||
|
},
|
||||||
|
y:sensitivity_y,
|
||||||
|
},
|
||||||
|
_=>{
|
||||||
|
Sensitivity::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
UserSettings{
|
||||||
|
fov,
|
||||||
|
sensitivity,
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
UserSettings::default()
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,8 @@ struct Camera {
|
|||||||
proj_inv: mat4x4<f32>,
|
proj_inv: mat4x4<f32>,
|
||||||
// from world to camera
|
// from world to camera
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
// camera position
|
// from camera to world
|
||||||
cam_pos: vec4<f32>,
|
view_inv: mat4x4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
//group 0 is the camera
|
//group 0 is the camera
|
||||||
@ -31,8 +31,7 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
|
|||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
|
|
||||||
// transposition = inversion for this orthonormal matrix
|
let inv_model_view = mat3x3<f32>(camera.view_inv[0].xyz, camera.view_inv[1].xyz, camera.view_inv[2].xyz);
|
||||||
let inv_model_view = transpose(mat3x3<f32>(camera.view[0].xyz, camera.view[1].xyz, camera.view[2].xyz));
|
|
||||||
let unprojected = camera.proj_inv * pos;
|
let unprojected = camera.proj_inv * pos;
|
||||||
|
|
||||||
var result: SkyOutput;
|
var result: SkyOutput;
|
||||||
@ -41,19 +40,22 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_ENTITY_INSTANCES=1024;
|
struct ModelInstance{
|
||||||
//group 1 is the model
|
transform:mat4x4<f32>,
|
||||||
@group(1)
|
normal_transform:mat3x3<f32>,
|
||||||
@binding(0)
|
color:vec4<f32>,
|
||||||
var<uniform> entity_transforms: array<mat4x4<f32>,MAX_ENTITY_INSTANCES>;
|
}
|
||||||
//var<uniform> entity_texture_transforms: array<mat3x3<f32>,MAX_ENTITY_INSTANCES>;
|
|
||||||
//my fancy idea is to create a megatexture for each model that includes all the textures each intance will need
|
//my fancy idea is to create a megatexture for each model that includes all the textures each intance will need
|
||||||
//the texture transform then maps the texture coordinates to the location of the specific texture
|
//the texture transform then maps the texture coordinates to the location of the specific texture
|
||||||
//how to do no texture?
|
//group 1 is the model
|
||||||
@group(1)
|
const MAX_MODEL_INSTANCES=4096;
|
||||||
|
@group(2)
|
||||||
|
@binding(0)
|
||||||
|
var<uniform> model_instances: array<ModelInstance, MAX_MODEL_INSTANCES>;
|
||||||
|
@group(2)
|
||||||
@binding(1)
|
@binding(1)
|
||||||
var model_texture: texture_2d<f32>;
|
var model_texture: texture_2d<f32>;
|
||||||
@group(1)
|
@group(2)
|
||||||
@binding(2)
|
@binding(2)
|
||||||
var model_sampler: sampler;
|
var model_sampler: sampler;
|
||||||
|
|
||||||
@ -62,6 +64,8 @@ struct EntityOutputTexture {
|
|||||||
@location(1) texture: vec2<f32>,
|
@location(1) texture: vec2<f32>,
|
||||||
@location(2) normal: vec3<f32>,
|
@location(2) normal: vec3<f32>,
|
||||||
@location(3) view: vec3<f32>,
|
@location(3) view: vec3<f32>,
|
||||||
|
@location(4) color: vec4<f32>,
|
||||||
|
@location(5) @interpolate(flat) model_color: vec4<f32>,
|
||||||
};
|
};
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_entity_texture(
|
fn vs_entity_texture(
|
||||||
@ -69,27 +73,30 @@ fn vs_entity_texture(
|
|||||||
@location(0) pos: vec3<f32>,
|
@location(0) pos: vec3<f32>,
|
||||||
@location(1) texture: vec2<f32>,
|
@location(1) texture: vec2<f32>,
|
||||||
@location(2) normal: vec3<f32>,
|
@location(2) normal: vec3<f32>,
|
||||||
|
@location(3) color: vec4<f32>,
|
||||||
) -> EntityOutputTexture {
|
) -> EntityOutputTexture {
|
||||||
var position: vec4<f32> = entity_transforms[instance] * vec4<f32>(pos, 1.0);
|
var position: vec4<f32> = model_instances[instance].transform * vec4<f32>(pos, 1.0);
|
||||||
var result: EntityOutputTexture;
|
var result: EntityOutputTexture;
|
||||||
result.normal = (entity_transforms[instance] * vec4<f32>(normal, 0.0)).xyz;
|
result.normal = model_instances[instance].normal_transform * normal;
|
||||||
result.texture=texture;//(entity_texture_transforms[instance] * vec3<f32>(texture, 1.0)).xy;
|
result.texture = texture;
|
||||||
result.view = position.xyz - camera.cam_pos.xyz;
|
result.color = color;
|
||||||
|
result.model_color = model_instances[instance].color;
|
||||||
|
result.view = position.xyz - camera.view_inv[3].xyz;//col(3)
|
||||||
result.position = camera.proj * camera.view * position;
|
result.position = camera.proj * camera.view * position;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//group 2 is the skybox texture
|
//group 2 is the skybox texture
|
||||||
@group(2)
|
@group(1)
|
||||||
@binding(0)
|
@binding(0)
|
||||||
var cube_texture: texture_cube<f32>;
|
var cube_texture: texture_cube<f32>;
|
||||||
@group(2)
|
@group(1)
|
||||||
@binding(1)
|
@binding(1)
|
||||||
var cube_sampler: sampler;
|
var cube_sampler: sampler;
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_sky(vertex: SkyOutput) -> @location(0) vec4<f32> {
|
fn fs_sky(vertex: SkyOutput) -> @location(0) vec4<f32> {
|
||||||
return textureSample(cube_texture, model_sampler, vertex.sampledir);
|
return textureSample(cube_texture, cube_sampler, vertex.sampledir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
@ -99,7 +106,7 @@ fn fs_entity_texture(vertex: EntityOutputTexture) -> @location(0) vec4<f32> {
|
|||||||
let d = dot(normal, incident);
|
let d = dot(normal, incident);
|
||||||
let reflected = incident - 2.0 * d * normal;
|
let reflected = incident - 2.0 * d * normal;
|
||||||
|
|
||||||
let fragment_color = textureSample(model_texture, model_sampler, vertex.texture).rgb;
|
let fragment_color = textureSample(model_texture, model_sampler, vertex.texture)*vertex.color;
|
||||||
let reflected_color = textureSample(cube_texture, cube_sampler, reflected).rgb;
|
let reflected_color = textureSample(cube_texture, cube_sampler, reflected).rgb;
|
||||||
return vec4<f32>(mix(vec3<f32>(0.1) + 0.5 * reflected_color,fragment_color,1.0-pow(1.0-abs(d),2.0)), 1.0);
|
return mix(vec4<f32>(vec3<f32>(0.05) + 0.2 * reflected_color,1.0),mix(vertex.model_color,vec4<f32>(fragment_color.rgb,1.0),fragment_color.a),0.5+0.5*abs(d));
|
||||||
}
|
}
|
||||||
|
237
src/timers.rs
Normal file
237
src/timers.rs
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
type TIME=crate::physics::TIME;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Timescale{
|
||||||
|
num:i64,
|
||||||
|
den:std::num::NonZeroU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Paused{}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Unpaused{}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PausedScaled{scale:Timescale}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct UnpausedScaled{scale:Timescale}
|
||||||
|
|
||||||
|
pub trait TimerState{}
|
||||||
|
impl TimerState for Paused{}
|
||||||
|
impl TimerState for Unpaused{}
|
||||||
|
impl TimerState for PausedScaled{}
|
||||||
|
impl TimerState for UnpausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsPaused{}
|
||||||
|
impl IsPaused for Paused{}
|
||||||
|
impl IsPaused for PausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsUnpaused{}
|
||||||
|
impl IsUnpaused for Unpaused{}
|
||||||
|
impl IsUnpaused for UnpausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsScaled{}
|
||||||
|
impl IsScaled for PausedScaled{}
|
||||||
|
impl IsScaled for UnpausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsUnscaled{}
|
||||||
|
impl IsUnscaled for Paused{}
|
||||||
|
impl IsUnscaled for Unpaused{}
|
||||||
|
|
||||||
|
//scaled timer wrapper
|
||||||
|
enum Scaled{
|
||||||
|
Paused(Timer<PausedScaled>),
|
||||||
|
Unpaused(Timer<UnpausedScaled>),
|
||||||
|
}
|
||||||
|
pub struct ScaledTimer{
|
||||||
|
timer:Scaled,
|
||||||
|
}
|
||||||
|
impl ScaledTimer{
|
||||||
|
pub fn unpaused()->Self{
|
||||||
|
Self{
|
||||||
|
timer:Scaled::Unpaused(unpaused_scaled(Timescale{num:1,den:std::num::NonZeroU64::new(1).unwrap()}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
match &self.timer{
|
||||||
|
Scaled::Paused(timer)=>timer.time(),
|
||||||
|
Scaled::Unpaused(timer)=>timer.time(time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn pause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Scaled::Paused(_)=>(),
|
||||||
|
Scaled::Unpaused(timer)=>self.timer=Scaled::Paused(timer.clone().pause(time)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn unpause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Scaled::Paused(timer)=>self.timer=Scaled::Unpaused(timer.clone().unpause(time)),
|
||||||
|
Scaled::Unpaused(_)=>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//unscaled timer wrapper
|
||||||
|
enum Unscaled{
|
||||||
|
Paused(Timer<Paused>),
|
||||||
|
Unpaused(Timer<Unpaused>),
|
||||||
|
}
|
||||||
|
pub struct UnscaledTimer{
|
||||||
|
timer:Unscaled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnscaledTimer{
|
||||||
|
pub fn unpaused()->Self{
|
||||||
|
Self{
|
||||||
|
timer:Unscaled::Unpaused(unpaused())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
match &self.timer{
|
||||||
|
Unscaled::Paused(timer)=>timer.time(),
|
||||||
|
Unscaled::Unpaused(timer)=>timer.time(time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn pause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Unscaled::Paused(_)=>(),
|
||||||
|
Unscaled::Unpaused(timer)=>self.timer=Unscaled::Paused(timer.clone().pause(time)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn unpause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Unscaled::Paused(timer)=>self.timer=Unscaled::Unpaused(timer.clone().unpause(time)),
|
||||||
|
Unscaled::Unpaused(_)=>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Timer<State:TimerState>{
|
||||||
|
offset:crate::physics::TIME,
|
||||||
|
state:State,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_offset(time:TIME,write_time:TIME)->TIME{
|
||||||
|
write_time-time
|
||||||
|
}
|
||||||
|
fn get_offset_scaled(time:TIME,write_time:TIME,scale:&Timescale)->TIME{
|
||||||
|
write_time-time*scale.num/scale.den.get() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paused()->Timer<Paused>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:Paused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unpaused()->Timer<Unpaused>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:Unpaused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn paused_scaled(scale:Timescale)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:PausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unpaused_scaled(scale:Timescale)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:UnpausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<Paused>{
|
||||||
|
pub fn time(&self)->TIME{
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
pub fn unpause(self,time:TIME)->Timer<Unpaused>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset(time,self.time()),
|
||||||
|
state:Unpaused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset(time,write_time);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(),&scale),
|
||||||
|
state:PausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<Unpaused>{
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
self.offset+time
|
||||||
|
}
|
||||||
|
pub fn pause(self,time:TIME)->Timer<Paused>{
|
||||||
|
Timer{
|
||||||
|
offset:self.time(time),
|
||||||
|
state:Paused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset(time,write_time);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(time),&scale),
|
||||||
|
state:UnpausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<PausedScaled>{
|
||||||
|
pub fn time(&self)->TIME{
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
pub fn unpause(self,time:TIME)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(),&self.state.scale),
|
||||||
|
state:UnpausedScaled{scale:self.state.scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset_scaled(time,write_time,&self.state.scale);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(),&scale),
|
||||||
|
state:PausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<UnpausedScaled>{
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
self.offset+time*self.state.scale.num/self.state.scale.den.get() as i64
|
||||||
|
}
|
||||||
|
pub fn pause(self,time:TIME)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:self.time(time),
|
||||||
|
state:PausedScaled{scale:self.state.scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset_scaled(time,write_time,&self.state.scale);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(time),&scale),
|
||||||
|
//self.offset+time*self.state.scale.num/self.state.scale.den.get() as i64-time*scale.num/scale.den.get() as i64
|
||||||
|
state:UnpausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_timer_unscaled(){
|
||||||
|
const ONE_SECOND:TIME=1_000_000_000;
|
||||||
|
let run_prepare=paused();
|
||||||
|
|
||||||
|
let run_start=run_prepare.unpause(ONE_SECOND);
|
||||||
|
let run_finish=run_start.pause(11*ONE_SECOND);
|
||||||
|
|
||||||
|
assert_eq!(run_finish.time(),10*ONE_SECOND);
|
||||||
|
}
|
107
src/worker.rs
Normal file
107
src/worker.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use std::thread;
|
||||||
|
use std::sync::{mpsc,Arc};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
//The goal here is to have a worker thread that parks itself when it runs out of work.
|
||||||
|
//The worker thread publishes the result of its work back to the worker object for every item in the work queue.
|
||||||
|
//The physics (target use case) knows when it has not changed the body, so not updating the value is also an option.
|
||||||
|
|
||||||
|
pub struct Worker<Task:Send,Value:Clone> {
|
||||||
|
sender: mpsc::Sender<Task>,
|
||||||
|
value:Arc<Mutex<Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Task:Send+'static,Value:Clone+Send+'static> Worker<Task,Value> {
|
||||||
|
pub fn new<F:FnMut(Task)->Value+Send+'static>(value:Value,mut f:F) -> Self {
|
||||||
|
let (sender, receiver) = mpsc::channel::<Task>();
|
||||||
|
let ret=Self {
|
||||||
|
sender,
|
||||||
|
value:Arc::new(Mutex::new(value)),
|
||||||
|
};
|
||||||
|
let value=ret.value.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
match receiver.recv() {
|
||||||
|
Ok(task) => {
|
||||||
|
let v=f(task);//make sure function is evaluated before lock is acquired
|
||||||
|
*value.lock()=v;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
println!("Worker stopping.",);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&self,task:Task)->Result<(), mpsc::SendError<Task>>{
|
||||||
|
self.sender.send(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grab_clone(&self)->Value{
|
||||||
|
self.value.lock().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CompatWorker<Task,Value:Clone,F>{
|
||||||
|
data:std::marker::PhantomData<Task>,
|
||||||
|
f:F,
|
||||||
|
value:Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Task,Value:Clone,F:FnMut(Task)->Value> CompatWorker<Task,Value,F> {
|
||||||
|
pub fn new(value:Value,f:F) -> Self {
|
||||||
|
Self {
|
||||||
|
f,
|
||||||
|
value,
|
||||||
|
data:std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&mut self,task:Task)->Result<(),()>{
|
||||||
|
self.value=(self.f)(task);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grab_clone(&self)->Value{
|
||||||
|
self.value.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]//How to run this test with printing: cargo test --release -- --nocapture
|
||||||
|
fn test_worker() {
|
||||||
|
println!("hiiiii");
|
||||||
|
// Create the worker thread
|
||||||
|
let worker = Worker::new(crate::physics::Body::with_pva(glam::Vec3::ZERO,glam::Vec3::ZERO,glam::Vec3::ZERO),
|
||||||
|
|_|crate::physics::Body::with_pva(glam::Vec3::ONE,glam::Vec3::ONE,glam::Vec3::ONE)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send tasks to the worker
|
||||||
|
for i in 0..5 {
|
||||||
|
let task = crate::instruction::TimedInstruction{
|
||||||
|
time:0,
|
||||||
|
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
||||||
|
};
|
||||||
|
worker.send(task).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Signal the worker to stop (in a real-world scenario)
|
||||||
|
// sender.send("STOP".to_string()).unwrap();
|
||||||
|
|
||||||
|
// Sleep to allow the worker thread to finish processing
|
||||||
|
thread::sleep(std::time::Duration::from_secs(2));
|
||||||
|
|
||||||
|
// Send a new task
|
||||||
|
let task = crate::instruction::TimedInstruction{
|
||||||
|
time:0,
|
||||||
|
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
||||||
|
};
|
||||||
|
worker.send(task).unwrap();
|
||||||
|
|
||||||
|
println!("value={:?}",worker.grab_clone());
|
||||||
|
|
||||||
|
// wait long enough to see print from final task
|
||||||
|
thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
//find roots of polynomials
|
//find roots of polynomials
|
||||||
|
#[inline]
|
||||||
pub fn zeroes2(a0:f32,a1:f32,a2:f32) -> Vec<f32>{
|
pub fn zeroes2(a0:f32,a1:f32,a2:f32) -> Vec<f32>{
|
||||||
if a2==0f32{
|
if a2==0f32{
|
||||||
return zeroes1(a0, a1);
|
return zeroes1(a0, a1);
|
||||||
|
Reference in New Issue
Block a user