switch to noise lib that works on arm neon (apple)

This commit is contained in:
2024-09-20 22:07:40 -04:00
parent cb422ff5dd
commit e66c75ca44
24 changed files with 2313 additions and 114 deletions

475
Cargo.lock generated
View File

@@ -31,6 +31,15 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
@@ -73,6 +82,15 @@ dependencies = [
"libc",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "approx"
version = "0.5.1"
@@ -100,13 +118,22 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
name = "ash"
version = "0.37.3+1.3.251"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
dependencies = [
"libloading 0.7.4",
]
[[package]]
name = "ash"
version = "0.38.0+1.3.281"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
dependencies = [
"libloading",
"libloading 0.8.5",
]
[[package]]
@@ -146,6 +173,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.3.0"
@@ -286,6 +324,30 @@ dependencies = [
"syn 2.0.77",
]
[[package]]
name = "bindgen"
version = "0.55.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b13ce559e6433d360c26305643803cb52cfbabbc2b9c47ce04a58493dfb443"
dependencies = [
"bitflags 1.3.2",
"cexpr",
"cfg-if 0.1.10",
"clang-sys",
"clap",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash 1.1.0",
"shlex 0.1.1",
"which",
]
[[package]]
name = "bit-set"
version = "0.6.0"
@@ -411,7 +473,7 @@ checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800"
dependencies = [
"jobserver",
"libc",
"shlex",
"shlex 1.3.0",
]
[[package]]
@@ -420,6 +482,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
@@ -444,6 +515,32 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading 0.8.5",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@@ -544,12 +641,27 @@ dependencies = [
"libc",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "cursor-icon"
version = "1.1.0"
@@ -566,7 +678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017"
dependencies = [
"bitflags 2.6.0",
"libloading",
"libloading 0.8.5",
"winapi",
]
@@ -582,7 +694,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading",
"libloading 0.8.5",
]
[[package]]
@@ -609,6 +721,19 @@ dependencies = [
"serde",
]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -656,6 +781,25 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "fastnoise-simd"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d156d53d8fcdad2a9392bbc74fb94a508fdb3c059dff24da748954ffe2308a42"
dependencies = [
"fastnoise_simd_bindings",
]
[[package]]
name = "fastnoise_simd_bindings"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb6813cbacdd6525a31c02a117acc8581f37f104b11ab77e942029f4df994698"
dependencies = [
"bindgen",
"cc",
]
[[package]]
name = "fastrand"
version = "2.1.1"
@@ -760,6 +904,12 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "glow"
version = "0.13.1"
@@ -833,6 +983,17 @@ dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"bytemuck",
"cfg-if 1.0.0",
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -853,12 +1014,27 @@ dependencies = [
"bitflags 2.6.0",
"com",
"libc",
"libloading",
"libloading 0.8.5",
"thiserror",
"widestring",
"winapi",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.4.0"
@@ -871,6 +1047,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "ilattice"
version = "0.1.0"
@@ -890,6 +1075,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jni"
version = "0.21.1"
@@ -937,7 +1128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
dependencies = [
"libc",
"libloading",
"libloading 0.8.5",
"pkg-config",
]
@@ -947,12 +1138,34 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if 1.0.0",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.5"
@@ -1048,7 +1261,7 @@ dependencies = [
"foreign-types",
"log",
"objc",
"paste 1.0.15",
"paste",
]
[[package]]
@@ -1133,7 +1346,7 @@ dependencies = [
"log",
"ndk-sys 0.6.0+11769913",
"num_enum",
"raw-window-handle",
"raw-window-handle 0.6.2",
"thiserror",
]
@@ -1170,6 +1383,16 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "nom"
version = "5.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b"
dependencies = [
"memchr",
"version_check",
]
[[package]]
name = "nonmax"
version = "0.5.5"
@@ -1239,7 +1462,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate",
"proc-macro-crate 3.2.0",
"proc-macro2",
"quote",
"syn 2.0.77",
@@ -1510,16 +1733,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "paste"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
dependencies = [
"paste-impl",
"proc-macro-hack",
]
[[package]]
name = "paste"
version = "1.0.15"
@@ -1527,13 +1740,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "paste-impl"
version = "0.1.18"
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
dependencies = [
"proc-macro-hack",
]
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
@@ -1591,7 +1801,7 @@ checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511"
dependencies = [
"cfg-if 1.0.0",
"concurrent-queue",
"hermit-abi",
"hermit-abi 0.4.0",
"pin-project-lite",
"rustix",
"tracing",
@@ -1619,6 +1829,16 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "3.2.0"
@@ -1628,12 +1848,6 @@ dependencies = [
"toml_edit 0.22.20",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.86"
@@ -1649,6 +1863,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-xml"
version = "0.36.1"
@@ -1703,6 +1923,12 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -1733,6 +1959,35 @@ dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "regex"
version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "renderdoc-sys"
version = "1.1.0"
@@ -1764,6 +2019,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "safe_arch"
version = "0.7.2"
@@ -1827,6 +2088,24 @@ dependencies = [
"syn 2.0.77",
]
[[package]]
name = "serde_json"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "shlex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
[[package]]
name = "shlex"
version = "1.3.0"
@@ -1842,7 +2121,7 @@ dependencies = [
"approx",
"num-complex",
"num-traits",
"paste 1.0.15",
"paste",
"wide",
]
@@ -1855,29 +2134,10 @@ dependencies = [
"approx",
"num-complex",
"num-traits",
"paste 1.0.15",
"paste",
"wide",
]
[[package]]
name = "simdeez"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ec898e1be717eee4b54a84ff2fc94ecb5a1b992d4ad148ce30575b45745662"
dependencies = [
"cfg-if 0.1.10",
"paste 0.1.18",
]
[[package]]
name = "simdnoise"
version = "3.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f69a3fa031fc2906ffe27aecc55bdd2c9cb95327c4695ea814a083145fa462"
dependencies = [
"simdeez",
]
[[package]]
name = "slab"
version = "0.4.9"
@@ -1960,6 +2220,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.109"
@@ -1991,6 +2257,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.63"
@@ -2052,6 +2327,17 @@ version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.21.1"
@@ -2145,12 +2431,27 @@ dependencies = [
"serde",
]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vk-parse"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79"
dependencies = [
"xml-rs",
]
[[package]]
name = "voxelgame"
version = "0.1.0"
@@ -2159,17 +2460,60 @@ dependencies = [
"bevy_ecs",
"block-mesh",
"bytemuck",
"fastnoise-simd",
"nalgebra",
"ndarray",
"pollster",
"rand",
"rustc-hash 2.0.0",
"simba 0.8.1",
"simdnoise",
"vulkano",
"wgpu",
"winit",
]
[[package]]
name = "vulkano"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70f4278f76307b3c388679234b397b4f90de29cdba53873c26b624ed82653d75"
dependencies = [
"ahash",
"ash 0.37.3+1.3.251",
"bytemuck",
"core-graphics-types",
"crossbeam-queue",
"half",
"heck",
"indexmap",
"libloading 0.8.5",
"objc",
"once_cell",
"parking_lot",
"proc-macro2",
"quote",
"raw-window-handle 0.5.2",
"regex",
"serde",
"serde_json",
"smallvec",
"thread_local",
"vk-parse",
"vulkano-macros",
]
[[package]]
name = "vulkano-macros"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52be622d364272fd77e298e7f68e8547ae66e7687cb86eb85335412cee7e3965"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "walkdir"
version = "2.5.0"
@@ -2406,7 +2750,7 @@ dependencies = [
"naga",
"parking_lot",
"profiling",
"raw-window-handle",
"raw-window-handle 0.6.2",
"smallvec",
"static_assertions",
"wasm-bindgen",
@@ -2434,7 +2778,7 @@ dependencies = [
"once_cell",
"parking_lot",
"profiling",
"raw-window-handle",
"raw-window-handle 0.6.2",
"rustc-hash 1.1.0",
"smallvec",
"thiserror",
@@ -2450,7 +2794,7 @@ checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f"
dependencies = [
"android_system_properties",
"arrayvec",
"ash",
"ash 0.38.0+1.3.281",
"bit-set",
"bitflags 2.6.0",
"block",
@@ -2466,7 +2810,7 @@ dependencies = [
"js-sys",
"khronos-egl",
"libc",
"libloading",
"libloading 0.8.5",
"log",
"metal",
"naga",
@@ -2476,7 +2820,7 @@ dependencies = [
"parking_lot",
"profiling",
"range-alloc",
"raw-window-handle",
"raw-window-handle 0.6.2",
"renderdoc-sys",
"rustc-hash 1.1.0",
"smallvec",
@@ -2498,6 +2842,15 @@ dependencies = [
"web-sys",
]
[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
"libc",
]
[[package]]
name = "wide"
version = "0.7.28"
@@ -2799,7 +3152,7 @@ dependencies = [
"orbclient",
"percent-encoding",
"pin-project",
"raw-window-handle",
"raw-window-handle 0.6.2",
"redox_syscall 0.4.1",
"rustix",
"sctk-adwaita",
@@ -2860,7 +3213,7 @@ dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"libloading",
"libloading 0.8.5",
"once_cell",
"rustix",
"x11rb-protocol",

View File

@@ -15,10 +15,11 @@ ndarray = "0.15.6"
pollster = "0.3"
rand = "0.8.5"
simba = "0.8.1"
simdnoise = "3.1.6"
wgpu = "22.0.0"
bevy_ecs = "0.13.2"
bevy_derive = "0.13.2"
winit = {version="0.30.1", features=["serde"]}
block-mesh = "0.2.0"
rustc-hash = "2.0.0"
vulkano = "0.34.1"
fastnoise-simd = "0.1.1"

View File

@@ -3,6 +3,7 @@ mod camera;
mod component;
mod handle_input;
mod input;
// mod render_vulkan;
pub mod render;
mod rsc;
mod state;
@@ -66,7 +67,7 @@ impl Client<'_> {
.expect("Failed to create window"),
);
let renderer = Renderer::spawn(window.clone());
let renderer = Renderer::new(window.clone());
world.insert_resource(RenderCommands(Vec::new()));
let state = ClientState::new();

View File

@@ -43,20 +43,6 @@ pub struct UpdateGridTransform {
}
impl<'a> Renderer<'a> {
pub fn spawn(window: Arc<Window>) -> Renderer<'a> {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY,
..Default::default()
});
let surface = instance
.create_surface(window)
.expect("Could not create window surface!");
Self::new(instance, surface, size)
}
pub fn handle_commands(&mut self, commands: Vec<RenderCommand>) {
let mut new_camera = false;
for cmd in commands {

View File

@@ -1,14 +1,16 @@
mod command;
mod util;
pub mod voxel;
use std::sync::Arc;
pub use command::*;
use super::camera::Camera;
use crate::{client::rsc::CLEAR_COLOR, util::timer::Timer};
use crate::client::rsc::CLEAR_COLOR;
use nalgebra::Vector2;
use util::DepthTexture;
use voxel::VoxelPipeline;
use winit::dpi::PhysicalSize;
use winit::{dpi::PhysicalSize, window::Window};
pub struct Renderer<'a> {
size: Vector2<u32>,
@@ -25,10 +27,19 @@ pub struct Renderer<'a> {
impl<'a> Renderer<'a> {
pub fn new(
instance: wgpu::Instance,
surface: wgpu::Surface<'a>,
size: PhysicalSize<u32>,
window: Arc<Window>,
) -> Self {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY,
..Default::default()
});
let surface = instance
.create_surface(window)
.expect("Could not create window surface!");
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
@@ -36,11 +47,16 @@ impl<'a> Renderer<'a> {
}))
.expect("Could not get adapter!");
let buf_size = (10u32.pow(9) * 15) / 10;
let (device, queue) = pollster::block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default(),
required_limits: wgpu::Limits {
max_storage_buffer_binding_size: buf_size,
max_buffer_size: buf_size as u64,
..Default::default()
},
memory_hints: wgpu::MemoryHints::default(),
},
None, // Trace path
@@ -160,8 +176,7 @@ impl<'a> Renderer<'a> {
self.surface.configure(&self.device, &self.config);
self.voxel_pipeline.resize(&self.device, self.size);
self.depth_texture =
DepthTexture::init(&self.device, &self.config, "depth_texture");
self.depth_texture = DepthTexture::init(&self.device, &self.config, "depth_texture");
self.voxel_pipeline.update_view(
&self.device,
&mut self.encoder,

View File

@@ -0,0 +1,53 @@
use rand::distributions::{Distribution, Standard};
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Zeroable)]
pub struct VoxelColor {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
unsafe impl bytemuck::Pod for VoxelColor {}
impl VoxelColor {
pub fn none() -> Self {
Self {
r: 0,
g: 0,
b: 0,
a: 0,
}
}
pub fn black() -> Self {
Self {
r: 0,
g: 0,
b: 0,
a: 255,
}
}
pub fn white() -> Self {
Self {
r: 255,
g: 255,
b: 255,
a: 255,
}
}
pub fn random() -> Self {
rand::random()
}
}
impl Distribution<VoxelColor> for Standard {
fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> VoxelColor {
VoxelColor {
r: rng.gen(),
g: rng.gen(),
b: rng.gen(),
a: rng.gen(),
}
}
}

View File

@@ -0,0 +1,24 @@
use nalgebra::Matrix4x3;
// this has cost me more than a couple of hours trying to figure out alignment :skull:
// putting transform at the beginning so I don't have to deal with its alignment
// I should probably look into encase (crate)
#[repr(C, align(16))]
#[derive(Clone, Copy, PartialEq, bytemuck::Zeroable)]
pub struct GridInfo {
pub transform: Matrix4x3<f32>,
pub width: u32,
pub height: u32,
}
unsafe impl bytemuck::Pod for GridInfo {}
impl Default for GridInfo {
fn default() -> Self {
Self {
transform: Matrix4x3::identity(),
width: 0,
height: 0,
}
}
}

View File

@@ -0,0 +1,12 @@
use nalgebra::{Projective3, Vector3};
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Zeroable)]
pub struct VoxelGroup {
pub transform: Projective3<f32>,
pub transform_inv: Projective3<f32>,
pub scale: u32,
pub offset: u32,
}
unsafe impl bytemuck::Pod for VoxelGroup {}

View File

@@ -0,0 +1,198 @@
use wgpu::TextureFormat;
use super::{group::VoxelGroup, light::GlobalLight, view::View};
use crate::{
client::render::util::{Storage, StorageTexture, Uniform},
util::oct_tree::OctNode,
};
use nalgebra::Vector3;
pub struct Layout {
pub texture: StorageTexture,
pub view: Uniform<View>,
pub voxel_groups: Storage<VoxelGroup>,
pub voxels: Storage<OctNode>,
pub global_lights: Storage<GlobalLight>,
render_bind_layout: wgpu::BindGroupLayout,
compute_bind_layout: wgpu::BindGroupLayout,
render_pipeline_layout: wgpu::PipelineLayout,
compute_pipeline_layout: wgpu::PipelineLayout,
format: TextureFormat,
}
impl Layout {
pub fn init(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
let view = Uniform::init(device, "view", 0);
let voxels = Storage::init(device, wgpu::ShaderStages::COMPUTE, "voxels", 1);
let voxel_groups = Storage::init(device, wgpu::ShaderStages::COMPUTE, "voxel groups", 2);
let global_lights = Storage::init_with(
device,
wgpu::ShaderStages::COMPUTE,
"global lights",
3,
&[GlobalLight {
direction: Vector3::new(-1.0, -2.3, 2.0).normalize(),
}],
);
let texture = StorageTexture::init(
device,
wgpu::Extent3d {
width: config.width,
height: config.height,
depth_or_array_layers: 1,
},
"compute output",
wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
4,
);
let render_bind_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
// This should match the filterable field of the
// corresponding Texture entry above.
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
label: Some("voxel render"),
});
let compute_bind_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
view.bind_group_layout_entry(),
voxels.bind_group_layout_entry(),
voxel_groups.bind_group_layout_entry(),
global_lights.bind_group_layout_entry(),
texture.bind_group_layout_entry(),
],
label: Some("voxel compute"),
});
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Tile Pipeline Layout"),
bind_group_layouts: &[&render_bind_layout],
push_constant_ranges: &[],
});
let compute_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("voxel compute"),
bind_group_layouts: &[&compute_bind_layout],
push_constant_ranges: &[],
});
Self {
view,
voxels,
voxel_groups,
global_lights,
texture,
render_bind_layout,
compute_bind_layout,
render_pipeline_layout,
compute_pipeline_layout,
format: config.format,
}
}
pub fn render_bind_group(&self, device: &wgpu::Device) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.render_bind_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&self.texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&self.texture.sampler),
},
],
label: Some("tile_bind_group"),
})
}
pub fn compute_bind_group(&self, device: &wgpu::Device) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.compute_bind_layout,
entries: &[
self.view.bind_group_entry(),
self.voxels.bind_group_entry(),
self.voxel_groups.bind_group_entry(),
self.global_lights.bind_group_entry(),
self.texture.bind_group_entry(),
],
label: Some("voxel compute"),
})
}
pub fn render_pipeline(
&self,
device: &wgpu::Device,
shader: wgpu::ShaderModule,
) -> wgpu::RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Voxel Pipeline"),
layout: Some(&self.render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: self.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: true,
},
multiview: None,
cache: None,
})
}
pub fn compute_pipeline(
&self,
device: &wgpu::Device,
shader: &wgpu::ShaderModule,
) -> wgpu::ComputePipeline {
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("voxel"),
layout: Some(&self.compute_pipeline_layout),
module: shader,
entry_point: "main",
compilation_options: Default::default(),
cache: None,
})
}
}

View File

@@ -0,0 +1,9 @@
use nalgebra::Vector3;
#[repr(C, align(16))]
#[derive(Clone, Copy, PartialEq, bytemuck::Zeroable)]
pub struct GlobalLight {
pub direction: Vector3<f32>,
}
unsafe impl bytemuck::Pod for GlobalLight {}

View File

@@ -0,0 +1,220 @@
mod color;
mod grid;
mod group;
mod layout;
mod light;
mod view;
use super::super::UpdateGridTransform;
use crate::{
client::{
camera::Camera,
render::{
util::{ArrBufUpdate, StorageTexture},
AddChunk, CreateVoxelGrid,
},
},
common::component::chunk,
};
use bevy_ecs::entity::Entity;
pub use color::*;
use layout::Layout;
use nalgebra::{Projective3, Transform3, Translation3, Vector2, Vector3};
use std::{collections::HashMap, ops::Deref};
use wgpu::include_wgsl;
use {group::VoxelGroup, view::View};
pub struct VoxelPipeline {
layout: Layout,
compute_pipeline: wgpu::ComputePipeline,
compute_bind_group: wgpu::BindGroup,
render_pipeline: wgpu::RenderPipeline,
render_bind_group: wgpu::BindGroup,
id_map: HashMap<Entity, (usize, VoxelGroup)>,
}
const RENDER_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("shader/render.wgsl");
const COMPUTE_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("shader/compute.wgsl");
impl VoxelPipeline {
pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
// shaders
let layout = Layout::init(device, config);
let render_bind_group = layout.render_bind_group(device);
let shader = device.create_shader_module(RENDER_SHADER);
let render_pipeline = layout.render_pipeline(device, shader);
let compute_bind_group = layout.compute_bind_group(device);
let shader = device.create_shader_module(COMPUTE_SHADER);
let compute_pipeline = layout.compute_pipeline(device, &shader);
Self {
layout,
compute_pipeline,
compute_bind_group,
render_pipeline,
render_bind_group,
id_map: HashMap::new(),
}
}
pub fn reset_shader(&mut self, device: &wgpu::Device) {
let shader = device.create_shader_module(COMPUTE_SHADER);
self.compute_pipeline = self.layout.compute_pipeline(device, &shader);
}
pub fn add_group(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
CreateVoxelGrid {
id,
pos,
orientation,
dimensions,
grid,
}: CreateVoxelGrid,
) {
}
pub fn update_shader(&mut self, device: &wgpu::Device) {
let Ok(shader) = std::fs::read_to_string(
env!("CARGO_MANIFEST_DIR").to_owned() + "/src/client/render/voxel/ray_oct/shader/compute.wgsl",
) else {
println!("Failed to reload shader!");
return;
};
device.push_error_scope(wgpu::ErrorFilter::Validation);
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
source: wgpu::ShaderSource::Wgsl(shader.into()),
});
if pollster::block_on(device.pop_error_scope()).is_some() {
let comp_info = pollster::block_on(shader.get_compilation_info());
println!("Failed to compile shaders:");
for msg in comp_info.messages {
println!("{}", msg.message);
}
} else {
self.compute_pipeline = self.layout.compute_pipeline(device, &shader);
}
}
pub fn add_chunk(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
AddChunk { id, pos, tree, .. }: AddChunk,
) {
let offset = self.layout.voxels.len();
let data = tree.raw();
let updates = [ArrBufUpdate { offset, data }];
let size = offset + data.len();
self.layout
.voxels
.update(device, encoder, belt, size, &updates);
let proj = Projective3::identity()
* Translation3::from((pos.deref() * chunk::SIDE_LENGTH as i32).cast())
* Translation3::from(-chunk::DIMENSIONS.cast() / 2.0);
let group = VoxelGroup {
transform: proj,
transform_inv: proj.inverse(),
scale: chunk::SCALE,
offset: offset as u32,
};
let updates = [ArrBufUpdate {
offset: self.layout.voxel_groups.len(),
data: &[group],
}];
let i = self.layout.voxel_groups.len();
let size = i + 1;
self.layout
.voxel_groups
.update(device, encoder, belt, size, &updates);
self.id_map.insert(id, (i, group));
self.compute_bind_group = self.layout.compute_bind_group(device);
}
pub fn resize(&mut self, device: &wgpu::Device, size: Vector2<u32>) {
self.layout.texture = StorageTexture::init(
device,
wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
},
"idk man im tired",
wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
4,
);
self.compute_bind_group = self.layout.compute_bind_group(device);
self.render_bind_group = self.layout.render_bind_group(device);
}
pub fn update_transform(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
update: UpdateGridTransform,
) {
if let Some((i, group)) = self.id_map.get_mut(&update.id) {
let offset = Vector3::from_element(-(2u32.pow(group.scale) as f32) / 2.0);
let proj = Projective3::identity()
* Translation3::from(update.pos)
* update.orientation
* Translation3::from(offset);
group.transform = proj;
group.transform_inv = proj.inverse();
let updates = [ArrBufUpdate {
offset: *i,
data: &[*group],
}];
let size = self.layout.voxel_groups.len();
self.layout
.voxel_groups
.update(device, encoder, belt, size, &updates);
}
}
pub fn update_view(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt,
size: Vector2<u32>,
camera: &Camera,
) {
let transform =
Transform3::identity() * Translation3::from(camera.pos) * camera.orientation;
let data = View {
zoom: camera.scale,
transform,
};
self.layout.view.update(device, encoder, belt, data);
}
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &self.render_bind_group, &[]);
render_pass.draw(0..4, 0..1);
}
pub const WORKGROUP_SIZE: u32 = 8;
pub fn compute(&self, pass: &mut wgpu::ComputePass) {
pass.set_pipeline(&self.compute_pipeline);
pass.set_bind_group(0, &self.compute_bind_group, &[]);
let buf = &self.layout.texture.buf;
let x = (buf.width() - 1) / Self::WORKGROUP_SIZE + 1;
let y = (buf.height() - 1) / Self::WORKGROUP_SIZE + 1;
pass.dispatch_workgroups(x, y, 1);
}
}

View File

@@ -0,0 +1,282 @@
@group(0) @binding(0)
var<uniform> view: View;
@group(0) @binding(1)
var<storage, read> voxels: array<u32>;
@group(0) @binding(2)
var<storage, read> voxel_groups: array<VoxelGroup>;
@group(0) @binding(3)
var<storage, read> global_lights: array<GlobalLight>;
@group(0) @binding(4)
var output: texture_storage_2d<rgba8unorm, write>;
struct GlobalLight {
dir: vec3<f32>,
};
struct View {
transform: mat4x4<f32>,
zoom: f32,
};
struct VoxelGroup {
transform: mat4x4<f32>,
transform_inv: mat4x4<f32>,
scale: u32,
offset: u32,
};
@compute
@workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) cell: vec3<u32>) {
let view_dim = textureDimensions(output);
// get position of the pixel; eye at origin, pixel on plane z = 1
if cell.x >= view_dim.x || cell.y >= view_dim.y {
return;
}
let view_dim_f = vec2<f32>(view_dim);
let aspect = view_dim_f.y / view_dim_f.x;
let pixel_pos = vec2<f32>(
(vec2<f32>(cell.xy) / view_dim_f - vec2<f32>(0.5)) * vec2<f32>(2.0, -2.0 * aspect)
);
let pos = view.transform * vec4<f32>(pixel_pos, 1.0, 1.0);
let dir = view.transform * vec4<f32>(normalize(vec3<f32>(pixel_pos, view.zoom)), 0.0);
var color = trace_full(pos, dir);
let light_mult = clamp((-dot(dir.xyz, global_lights[0].dir) - 0.99) * 200.0, 0.0, 1.0);
let sun_color = light_mult * vec3<f32>(1.0, 1.0, 1.0);
let sky_bg = vec3<f32>(0.3, 0.6, 1.0);
let sky_color = sun_color + sky_bg * (1.0 - light_mult);
color += vec4<f32>(sky_color * (1.0 - color.a), 1.0 - color.a);
color.a = 1.0;
textureStore(output, cell.xy, color);
}
const LEAF_BIT = 1u << 31u;
const LEAF_MASK = ~LEAF_BIT;
const ZERO3F = vec3<f32>(0.0);
const ZERO2F = vec2<f32>(0.0);
const FULL_ALPHA = 0.999;
const EPSILON = 0.00000000001;
const MAX_ITERS = 10000;
// NOTE: CANNOT GO HIGHER THAN 23 due to how floating point
// numbers are stored and the bit manipulation used
const MAX_SCALE: u32 = 16;
const AMBIENT: f32 = 0.2;
const SPECULAR: f32 = 0.5;
fn trace_full(pos_view: vec4<f32>, dir_view: vec4<f32>) -> vec4<f32> {
let gi = 0;
let group = voxel_groups[gi];
if group.scale == 0 {
return vec4<f32>(0.0);
}
let dimensions = vec3<u32>(1u << group.scale);
let dim_f = vec3<f32>(dimensions);
let dim_i = vec3<i32>(dimensions);
// transform so that group is at 0,0
let pos_start = (group.transform_inv * pos_view).xyz;
var dir = (group.transform_inv * dir_view).xyz;
if dir.x == 0 {dir.x = EPSILON;}
if dir.y == 0 {dir.y = EPSILON;}
if dir.z == 0 {dir.z = EPSILON;}
let dir_if = sign(dir);
let dir_uf = max(dir_if, vec3<f32>(0.0));
// calculate normals
var normals = mat3x3<f32>(
(group.transform * vec4<f32>(dir_if.x, 0.0, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, dir_if.y, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, 0.0, dir_if.z, 0.0)).xyz,
);
var axis = 0u;
// find where ray intersects with group
let pos_min = (vec3<f32>(1.0) - dir_uf) * dim_f;
// time of intersection; x = td + p, solve for t
var t_min = (pos_min - pos_start) / dir;
if outside3f(pos_start, ZERO3F, dim_f) {
// points of intersection
let px = pos_start + t_min.x * dir;
let py = pos_start + t_min.y * dir;
let pz = pos_start + t_min.z * dir;
// check if point is in bounds
let hit = vec3<bool>(
inside2f(px.yz, ZERO2F, dim_f.yz),
inside2f(py.xz, ZERO2F, dim_f.xz),
inside2f(pz.xy, ZERO2F, dim_f.xy),
) && (t_min > ZERO3F);
if !any(hit) {
return vec4<f32>(0.0);
}
axis = select(select(2u, 1u, hit.y), 0u, hit.x);
}
let t_mult = f32(1u << (MAX_SCALE - group.scale));
t_min *= t_mult;
// time to move 1 unit in each direction
let full = f32(1u << MAX_SCALE);
let inc_t = abs(1.0 / dir) * full;
let t_offset = max(max(t_min.x, t_min.y), t_min.z);
var t = max(0.0, t_offset);
let dir_i = vec3<i32>(dir_if);
let dir_u = vec3<u32>(dir_uf);
let dir_bits = vec_to_dir(dir_u);
let inv_dir_bits = 7 - dir_bits;
var node_start = 1u;
var scale = MAX_SCALE - 1;
var scale_exp2 = 0.5;
var color = vec4<f32>(0.0);
var parents = array<u32, MAX_SCALE>();
var prev = LEAF_BIT;
var old_t = t / t_mult;
var child = 0u;
var vox_pos = vec3<f32>(1.0);
let t_center = t_min + scale_exp2 * inc_t;
if t > t_center.x { vox_pos.x = 1.5; child |= 4u; }
if t > t_center.y { vox_pos.y = 1.5; child |= 2u; }
if t > t_center.z { vox_pos.z = 1.5; child |= 1u; }
let min_adj = t_min - inc_t;
var iters = 0;
loop {
if iters == MAX_ITERS {
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
}
iters += 1;
let t_corner = vox_pos * inc_t + min_adj;
let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)];
if node >= LEAF_BIT {
if node != prev {
if node != LEAF_BIT {
let real_t = t / t_mult;
let dist = real_t - old_t;
old_t = real_t;
let filt = min(dist / 64.0, 1.0);
if prev == LEAF_BIT + 3 {
color.a += filt * (1.0 - color.a);
if color.a > FULL_ALPHA { break; }
}
var pos = (pos_view + dir_view * real_t).xyz;
pos[axis] = round(pos[axis]) - (1.0 - dir_uf[axis]);
let vcolor = get_color(node & LEAF_MASK, pos);
var normal = normals[axis];
let light_color = vec3<f32>(1.0);
let light_dir = global_lights[0].dir;
let diffuse = max(dot(light_dir, normal), 0.0) * light_color;
let ambient = AMBIENT * light_color;
let spec_val = pow(max(dot(dir_view.xyz, reflect(-light_dir, normal)), 0.0), 32.0) * SPECULAR;
let specular = spec_val * light_color;
let new_color = (ambient + diffuse + specular) * vcolor.xyz;
let new_a = min(vcolor.a + spec_val, 1.0);
color += vec4<f32>(new_color.xyz * new_a, new_a) * (1.0 - color.a);
if color.a > FULL_ALPHA { break; }
}
prev = node;
}
// move to next time point and determine which axis to move along
let t_next = t_corner + scale_exp2 * inc_t;
t = min(min(t_next.x, t_next.y), t_next.z);
axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z);
let move_dir = 4u >> axis;
// check if need to pop stack
if (child & move_dir) > 0 {
// calculate new scale; first differing bit after adding
let axis_pos = vox_pos[axis];
// AWARE
let differing = bitcast<u32>(axis_pos) ^ bitcast<u32>(axis_pos + scale_exp2);
scale = (bitcast<u32>(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE);
scale_exp2 = bitcast<f32>((scale + 127 - MAX_SCALE) << 23);
if scale >= MAX_SCALE { break; }
// restore & recalculate parent
let parent_info = parents[scale];
node_start = parent_info >> 3;
child = parent_info & 7;
let scale_vec = vec3<u32>(scale + 23 - MAX_SCALE);
// remove bits lower than current scale
vox_pos = bitcast<vec3<f32>>((bitcast<vec3<u32>>(vox_pos) >> scale_vec) << scale_vec);
}
// move to next child and voxel position
child += move_dir;
vox_pos[axis] += scale_exp2;
} else {
// push current node to stack
parents[scale] = (node_start << 3) + child;
scale -= 1u;
// calculate child node vars
scale_exp2 *= 0.5;
child = 0u;
let t_center = t_corner + scale_exp2 * inc_t;
if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; }
if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; }
if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; }
node_start = node;
}
}
// let fog = min(t / t_mult / 1000.0, 1.0);
// return vec4<f32>(color.xyz * (1.0 - fog) + vec3<f32>(fog), color.a * (1.0 - fog) + fog);
// return vec4<f32>(f32(iters) / f32(MAX_ITERS), 0.0, 0.0, 1.0);
return color;
}
fn dir_to_vec(bits: u32) -> vec3<u32> {
return vec3<u32>(bits >> 2, (bits & 2) >> 1, bits & 1);
}
fn vec_to_dir(vec: vec3<u32>) -> u32 {
return vec.x * 4 + vec.y * 2 + vec.z * 1;
}
fn get_color(id: u32, pos: vec3<f32>) -> vec4<f32> {
let random = random(floor(pos));
let random2 = random(floor(pos) + vec3<f32>(0.0001));
switch id {
case 0u: {
return vec4<f32>(0.0);
}
case 1u: {
let color = vec3<f32>(0.5, 0.5, 0.5 + random * 0.2) * (random2 * 0.4 + 0.8);
return vec4<f32>(color, 1.0);
}
case 2u: {
let color = vec3<f32>(0.4 + random * 0.2, 0.9, 0.4 + random * 0.2) * (random2 * 0.2 + 0.9);
return vec4<f32>(color, 1.0);
}
case 3u: {
let color = vec3<f32>(0.5, 0.5, 1.0) * (random2 * 0.2 + 0.8);
return vec4<f32>(color, 0.5);
}
default: {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
}
}
fn random(pos: vec3<f32>) -> f32 {
return fract(sin(dot(pos, vec3<f32>(12.9898, 78.233, 25.1279))) * 43758.5453123);
}
fn outside3f(v: vec3<f32>, low: vec3<f32>, high: vec3<f32>) -> bool {
return any(v < low) || any(v > high);
}
fn inside2f(v: vec2<f32>, low: vec2<f32>, high: vec2<f32>) -> bool {
return all(v >= low) && all(v <= high);
}
fn inside3i(v: vec3<i32>, low: vec3<i32>, high: vec3<i32>) -> bool {
return all(v >= low) && all(v <= high);
}

View File

@@ -0,0 +1,419 @@
@group(0) @binding(0)
var<uniform> view: View;
@group(0) @binding(1)
var<storage, read> voxels: array<u32>;
@group(0) @binding(2)
var<storage, read> voxel_groups: array<VoxelGroup>;
@group(0) @binding(3)
var<storage, read> global_lights: array<GlobalLight>;
@group(0) @binding(4)
var output: texture_storage_2d<rgba8unorm, write>;
struct GlobalLight {
dir: vec3<f32>,
};
struct View {
transform: mat4x4<f32>,
zoom: f32,
};
struct VoxelGroup {
transform: mat4x4<f32>,
transform_inv: mat4x4<f32>,
scale: u32,
offset: u32,
};
@compute
@workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) cell: vec3<u32>) {
let view_dim = textureDimensions(output);
// get position of the pixel; eye at origin, pixel on plane z = 1
if cell.x >= view_dim.x || cell.y >= view_dim.y {
return;
}
let view_dim_f = vec2<f32>(view_dim);
let aspect = view_dim_f.y / view_dim_f.x;
let pixel_pos = vec3<f32>(
(vec2<f32>(cell.xy) / view_dim_f - vec2<f32>(0.5)) * vec2<f32>(2.0, -2.0 * aspect),
view.zoom
);
let pos = view.transform * vec4<f32>(pixel_pos, 1.0);
let dir = view.transform * vec4<f32>(normalize(pixel_pos), 0.0);
var color = trace_full(pos, dir);
let light_mult = clamp((-dot(dir.xyz, global_lights[0].dir) - 0.99) * 200.0, 0.0, 1.0);
let sun_color = light_mult * vec3<f32>(1.0, 1.0, 1.0);
let sky_bg = vec3<f32>(0.3, 0.6, 1.0);
let sky_color = sun_color + sky_bg * (1.0 - light_mult);
color += vec4<f32>(sky_color * (1.0 - color.a), 1.0 - color.a);
color.a = 1.0;
textureStore(output, cell.xy, color);
}
const LEAF_BIT = 1u << 31u;
const LEAF_MASK = ~LEAF_BIT;
const ZERO3F = vec3<f32>(0.0);
const ZERO2F = vec2<f32>(0.0);
const FULL_ALPHA = 0.999;
const EPSILON = 0.00000000001;
const MAX_ITERS = 2000;
// NOTE: CANNOT GO HIGHER THAN 23 due to how floating point
// numbers are stored and the bit manipulation used
const MAX_SCALE: u32 = 10;
fn trace_full(pos_view: vec4<f32>, dir_view: vec4<f32>) -> vec4<f32> {
let gi = 0;
let group = voxel_groups[gi];
if group.scale == 0 {
return vec4<f32>(0.0);
}
let dimensions = vec3<u32>(1u << group.scale);
let dim_f = vec3<f32>(dimensions);
let dim_i = vec3<i32>(dimensions);
// transform so that group is at 0,0
let pos_start = (group.transform_inv * pos_view).xyz;
var dir = (group.transform_inv * dir_view).xyz;
if dir.x == 0 {dir.x = EPSILON;}
if dir.y == 0 {dir.y = EPSILON;}
if dir.z == 0 {dir.z = EPSILON;}
let dir_if = sign(dir);
let dir_uf = max(dir_if, vec3<f32>(0.0));
// calculate normals
var normals = mat3x3<f32>(
(group.transform * vec4<f32>(dir_if.x, 0.0, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, dir_if.y, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, 0.0, dir_if.z, 0.0)).xyz,
);
var axis = 0u;
// find where ray intersects with group
let pos_min = (vec3<f32>(1.0) - dir_uf) * dim_f;
// time of intersection; x = td + p, solve for t
var t_min = (pos_min - pos_start) / dir;
if outside3f(pos_start, ZERO3F, dim_f) {
// points of intersection
let px = pos_start + t_min.x * dir;
let py = pos_start + t_min.y * dir;
let pz = pos_start + t_min.z * dir;
// check if point is in bounds
let hit = vec3<bool>(
inside2f(px.yz, ZERO2F, dim_f.yz),
inside2f(py.xz, ZERO2F, dim_f.xz),
inside2f(pz.xy, ZERO2F, dim_f.xy),
) && (t_min > ZERO3F);
if !any(hit) {
return vec4<f32>(0.0);
}
axis = select(select(2u, 1u, hit.y), 0u, hit.x);
}
let t_mult = f32(1u << (MAX_SCALE - group.scale));
t_min *= t_mult;
// time to move 1 unit in each direction
let full = f32(1u << MAX_SCALE);
let inc_t = abs(1.0 / dir) * full;
let t_offset = max(max(t_min.x, t_min.y), t_min.z);
var t = max(0.0, t_offset);
let dir_i = vec3<i32>(dir_if);
let dir_u = vec3<u32>((dir_i + vec3<i32>(1)) / 2);
let dir_bits = vec_to_dir(dir_u);
let inv_dir_bits = 7 - dir_bits;
var node_start = 1u;
var scale = MAX_SCALE - 1;
var scale_exp2 = 0.5;
var skip = LEAF_BIT;
var color = vec4<f32>(0.0);
var parents = array<u32, MAX_SCALE>();
var child = 0u;
var vox_pos = vec3<f32>(1.0);
let t_center = t_min + scale_exp2 * inc_t;
if t > t_center.x { vox_pos.x = 1.5; child |= 4u; }
if t > t_center.y { vox_pos.y = 1.5; child |= 2u; }
if t > t_center.z { vox_pos.z = 1.5; child |= 1u; }
let min_adj = t_min - inc_t;
var iters = 0;
loop {
if iters == MAX_ITERS {
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
}
iters += 1;
let t_corner = vox_pos * inc_t + min_adj;
let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)];
if node >= LEAF_BIT {
if node != skip && node != LEAF_BIT {
skip = node;
let normal = normals[axis];
let sun_dir = global_lights[0].dir;
let new_pos = pos_view + dir_view * t / t_mult - vec4<f32>(normals[axis] * 0.001, 0.0);
let light = trace_light(new_pos, vec4<f32>(-sun_dir, 0.0));
let diffuse = max(dot(sun_dir, normal) + 0.1, 0.0);
let ambient = 0.2;
let specular = (exp(max(
-(dot(reflect(dir_view.xyz, normal), sun_dir) + 0.90) * 4.0, 0.0
)) - 1.0) * light;
let lighting = max(diffuse * light.a, ambient);
let vcolor = get_color(node & LEAF_MASK);
let new_rgb = min(vcolor.xyz * lighting + specular.xyz + light.xyz * vcolor.xyz, vec3<f32>(1.0));
let new_a = min(vcolor.a + specular.a, 1.0);
let new_color = vec4<f32>(new_rgb, new_a);
color += vec4<f32>(new_color.xyz * new_color.a, new_color.a) * (1.0 - color.a);
if color.a > FULL_ALPHA { break; }
}
// move to next time point and determine which axis to move along
let t_next = t_corner + scale_exp2 * inc_t;
t = min(min(t_next.x, t_next.y), t_next.z);
axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z);
let move_dir = 4u >> axis;
// check if need to pop stack
if (child & move_dir) > 0 {
// calculate new scale; first differing bit after adding
let axis_pos = vox_pos[axis];
// AWARE
let differing = bitcast<u32>(axis_pos) ^ bitcast<u32>(axis_pos + scale_exp2);
scale = (bitcast<u32>(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE);
scale_exp2 = bitcast<f32>((scale + 127 - MAX_SCALE) << 23);
if scale >= MAX_SCALE { break; }
// restore & recalculate parent
let parent_info = parents[scale];
node_start = parent_info >> 3;
child = parent_info & 7;
let scale_vec = vec3<u32>(scale + 23 - MAX_SCALE);
// remove bits lower than current scale
vox_pos = bitcast<vec3<f32>>((bitcast<vec3<u32>>(vox_pos) >> scale_vec) << scale_vec);
}
// move to next child and voxel position
child += move_dir;
vox_pos[axis] += scale_exp2;
} else {
// push current node to stack
parents[scale] = (node_start << 3) + child;
scale -= 1u;
// calculate child node vars
scale_exp2 *= 0.5;
child = 0u;
let t_center = t_corner + scale_exp2 * inc_t;
if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; }
if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; }
if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; }
node_start += 8 + node;
}
}
// return vec4<f32>(f32(iters) / f32(MAX_ITERS), 0.0, 0.0, 1.0);
return color;
}
fn trace_light(pos_view: vec4<f32>, dir_view: vec4<f32>) -> vec4<f32> {
let gi = 0;
let group = voxel_groups[gi];
if group.scale == 0 {
return vec4<f32>(0.0);
}
let dimensions = vec3<u32>(1u << group.scale);
let dim_f = vec3<f32>(dimensions);
let dim_i = vec3<i32>(dimensions);
// transform so that group is at 0,0
let pos_start = (group.transform_inv * pos_view).xyz;
var dir = (group.transform_inv * dir_view).xyz;
if dir.x == 0 {dir.x = EPSILON;}
if dir.y == 0 {dir.y = EPSILON;}
if dir.z == 0 {dir.z = EPSILON;}
let dir_if = sign(dir);
let dir_uf = max(dir_if, vec3<f32>(0.0));
// calculate normals
var normals = mat3x3<f32>(
(group.transform * vec4<f32>(dir_if.x, 0.0, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, dir_if.y, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, 0.0, dir_if.z, 0.0)).xyz,
);
var axis = 0u;
// find where ray intersects with group
let pos_min = (vec3<f32>(1.0) - dir_uf) * dim_f;
// time of intersection; x = td + p, solve for t
var t_min = (pos_min - pos_start) / dir;
if outside3f(pos_start, ZERO3F, dim_f) {
// points of intersection
let px = pos_start + t_min.x * dir;
let py = pos_start + t_min.y * dir;
let pz = pos_start + t_min.z * dir;
// check if point is in bounds
let hit = vec3<bool>(
inside2f(px.yz, ZERO2F, dim_f.yz),
inside2f(py.xz, ZERO2F, dim_f.xz),
inside2f(pz.xy, ZERO2F, dim_f.xy),
) && (t_min > ZERO3F);
if !any(hit) {
return vec4<f32>(0.0);
}
axis = select(select(2u, 1u, hit.y), 0u, hit.x);
}
let t_mult = f32(1u << (MAX_SCALE - group.scale));
t_min *= t_mult;
// time to move 1 unit in each direction
let full = f32(1u << MAX_SCALE);
let inc_t = abs(1.0 / dir) * full;
let t_offset = max(max(t_min.x, t_min.y), t_min.z);
var t = max(0.0, t_offset);
var old_t = t;
let dir_i = vec3<i32>(dir_if);
let dir_u = vec3<u32>((dir_i + vec3<i32>(1)) / 2);
let dir_bits = vec_to_dir(dir_u);
let inv_dir_bits = 7 - dir_bits;
var node_start = 1u;
var scale = MAX_SCALE - 1;
var scale_exp2 = 0.5;
var mask = vec4<f32>(0.0);
var skip = LEAF_BIT;
var parents = array<u32, MAX_SCALE>();
var child = 0u;
var vox_pos = vec3<f32>(1.0);
let t_center = t_min + scale_exp2 * inc_t;
if t > t_center.x { vox_pos.x = 1.5; child |= 4u; }
if t > t_center.y { vox_pos.y = 1.5; child |= 2u; }
if t > t_center.z { vox_pos.z = 1.5; child |= 1u; }
let min_adj = t_min - inc_t;
var data = 0u;
var iters = 0;
loop {
if iters == MAX_ITERS {
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
}
iters += 1;
let t_corner = vox_pos * inc_t + min_adj;
let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)];
if node >= LEAF_BIT {
if node != skip && node != LEAF_BIT {
skip = node;
if data == 3 {
let dist = (t - old_t) / t_mult;
let vcolor = vec4<f32>(vec3<f32>(0.0), min(dist / 12.0, 1.0));
mask += vec4<f32>(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a);
}
data = node & LEAF_MASK;
if data != 3 && data != 0 {
let vcolor = get_color(data);
mask += vec4<f32>(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a);
}
old_t = t;
if mask.a > FULL_ALPHA { break; }
}
// move to next time point and determine which axis to move along
let t_next = t_corner + scale_exp2 * inc_t;
t = min(min(t_next.x, t_next.y), t_next.z);
axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z);
let move_dir = 4u >> axis;
// check if need to pop stack
if (child & move_dir) > 0 {
// calculate new scale; first differing bit after adding
let axis_pos = vox_pos[axis];
// AWARE
let differing = bitcast<u32>(axis_pos) ^ bitcast<u32>(axis_pos + scale_exp2);
scale = (bitcast<u32>(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE);
scale_exp2 = bitcast<f32>((scale + 127 - MAX_SCALE) << 23);
if scale >= MAX_SCALE { break; }
// restore & recalculate parent
let parent_info = parents[scale];
node_start = parent_info >> 3;
child = parent_info & 7;
let scale_vec = vec3<u32>(scale + 23 - MAX_SCALE);
// remove bits lower than current scale
vox_pos = bitcast<vec3<f32>>((bitcast<vec3<u32>>(vox_pos) >> scale_vec) << scale_vec);
}
// move to next child and voxel position
child += move_dir;
vox_pos[axis] += scale_exp2;
} else {
// push current node to stack
parents[scale] = (node_start << 3) + child;
scale -= 1u;
// calculate child node vars
scale_exp2 *= 0.5;
child = 0u;
let t_center = t_corner + scale_exp2 * inc_t;
if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; }
if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; }
if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; }
node_start += 8 + node;
}
}
if data == 3 {
let dist = (t - old_t) / t_mult;
let vcolor = vec4<f32>(vec3<f32>(0.0), min(dist / 12.0, 1.0));
mask += vec4<f32>(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a);
}
mask.a = 1.0 - mask.a;
mask = vec4<f32>(mask.a * mask.xyz, mask.a);
return mask;
}
fn dir_to_vec(bits: u32) -> vec3<u32> {
return vec3<u32>(bits >> 2, (bits & 2) >> 1, bits & 1);
}
fn vec_to_dir(vec: vec3<u32>) -> u32 {
return vec.x * 4 + vec.y * 2 + vec.z * 1;
}
fn get_color(id: u32) -> vec4<f32> {
switch id {
case 0u: {
return vec4<f32>(0.0);
}
case 1u: {
return vec4<f32>(0.5, 0.5, 0.5, 1.0);
}
case 2u: {
return vec4<f32>(0.5, 1.0, 0.5, 1.0);
}
case 3u: {
return vec4<f32>(0.5, 0.5, 1.0, 0.5);
}
default: {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
}
}
fn outside3f(v: vec3<f32>, low: vec3<f32>, high: vec3<f32>) -> bool {
return any(v < low) || any(v > high);
}
fn inside2f(v: vec2<f32>, low: vec2<f32>, high: vec2<f32>) -> bool {
return all(v >= low) && all(v <= high);
}
fn inside3i(v: vec3<i32>, low: vec3<i32>, high: vec3<i32>) -> bool {
return all(v >= low) && all(v <= high);
}

View File

@@ -0,0 +1,364 @@
@group(0) @binding(0)
var<uniform> view: View;
@group(0) @binding(1)
var<storage, read> voxels: array<u32>;
@group(0) @binding(2)
var<storage, read> voxel_groups: array<VoxelGroup>;
@group(0) @binding(3)
var<storage, read> global_lights: array<GlobalLight>;
@group(0) @binding(4)
var output: texture_storage_2d<rgba8unorm, write>;
struct GlobalLight {
dir: vec3<f32>,
};
struct View {
transform: mat4x4<f32>,
zoom: f32,
};
struct VoxelGroup {
transform: mat4x4<f32>,
transform_inv: mat4x4<f32>,
scale: u32,
offset: u32,
};
@compute
@workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) cell: vec3<u32>) {
let view_dim = textureDimensions(output);
// get position of the pixel; eye at origin, pixel on plane z = 1
if cell.x >= view_dim.x || cell.y >= view_dim.y {
return;
}
let view_dim_f = vec2<f32>(view_dim);
let aspect = view_dim_f.y / view_dim_f.x;
let pixel_pos = vec3<f32>(
(vec2<f32>(cell.xy) / view_dim_f - vec2<f32>(0.5)) * vec2<f32>(2.0, -2.0 * aspect),
view.zoom
);
let pos = view.transform * vec4<f32>(pixel_pos, 1.0);
let dir = view.transform * vec4<f32>(normalize(pixel_pos), 0.0);
let start = start_ray(pos, dir);
var color = vec4<f32>(0.0);
let ambient = 0.2;
if start.hit {
var res = ray_next(start.ray, LEAF_BIT);
var normals = start.normals;
let specular = (exp(max(
-(dot(reflect(dir.xyz, normals[res.ray.axis]), global_lights[0].dir) + 0.90) * 4.0, 0.0
)) - 1.0);
while res.data != 0 {
let data = res.data & LEAF_MASK;
if data != 0 {
let vcolor = get_color(data);
let diffuse = max(dot(global_lights[0].dir, normals[res.ray.axis]) + 0.1, 0.0);
let light = max(diffuse, ambient);
let new_color = min(vcolor.xyz * light, vec3<f32>(1.0));
color += vec4<f32>(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a);
if color.a > FULL_ALPHA { break; }
}
let old_t = res.ray.t;
res = ray_next(res.ray, res.data);
if data == 3 {
let dist = (res.ray.t - old_t) / start.t_mult;
let a = min(dist / 12.0, 1.0);
color += vec4<f32>(vec3<f32>(0.0) * a, a) * (1.0 - color.a);
}
}
if color.a != 0 {
let pos = pos + dir * res.ray.t / start.t_mult - vec4<f32>(normals[res.ray.axis] * 0.001, 0.0);
let dir = vec4<f32>(-global_lights[0].dir, 0.0);
let start = start_ray(pos, dir);
res = ray_next(start.ray, LEAF_BIT);
var light = 1.0;
while res.data != 0 {
let data = res.data & LEAF_MASK;
if data != 0 {
let vcolor = get_color(data);
if data != 3 { light -= vcolor.a * light; }
if light <= 0 { break; }
}
let old_t = res.ray.t;
res = ray_next(res.ray, res.data);
if data == 3 {
let dist = (res.ray.t - old_t) / start.t_mult;
let a = min(dist / 12.0, 1.0);
light -= a;
}
}
color = vec4<f32>(color.xyz * max(light, ambient), color.a) + vec4<f32>(vec3<f32>(specular * light), 0.0);
}
// color = vec4<f32>(pos.xyz / 128.0, 1.0);
}
// var color = trace_full(pos, dir);
let light_mult = clamp((-dot(dir.xyz, global_lights[0].dir) - 0.99) * 200.0, 0.0, 1.0);
let sun_color = light_mult * vec3<f32>(1.0, 1.0, 1.0);
let sky_bg = vec3<f32>(0.3, 0.6, 1.0);
let sky_color = sun_color + sky_bg * (1.0 - light_mult);
color += vec4<f32>(sky_color * (1.0 - color.a), 1.0 - color.a);
color.a = 1.0;
textureStore(output, cell.xy, color);
}
const LEAF_BIT = 1u << 31u;
const LEAF_MASK = ~LEAF_BIT;
const ZERO3F = vec3<f32>(0.0);
const ZERO2F = vec2<f32>(0.0);
const FULL_ALPHA = 0.999;
const EPSILON = 0.00000000001;
const MAX_ITERS = 2000;
// NOTE: CANNOT GO HIGHER THAN 23 due to how floating point
// numbers are stored and the bit manipulation used
const MAX_SCALE: u32 = 10;
struct Ray {
t: f32,
vox_pos: vec3<f32>,
t_inc: vec3<f32>,
scale: u32,
min_adj: vec3<f32>,
child: u32,
axis: u32,
node_start: u32,
group_offset: u32,
inv_dir_bits: u32,
parents: array<u32, MAX_SCALE>,
};
struct RayResult {
ray: Ray,
data: u32,
}
struct RayStart {
hit: bool,
ray: Ray,
normals: mat3x3<f32>,
t_mult: f32,
}
fn start_ray(pos_view: vec4<f32>, dir_view: vec4<f32>) -> RayStart {
let gi = 0;
let group = voxel_groups[gi];
if group.scale == 0 {
return RayStart();
}
let dimensions = vec3<u32>(1u << group.scale);
let dim_f = vec3<f32>(dimensions);
let dim_i = vec3<i32>(dimensions);
// transform so that group is at 0,0
let pos = (group.transform_inv * pos_view).xyz;
var dir = (group.transform_inv * dir_view).xyz;
if dir.x == 0 {dir.x = EPSILON;}
if dir.y == 0 {dir.y = EPSILON;}
if dir.z == 0 {dir.z = EPSILON;}
let dir_if = sign(dir);
let dir_uf = max(dir_if, vec3<f32>(0.0));
// calculate normals
var normals = mat3x3<f32>(
(group.transform * vec4<f32>(dir_if.x, 0.0, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, dir_if.y, 0.0, 0.0)).xyz,
(group.transform * vec4<f32>(0.0, 0.0, dir_if.z, 0.0)).xyz,
);
var axis = 0u;
// find where ray intersects with group
let pos_min = (vec3<f32>(1.0) - dir_uf) * dim_f;
// time of intersection; x = td + p, solve for t
var t_min = (pos_min - pos) / dir;
if outside3f(pos, ZERO3F, dim_f) {
// points of intersection
let px = pos + t_min.x * dir;
let py = pos + t_min.y * dir;
let pz = pos + t_min.z * dir;
// check if point is in bounds
let hit = vec3<bool>(
inside2f(px.yz, ZERO2F, dim_f.yz),
inside2f(py.xz, ZERO2F, dim_f.xz),
inside2f(pz.xy, ZERO2F, dim_f.xy),
) && (t_min > ZERO3F);
if !any(hit) {
return RayStart();
}
axis = select(select(2u, 1u, hit.y), 0u, hit.x);
}
let t_mult = f32(1u << (MAX_SCALE - group.scale));
t_min *= t_mult;
// time to move 1 unit in each direction
let full = f32(1u << MAX_SCALE);
let t_inc = abs(1.0 / dir) * full;
let t_offset = max(max(t_min.x, t_min.y), t_min.z);
let t = max(0.0, t_offset);
let dir_i = vec3<i32>(dir_if);
let dir_u = vec3<u32>((dir_i + vec3<i32>(1)) / 2);
let dir_bits = vec_to_dir(dir_u);
let inv_dir_bits = 7 - dir_bits;
let node_start = 1u;
let scale = MAX_SCALE - 1;
let scale_exp2 = 0.5;
let parents = array<u32, MAX_SCALE>();
var child = 0u;
var vox_pos = vec3<f32>(1.0);
let t_center = t_min + scale_exp2 * t_inc;
if t > t_center.x { vox_pos.x = 1.5; child |= 4u; }
if t > t_center.y { vox_pos.y = 1.5; child |= 2u; }
if t > t_center.z { vox_pos.z = 1.5; child |= 1u; }
let min_adj = t_min - t_inc;
return RayStart(
true,
Ray(
t,
vox_pos,
t_inc,
scale,
min_adj,
child,
axis,
node_start,
group.offset,
inv_dir_bits,
parents,
),
normals,
t_mult,
);
}
fn ray_next(ray: Ray, skip: u32) -> RayResult {
let group_offset = ray.group_offset;
let t_inc = ray.t_inc;
let min_adj = ray.min_adj;
let inv_dir_bits = ray.inv_dir_bits;
var scale = ray.scale;
var scale_exp2 = bitcast<f32>((scale + 127 - MAX_SCALE) << 23);
var vox_pos = ray.vox_pos;
var t = ray.t;
var node_start = ray.node_start;
var child = ray.child;
var parents = ray.parents;
var axis: u32;
var data = 0u;
loop {
let t_corner = vox_pos * t_inc + min_adj;
let node = voxels[group_offset + node_start + (child ^ inv_dir_bits)];
if node >= LEAF_BIT {
if node != skip {
data = node;
break;
}
// move to next time point and determine which axis to move along
let t_next = t_corner + scale_exp2 * t_inc;
t = min(min(t_next.x, t_next.y), t_next.z);
axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z);
let move_dir = 4u >> axis;
// check if need to pop stack
if (child & move_dir) > 0 {
// calculate new scale; first differing bit after adding
let axis_pos = vox_pos[axis];
// AWARE
let differing = bitcast<u32>(axis_pos) ^ bitcast<u32>(axis_pos + scale_exp2);
scale = (bitcast<u32>(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE);
scale_exp2 = bitcast<f32>((scale + 127 - MAX_SCALE) << 23);
if scale >= MAX_SCALE { break; }
// restore & recalculate parent
let parent_info = parents[scale];
node_start = parent_info >> 3;
child = parent_info & 7;
let scale_vec = vec3<u32>(scale + 23 - MAX_SCALE);
// remove bits lower than current scale
vox_pos = bitcast<vec3<f32>>((bitcast<vec3<u32>>(vox_pos) >> scale_vec) << scale_vec);
}
// move to next child and voxel position
child += move_dir;
vox_pos[axis] += scale_exp2;
} else {
// push current node to stack
parents[scale] = (node_start << 3) + child;
scale -= 1u;
// calculate child node vars
scale_exp2 *= 0.5;
child = 0u;
let t_center = t_corner + scale_exp2 * t_inc;
if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; }
if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; }
if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; }
node_start += 8 + node;
}
}
return RayResult(
Ray(
t,
vox_pos,
t_inc,
scale,
min_adj,
child,
axis,
node_start,
group_offset,
inv_dir_bits,
parents,
),
data
);
}
fn dir_to_vec(bits: u32) -> vec3<u32> {
return vec3<u32>(bits >> 2, (bits & 2) >> 1, bits & 1);
}
fn vec_to_dir(vec: vec3<u32>) -> u32 {
return vec.x * 4 + vec.y * 2 + vec.z * 1;
}
fn get_color(id: u32) -> vec4<f32> {
switch id {
case 0u: {
return vec4<f32>(0.0);
}
case 1u: {
return vec4<f32>(0.5, 0.5, 0.5, 1.0);
}
case 2u: {
return vec4<f32>(0.5, 1.0, 0.5, 1.0);
}
case 3u: {
return vec4<f32>(0.5, 0.5, 1.0, 0.5);
}
default: {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
}
}
fn outside3f(v: vec3<f32>, low: vec3<f32>, high: vec3<f32>) -> bool {
return any(v < low) || any(v > high);
}
fn inside2f(v: vec2<f32>, low: vec2<f32>, high: vec2<f32>) -> bool {
return all(v >= low) && all(v <= high);
}
fn inside3i(v: vec3<i32>, low: vec3<i32>, high: vec3<i32>) -> bool {
return all(v >= low) && all(v <= high);
}

View File

@@ -0,0 +1,38 @@
// Vertex shader
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_pos: vec2<f32>,
};
@group(0) @binding(0)
var tex: texture_2d<f32>;
@group(0) @binding(1)
var sample: sampler;
@vertex
fn vs_main(
@builtin(vertex_index) vi: u32,
@builtin(instance_index) ii: u32,
) -> VertexOutput {
var out: VertexOutput;
let pos = vec2<f32>(
f32(vi % 2u),
f32(vi / 2u),
);
out.clip_position = vec4<f32>(pos * 2.0 - 1.0, 0.0, 1.0);
out.tex_pos = pos;
out.tex_pos.y = 1.0 - out.tex_pos.y;
return out;
}
// Fragment shader
@fragment
fn fs_main(
in: VertexOutput,
) -> @location(0) vec4<f32> {
return textureSample(tex, sample, in.tex_pos);
}

View File

@@ -0,0 +1,19 @@
use nalgebra::Transform3;
#[repr(C, align(16))]
#[derive(Clone, Copy, PartialEq, bytemuck::Zeroable)]
pub struct View {
pub transform: Transform3<f32>,
pub zoom: f32,
}
unsafe impl bytemuck::Pod for View {}
impl Default for View {
fn default() -> Self {
Self {
zoom: 1.0,
transform: Transform3::identity(),
}
}
}

View File

@@ -58,7 +58,7 @@ const ZERO3F = vec3<f32>(0.0);
const ZERO2F = vec2<f32>(0.0);
const FULL_ALPHA = 0.999;
const EPSILON = 0.00000000001;
const MAX_ITERS = 2000;
const MAX_ITERS = 10000;
// NOTE: CANNOT GO HIGHER THAN 23 due to how floating point
// numbers are stored and the bit manipulation used
const MAX_SCALE: u32 = 13;
@@ -166,12 +166,8 @@ fn trace_full(pos_view: vec4<f32>, dir_view: vec4<f32>) -> vec4<f32> {
}
var pos = (pos_view + dir_view * real_t).xyz;
pos[axis] = round(pos[axis]) - (1.0 - dir_uf[axis]);
// if true {return vec4<f32>(floor(pos) / 16.0, 1.0);}
// let pos = (vox_pos - 1.5) * (dir_if) + 0.5 - scale_exp2 * (1.0 - dir_uf);
// let pos = t / t_mult;
// if true {return vec4<f32>(pos, 1.0);}
let vcolor = get_color(node & LEAF_MASK, pos);
let normal = normals[axis];
var normal = normals[axis];
let light_color = vec3<f32>(1.0);
let light_dir = global_lights[0].dir;

View File

@@ -0,0 +1,82 @@
use crate::{
client::camera::Camera,
common::component::{ChunkMesh, ChunkPos}, util::oct_tree::OctTree,
};
use super::{voxel::VoxelColor, Renderer};
use bevy_ecs::entity::Entity;
use nalgebra::{Rotation3, Vector3};
use ndarray::Array3;
#[derive(Debug, Clone)]
pub enum RenderCommand {
CreateVoxelGrid(CreateVoxelGrid),
AddChunk(AddChunk),
UpdateGridTransform(UpdateGridTransform),
ViewUpdate(Camera),
}
#[derive(Debug, Clone)]
pub struct CreateVoxelGrid {
pub id: Entity,
pub pos: Vector3<f32>,
pub orientation: Rotation3<f32>,
pub dimensions: Vector3<usize>,
pub grid: Array3<VoxelColor>,
}
#[derive(Debug, Clone)]
pub struct AddChunk {
pub id: Entity,
pub pos: ChunkPos,
pub mesh: ChunkMesh,
pub tree: OctTree,
}
#[derive(Debug, Clone)]
pub struct UpdateGridTransform {
pub id: Entity,
pub pos: Vector3<f32>,
pub orientation: Rotation3<f32>,
}
impl Renderer {
pub fn handle_commands(&mut self, commands: Vec<RenderCommand>) {
let mut new_camera = false;
for cmd in commands {
match cmd {
RenderCommand::CreateVoxelGrid(desc) => self.voxel_pipeline.add_group(
&self.device,
&mut self.encoder,
&mut self.staging_belt,
desc,
),
RenderCommand::ViewUpdate(camera) => {
new_camera = true;
self.camera = camera;
}
RenderCommand::UpdateGridTransform(update) => self.voxel_pipeline.update_transform(
&self.device,
&mut self.encoder,
&mut self.staging_belt,
update,
),
RenderCommand::AddChunk(desc) => self.voxel_pipeline.add_chunk(
&self.device,
&mut self.encoder,
&mut self.staging_belt,
desc,
),
}
}
if new_camera {
self.voxel_pipeline.update_view(
&self.device,
&mut self.encoder,
&mut self.staging_belt,
self.size,
&self.camera,
);
}
}
}

View File

@@ -0,0 +1,79 @@
mod command;
pub mod voxel;
use std::sync::Arc;
pub use command::*;
use vulkano::{
device::{Device, DeviceCreateInfo, QueueCreateInfo, QueueFlags}, instance::{Instance, InstanceCreateInfo}, memory::allocator::StandardMemoryAllocator, VulkanLibrary
};
use super::camera::Camera;
use crate::client::rsc::CLEAR_COLOR;
use nalgebra::Vector2;
use voxel::VoxelPipeline;
use winit::{dpi::PhysicalSize, window::Window};
pub struct Renderer {
camera: Camera,
}
impl Renderer {
pub fn new(window: Arc<Window>) -> Self {
let library = VulkanLibrary::new().expect("no local Vulkan library/DLL");
let instance = Instance::new(library, InstanceCreateInfo::default())
.expect("failed to create instance");
let physical_device = instance
.enumerate_physical_devices()
.expect("could not enumerate devices")
.next()
.expect("no devices available");
let queue_family_index = physical_device
.queue_family_properties()
.iter()
.enumerate()
.position(|(_queue_family_index, queue_family_properties)| {
queue_family_properties
.queue_flags
.contains(QueueFlags::GRAPHICS)
})
.expect("couldn't find a graphical queue family")
as u32;
let (device, mut queues) = Device::new(
physical_device,
DeviceCreateInfo {
// here we pass the desired queue family to use by index
queue_create_infos: vec![QueueCreateInfo {
queue_family_index,
..Default::default()
}],
..Default::default()
},
)
.expect("failed to create device");
let queue = queues.next().unwrap();
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
Self {
camera: Camera::default(),
size: Vector2::new(size.width, size.height),
voxel_pipeline: VoxelPipeline::new(&device, &config),
}
}
pub fn reset_shader(&mut self) {
todo!()
}
pub fn update_shader(&mut self) {
todo!()
}
pub fn draw(&mut self) {}
pub fn resize(&mut self, size: PhysicalSize<u32>) {
self.size = Vector2::new(size.width, size.height);
todo!();
}
}

View File

@@ -0,0 +1,25 @@
use std::sync::Arc;
use vulkano::{buffer::{Buffer, BufferCreateInfo, BufferUsage}, memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}};
pub struct VoxelPipeline {
}
impl VoxelPipeline {
pub fn init(memory_allocator: Arc<impl MemoryAllocator>) {
let buffer = Buffer::from_data(
memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::UNIFORM_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
data,
);
}
}

View File

@@ -3,8 +3,9 @@ use std::collections::{HashMap, HashSet};
use bevy_ecs::{entity::Entity, system::Commands};
use crate::{
common::component::{ChunkBundle, ChunkData, ChunkMesh, ChunkPos}, server::generation::generate_tree, util::
thread::{ExitType, ThreadChannel, ThreadHandle}
common::component::{ChunkBundle, ChunkData, ChunkMesh, ChunkPos},
server::generation::generate_tree,
util::{oct_tree::OctTree, thread::{ExitType, ThreadChannel, ThreadHandle}},
};
pub struct ChunkManager {
@@ -107,7 +108,14 @@ fn chunk_loader_main(channel: ThreadChannel<ServerChunkMsg, ChunkLoaderMsg>) {
let tree = ChunkData::from_tree(generate_tree(pos));
let tree_time = std::time::Instant::now() - start;
println!("gen time: {:<5?}; size: {}", tree_time, tree.raw().len());
// let worst = OctTree::from_fn(f_leaf, f_node, levels);
println!(
"gen time: {:<5?}; size: {} nodes = {} bytes",
tree_time,
tree.raw().len(),
std::mem::size_of_val(tree.raw())
);
channel.send(ServerChunkMsg::ChunkGenerated(GeneratedChunk {
pos,
@@ -121,4 +129,3 @@ fn chunk_loader_main(channel: ThreadChannel<ServerChunkMsg, ChunkLoaderMsg>) {
}
}
}

View File

@@ -1,2 +0,0 @@
mod load;
pub use load::*;

View File

@@ -1,5 +1,5 @@
use fastnoise_simd::FastNoiseSIMD;
use nalgebra::Vector3;
use simdnoise::NoiseBuilder;
use crate::{
common::component::{chunk, ChunkPos},
@@ -11,11 +11,11 @@ pub fn generate_tree(pos: ChunkPos) -> OctTree {
return OctTree::from_leaf(0, 8);
}
let posf: Vector3<f32> = pos.cast() * chunk::SIDE_LENGTH as f32;
let noise1 = generate_noise_map(0, 1.0, posf, chunk::SCALE, &mut |v: f32| {
let noise1 = generate_noise_map(2, 1.0, posf, chunk::SCALE, &mut |v: f32| {
(v * 2.0).exp2() * TOP * 0.25
});
let noise2 = generate_noise_map(1, 50.0, posf, chunk::SCALE, &mut |v: f32| v * 20.0 + GRASS);
OctTree::from_fn_rec(
let noise2 = generate_noise_map(0, 50.0, posf, chunk::SCALE, &mut |v: f32| v * 20.0 + GRASS);
OctTree::from_fn(
&mut |p| generate_leaf(p, posf, (&noise1.base, &noise2.base)),
&mut |p, lvl| generate_node(p, lvl, posf, (&noise1, &noise2)),
chunk::SCALE,
@@ -89,10 +89,15 @@ fn generate_noise_map(
adjust: &mut impl FnMut(f32) -> f32,
) -> NoiseMap {
let mut size = 2usize.pow(levels);
let (mut base, min, max) = NoiseBuilder::gradient_2d_offset(posf.x, size, posf.z, size)
.with_seed(seed)
.with_freq(freq / (size as f32))
.generate();
let posi = Vector3::new(posf.x as i32, posf.y as i32, posf.z as i32);
let noise = FastNoiseSIMD::new(seed).get_simplex_set(posi.x, posi.y, posi.z, size as i32, size as i32, 1, freq * 150.0 / size as f32);
let mut base = noise.as_vec();
let (mut min, mut max) = (f32::MAX, f32::MIN);
for v in &base {
min = v.min(min);
max = v.max(max);
}
println!("{min}, {max}");
for v in &mut base {
*v = adjust((*v - min) / (max - min));
}

View File

@@ -31,7 +31,13 @@ impl OctNode {
}
}
type OctNodeMap = FxHashMap<[OctNode; 8], OctNode>;
#[derive(Debug, Clone, Copy)]
struct OctNodeEntry {
pub node: OctNode,
pub count: u32,
}
type OctNodeMap = FxHashMap<[OctNode; 8], OctNodeEntry>;
#[derive(Debug, Clone)]
pub struct OctTree {
@@ -61,7 +67,10 @@ impl OctTree {
levels,
}
}
pub fn from_fn_rec(
pub fn from_leaf_fn(f_leaf: &mut impl FnMut(Vector3<usize>) -> u32, levels: u32) -> OctTree {
Self::from_fn(f_leaf, &mut |_, _| None, levels)
}
pub fn from_fn(
f_leaf: &mut impl FnMut(Vector3<usize>) -> u32,
f_node: &mut impl FnMut(Vector3<usize>, u32) -> Option<u32>,
levels: u32,
@@ -102,8 +111,9 @@ impl OctTree {
core::array::from_fn(|i| OctNode::new_leaf(f_leaf(offset + CORNERS[i])));
if leaves[1..].iter().all(|l| *l == leaves[0]) {
data.push(leaves[0]);
} else if let Some(node) = map.get(&leaves) {
data.push(*node);
} else if let Some(entry) = map.get_mut(&leaves) {
data.push(entry.node);
entry.count += 1;
} else {
data.extend_from_slice(&leaves);
}
@@ -128,7 +138,10 @@ impl OctTree {
let node = OctNode::new_node(sub_start as u32);
data[i + j] = node;
data_start += len;
map.insert(data[sub_start..sub_start+8].try_into().unwrap(), node);
map.insert(
data[sub_start..sub_start + 8].try_into().unwrap(),
OctNodeEntry { node, count: 1 },
);
}
}
}
@@ -137,14 +150,15 @@ impl OctTree {
if first.is_leaf() && data[i + 1..i + 8].iter().all(|l| *l == first) {
data.truncate(i);
data.push(first);
} else if let Some(node) = map.get(&data[i..i + 8]) {
} else if let Some(entry) = map.get_mut(&data[i..i + 8]) {
data.truncate(i);
data.push(*node);
data.push(entry.node);
entry.count += 1;
}
}
}
pub fn from_arr(arr: ArrayView3<u32>, levels: u32) -> Self {
Self::from_fn_rec(&mut |p| arr[(p.x, p.y, p.z)], &mut |_, _| None, levels)
Self::from_fn(&mut |p| arr[(p.x, p.y, p.z)], &mut |_, _| None, levels)
}
pub fn get(&self, mut pos: Vector3<usize>) -> u32 {
let mut data_start = 1;
@@ -164,7 +178,6 @@ impl OctTree {
pub fn raw(&self) -> &[OctNode] {
&self.data
}
pub fn mesh(&self) {}
}
pub struct OctTreeIter<'a> {