diff --git a/Cargo.lock b/Cargo.lock index 56d400e..5509ef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index b02335d..19d186a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/client/mod.rs b/src/client/mod.rs index 59434ea..3a7d9b8 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -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(); diff --git a/src/client/render/command.rs b/src/client/render/command.rs index 57e0a02..3ed91ae 100644 --- a/src/client/render/command.rs +++ b/src/client/render/command.rs @@ -43,20 +43,6 @@ pub struct UpdateGridTransform { } impl<'a> Renderer<'a> { - pub fn spawn(window: Arc) -> 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) { let mut new_camera = false; for cmd in commands { diff --git a/src/client/render/mod.rs b/src/client/render/mod.rs index 16381be..7f05177 100644 --- a/src/client/render/mod.rs +++ b/src/client/render/mod.rs @@ -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, @@ -25,10 +27,19 @@ pub struct Renderer<'a> { impl<'a> Renderer<'a> { pub fn new( - instance: wgpu::Instance, - surface: wgpu::Surface<'a>, - size: PhysicalSize, + window: Arc, ) -> 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, diff --git a/src/client/render/voxel/ray/voxel/color.rs b/src/client/render/voxel/ray/voxel/color.rs new file mode 100644 index 0000000..4021376 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/color.rs @@ -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 for Standard { + fn sample(&self, rng: &mut R) -> VoxelColor { + VoxelColor { + r: rng.gen(), + g: rng.gen(), + b: rng.gen(), + a: rng.gen(), + } + } +} diff --git a/src/client/render/voxel/ray/voxel/grid.rs b/src/client/render/voxel/ray/voxel/grid.rs new file mode 100644 index 0000000..95cd870 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/grid.rs @@ -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, + 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, + } + } +} diff --git a/src/client/render/voxel/ray/voxel/group.rs b/src/client/render/voxel/ray/voxel/group.rs new file mode 100644 index 0000000..f82cdc8 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/group.rs @@ -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, + pub transform_inv: Projective3, + pub scale: u32, + pub offset: u32, +} + +unsafe impl bytemuck::Pod for VoxelGroup {} diff --git a/src/client/render/voxel/ray/voxel/layout.rs b/src/client/render/voxel/ray/voxel/layout.rs new file mode 100644 index 0000000..0f96aa7 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/layout.rs @@ -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, + pub voxel_groups: Storage, + pub voxels: Storage, + pub global_lights: Storage, + 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, + }) + } +} diff --git a/src/client/render/voxel/ray/voxel/light.rs b/src/client/render/voxel/ray/voxel/light.rs new file mode 100644 index 0000000..643d06c --- /dev/null +++ b/src/client/render/voxel/ray/voxel/light.rs @@ -0,0 +1,9 @@ +use nalgebra::Vector3; + +#[repr(C, align(16))] +#[derive(Clone, Copy, PartialEq, bytemuck::Zeroable)] +pub struct GlobalLight { + pub direction: Vector3, +} + +unsafe impl bytemuck::Pod for GlobalLight {} diff --git a/src/client/render/voxel/ray/voxel/mod.rs b/src/client/render/voxel/ray/voxel/mod.rs new file mode 100644 index 0000000..1204fd0 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/mod.rs @@ -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, +} + +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) { + 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, + 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); + } +} diff --git a/src/client/render/voxel/ray/voxel/shader/compute.wgsl b/src/client/render/voxel/ray/voxel/shader/compute.wgsl new file mode 100644 index 0000000..e0b9144 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/shader/compute.wgsl @@ -0,0 +1,282 @@ +@group(0) @binding(0) +var view: View; +@group(0) @binding(1) +var voxels: array; +@group(0) @binding(2) +var voxel_groups: array; +@group(0) @binding(3) +var global_lights: array; +@group(0) @binding(4) +var output: texture_storage_2d; + +struct GlobalLight { + dir: vec3, +}; + +struct View { + transform: mat4x4, + zoom: f32, +}; + +struct VoxelGroup { + transform: mat4x4, + transform_inv: mat4x4, + scale: u32, + offset: u32, +}; + +@compute +@workgroup_size(8, 8, 1) +fn main(@builtin(global_invocation_id) cell: vec3) { + 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(view_dim); + let aspect = view_dim_f.y / view_dim_f.x; + let pixel_pos = vec2( + (vec2(cell.xy) / view_dim_f - vec2(0.5)) * vec2(2.0, -2.0 * aspect) + ); + let pos = view.transform * vec4(pixel_pos, 1.0, 1.0); + let dir = view.transform * vec4(normalize(vec3(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(1.0, 1.0, 1.0); + let sky_bg = vec3(0.3, 0.6, 1.0); + let sky_color = sun_color + sky_bg * (1.0 - light_mult); + color += vec4(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(0.0); +const ZERO2F = vec2(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, dir_view: vec4) -> vec4 { + let gi = 0; + let group = voxel_groups[gi]; + if group.scale == 0 { + return vec4(0.0); + } + let dimensions = vec3(1u << group.scale); + let dim_f = vec3(dimensions); + let dim_i = vec3(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(0.0)); + + + + // calculate normals + var normals = mat3x3( + (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, + ); + var axis = 0u; + + // find where ray intersects with group + let pos_min = (vec3(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( + 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(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(dir_if); + let dir_u = vec3(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(0.0); + var parents = array(); + var prev = LEAF_BIT; + var old_t = t / t_mult; + + var child = 0u; + var vox_pos = vec3(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(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(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(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(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((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(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(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(color.xyz * (1.0 - fog) + vec3(fog), color.a * (1.0 - fog) + fog); + // return vec4(f32(iters) / f32(MAX_ITERS), 0.0, 0.0, 1.0); + return color; +} + +fn dir_to_vec(bits: u32) -> vec3 { + return vec3(bits >> 2, (bits & 2) >> 1, bits & 1); +} + +fn vec_to_dir(vec: vec3) -> u32 { + return vec.x * 4 + vec.y * 2 + vec.z * 1; +} + +fn get_color(id: u32, pos: vec3) -> vec4 { + let random = random(floor(pos)); + let random2 = random(floor(pos) + vec3(0.0001)); + switch id { + case 0u: { + return vec4(0.0); + } + case 1u: { + let color = vec3(0.5, 0.5, 0.5 + random * 0.2) * (random2 * 0.4 + 0.8); + return vec4(color, 1.0); + } + case 2u: { + let color = vec3(0.4 + random * 0.2, 0.9, 0.4 + random * 0.2) * (random2 * 0.2 + 0.9); + return vec4(color, 1.0); + } + case 3u: { + let color = vec3(0.5, 0.5, 1.0) * (random2 * 0.2 + 0.8); + return vec4(color, 0.5); + } + default: { + return vec4(1.0, 0.0, 0.0, 1.0); + } + } +} + +fn random(pos: vec3) -> f32 { + return fract(sin(dot(pos, vec3(12.9898, 78.233, 25.1279))) * 43758.5453123); +} + +fn outside3f(v: vec3, low: vec3, high: vec3) -> bool { + return any(v < low) || any(v > high); +} + +fn inside2f(v: vec2, low: vec2, high: vec2) -> bool { + return all(v >= low) && all(v <= high); +} + +fn inside3i(v: vec3, low: vec3, high: vec3) -> bool { + return all(v >= low) && all(v <= high); +} diff --git a/src/client/render/voxel/ray/voxel/shader/compute_shadow.wgsl b/src/client/render/voxel/ray/voxel/shader/compute_shadow.wgsl new file mode 100644 index 0000000..4a60e9f --- /dev/null +++ b/src/client/render/voxel/ray/voxel/shader/compute_shadow.wgsl @@ -0,0 +1,419 @@ +@group(0) @binding(0) +var view: View; +@group(0) @binding(1) +var voxels: array; +@group(0) @binding(2) +var voxel_groups: array; +@group(0) @binding(3) +var global_lights: array; +@group(0) @binding(4) +var output: texture_storage_2d; + +struct GlobalLight { + dir: vec3, +}; + +struct View { + transform: mat4x4, + zoom: f32, +}; + +struct VoxelGroup { + transform: mat4x4, + transform_inv: mat4x4, + scale: u32, + offset: u32, +}; + +@compute +@workgroup_size(8, 8, 1) +fn main(@builtin(global_invocation_id) cell: vec3) { + 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(view_dim); + let aspect = view_dim_f.y / view_dim_f.x; + let pixel_pos = vec3( + (vec2(cell.xy) / view_dim_f - vec2(0.5)) * vec2(2.0, -2.0 * aspect), + view.zoom + ); + let pos = view.transform * vec4(pixel_pos, 1.0); + let dir = view.transform * vec4(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(1.0, 1.0, 1.0); + let sky_bg = vec3(0.3, 0.6, 1.0); + let sky_color = sun_color + sky_bg * (1.0 - light_mult); + color += vec4(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(0.0); +const ZERO2F = vec2(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, dir_view: vec4) -> vec4 { + let gi = 0; + let group = voxel_groups[gi]; + if group.scale == 0 { + return vec4(0.0); + } + let dimensions = vec3(1u << group.scale); + let dim_f = vec3(dimensions); + let dim_i = vec3(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(0.0)); + + + + // calculate normals + var normals = mat3x3( + (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, + ); + var axis = 0u; + + // find where ray intersects with group + let pos_min = (vec3(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( + 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(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(dir_if); + let dir_u = vec3((dir_i + vec3(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(0.0); + var parents = array(); + + var child = 0u; + var vox_pos = vec3(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(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(normals[axis] * 0.001, 0.0); + + let light = trace_light(new_pos, vec4(-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(1.0)); + let new_a = min(vcolor.a + specular.a, 1.0); + let new_color = vec4(new_rgb, new_a); + color += vec4(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(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((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(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(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(iters) / f32(MAX_ITERS), 0.0, 0.0, 1.0); + return color; +} + +fn trace_light(pos_view: vec4, dir_view: vec4) -> vec4 { + let gi = 0; + let group = voxel_groups[gi]; + if group.scale == 0 { + return vec4(0.0); + } + let dimensions = vec3(1u << group.scale); + let dim_f = vec3(dimensions); + let dim_i = vec3(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(0.0)); + + + + // calculate normals + var normals = mat3x3( + (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, + ); + var axis = 0u; + + // find where ray intersects with group + let pos_min = (vec3(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( + 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(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(dir_if); + let dir_u = vec3((dir_i + vec3(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(0.0); + var skip = LEAF_BIT; + var parents = array(); + + var child = 0u; + var vox_pos = vec3(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(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(vec3(0.0), min(dist / 12.0, 1.0)); + mask += vec4(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(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(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((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(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(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(vec3(0.0), min(dist / 12.0, 1.0)); + mask += vec4(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a); + } + mask.a = 1.0 - mask.a; + mask = vec4(mask.a * mask.xyz, mask.a); + return mask; +} + +fn dir_to_vec(bits: u32) -> vec3 { + return vec3(bits >> 2, (bits & 2) >> 1, bits & 1); +} + +fn vec_to_dir(vec: vec3) -> u32 { + return vec.x * 4 + vec.y * 2 + vec.z * 1; +} + +fn get_color(id: u32) -> vec4 { + switch id { + case 0u: { + return vec4(0.0); + } + case 1u: { + return vec4(0.5, 0.5, 0.5, 1.0); + } + case 2u: { + return vec4(0.5, 1.0, 0.5, 1.0); + } + case 3u: { + return vec4(0.5, 0.5, 1.0, 0.5); + } + default: { + return vec4(1.0, 0.0, 0.0, 1.0); + } + } +} + +fn outside3f(v: vec3, low: vec3, high: vec3) -> bool { + return any(v < low) || any(v > high); +} + +fn inside2f(v: vec2, low: vec2, high: vec2) -> bool { + return all(v >= low) && all(v <= high); +} + +fn inside3i(v: vec3, low: vec3, high: vec3) -> bool { + return all(v >= low) && all(v <= high); +} diff --git a/src/client/render/voxel/ray/voxel/shader/compute_struct.wgsl b/src/client/render/voxel/ray/voxel/shader/compute_struct.wgsl new file mode 100644 index 0000000..3554f06 --- /dev/null +++ b/src/client/render/voxel/ray/voxel/shader/compute_struct.wgsl @@ -0,0 +1,364 @@ +@group(0) @binding(0) +var view: View; +@group(0) @binding(1) +var voxels: array; +@group(0) @binding(2) +var voxel_groups: array; +@group(0) @binding(3) +var global_lights: array; +@group(0) @binding(4) +var output: texture_storage_2d; + +struct GlobalLight { + dir: vec3, +}; + +struct View { + transform: mat4x4, + zoom: f32, +}; + +struct VoxelGroup { + transform: mat4x4, + transform_inv: mat4x4, + scale: u32, + offset: u32, +}; + +@compute +@workgroup_size(8, 8, 1) +fn main(@builtin(global_invocation_id) cell: vec3) { + 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(view_dim); + let aspect = view_dim_f.y / view_dim_f.x; + let pixel_pos = vec3( + (vec2(cell.xy) / view_dim_f - vec2(0.5)) * vec2(2.0, -2.0 * aspect), + view.zoom + ); + let pos = view.transform * vec4(pixel_pos, 1.0); + let dir = view.transform * vec4(normalize(pixel_pos), 0.0); + + let start = start_ray(pos, dir); + var color = vec4(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(1.0)); + color += vec4(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(vec3(0.0) * a, a) * (1.0 - color.a); + } + } + if color.a != 0 { + let pos = pos + dir * res.ray.t / start.t_mult - vec4(normals[res.ray.axis] * 0.001, 0.0); + let dir = vec4(-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(color.xyz * max(light, ambient), color.a) + vec4(vec3(specular * light), 0.0); + } + // color = vec4(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(1.0, 1.0, 1.0); + let sky_bg = vec3(0.3, 0.6, 1.0); + let sky_color = sun_color + sky_bg * (1.0 - light_mult); + color += vec4(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(0.0); +const ZERO2F = vec2(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, + t_inc: vec3, + scale: u32, + min_adj: vec3, + child: u32, + axis: u32, + node_start: u32, + group_offset: u32, + inv_dir_bits: u32, + parents: array, +}; + +struct RayResult { + ray: Ray, + data: u32, +} + +struct RayStart { + hit: bool, + ray: Ray, + normals: mat3x3, + t_mult: f32, +} + +fn start_ray(pos_view: vec4, dir_view: vec4) -> RayStart { + let gi = 0; + let group = voxel_groups[gi]; + if group.scale == 0 { + return RayStart(); + } + let dimensions = vec3(1u << group.scale); + let dim_f = vec3(dimensions); + let dim_i = vec3(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(0.0)); + + + + // calculate normals + var normals = mat3x3( + (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, + ); + var axis = 0u; + + // find where ray intersects with group + let pos_min = (vec3(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( + 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(dir_if); + let dir_u = vec3((dir_i + vec3(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(); + + var child = 0u; + var vox_pos = vec3(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((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(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((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(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(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 { + return vec3(bits >> 2, (bits & 2) >> 1, bits & 1); +} + +fn vec_to_dir(vec: vec3) -> u32 { + return vec.x * 4 + vec.y * 2 + vec.z * 1; +} + +fn get_color(id: u32) -> vec4 { + switch id { + case 0u: { + return vec4(0.0); + } + case 1u: { + return vec4(0.5, 0.5, 0.5, 1.0); + } + case 2u: { + return vec4(0.5, 1.0, 0.5, 1.0); + } + case 3u: { + return vec4(0.5, 0.5, 1.0, 0.5); + } + default: { + return vec4(1.0, 0.0, 0.0, 1.0); + } + } +} + +fn outside3f(v: vec3, low: vec3, high: vec3) -> bool { + return any(v < low) || any(v > high); +} + +fn inside2f(v: vec2, low: vec2, high: vec2) -> bool { + return all(v >= low) && all(v <= high); +} + +fn inside3i(v: vec3, low: vec3, high: vec3) -> bool { + return all(v >= low) && all(v <= high); +} diff --git a/src/client/render/voxel/ray/voxel/shader/render.wgsl b/src/client/render/voxel/ray/voxel/shader/render.wgsl new file mode 100644 index 0000000..f5ed64e --- /dev/null +++ b/src/client/render/voxel/ray/voxel/shader/render.wgsl @@ -0,0 +1,38 @@ +// Vertex shader + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_pos: vec2, +}; + +@group(0) @binding(0) +var tex: texture_2d; +@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(vi % 2u), + f32(vi / 2u), + ); + out.clip_position = vec4(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 { + return textureSample(tex, sample, in.tex_pos); +} + diff --git a/src/client/render/voxel/ray/voxel/view.rs b/src/client/render/voxel/ray/voxel/view.rs new file mode 100644 index 0000000..6419a7d --- /dev/null +++ b/src/client/render/voxel/ray/voxel/view.rs @@ -0,0 +1,19 @@ +use nalgebra::Transform3; + +#[repr(C, align(16))] +#[derive(Clone, Copy, PartialEq, bytemuck::Zeroable)] +pub struct View { + pub transform: Transform3, + pub zoom: f32, +} + +unsafe impl bytemuck::Pod for View {} + +impl Default for View { + fn default() -> Self { + Self { + zoom: 1.0, + transform: Transform3::identity(), + } + } +} diff --git a/src/client/render/voxel/ray_oct/shader/compute.wgsl b/src/client/render/voxel/ray_oct/shader/compute.wgsl index 74e9009..3296af8 100644 --- a/src/client/render/voxel/ray_oct/shader/compute.wgsl +++ b/src/client/render/voxel/ray_oct/shader/compute.wgsl @@ -58,7 +58,7 @@ const ZERO3F = vec3(0.0); const ZERO2F = vec2(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, dir_view: vec4) -> vec4 { } var pos = (pos_view + dir_view * real_t).xyz; pos[axis] = round(pos[axis]) - (1.0 - dir_uf[axis]); - // if true {return vec4(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(pos, 1.0);} let vcolor = get_color(node & LEAF_MASK, pos); - let normal = normals[axis]; + var normal = normals[axis]; let light_color = vec3(1.0); let light_dir = global_lights[0].dir; diff --git a/src/client/render_vulkan/command.rs b/src/client/render_vulkan/command.rs new file mode 100644 index 0000000..4a91938 --- /dev/null +++ b/src/client/render_vulkan/command.rs @@ -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, + pub orientation: Rotation3, + pub dimensions: Vector3, + pub grid: Array3, +} + +#[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, + pub orientation: Rotation3, +} + +impl Renderer { + pub fn handle_commands(&mut self, commands: Vec) { + 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, + ); + } + } +} diff --git a/src/client/render_vulkan/mod.rs b/src/client/render_vulkan/mod.rs new file mode 100644 index 0000000..25c1e4b --- /dev/null +++ b/src/client/render_vulkan/mod.rs @@ -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) -> 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) { + self.size = Vector2::new(size.width, size.height); + todo!(); + } +} diff --git a/src/client/render_vulkan/voxel/mod.rs b/src/client/render_vulkan/voxel/mod.rs new file mode 100644 index 0000000..b2b10da --- /dev/null +++ b/src/client/render_vulkan/voxel/mod.rs @@ -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) { + 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, + ); + } +} diff --git a/src/server/chunk/load.rs b/src/server/chunk.rs similarity index 88% rename from src/server/chunk/load.rs rename to src/server/chunk.rs index 463f8a2..1fc36c4 100644 --- a/src/server/chunk/load.rs +++ b/src/server/chunk.rs @@ -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) { 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) { } } } - diff --git a/src/server/chunk/mod.rs b/src/server/chunk/mod.rs deleted file mode 100644 index c846229..0000000 --- a/src/server/chunk/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod load; -pub use load::*; diff --git a/src/server/generation/mod.rs b/src/server/generation/mod.rs index c4026fa..ca38c62 100644 --- a/src/server/generation/mod.rs +++ b/src/server/generation/mod.rs @@ -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 = 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)); } diff --git a/src/util/oct_tree.rs b/src/util/oct_tree.rs index 987e54c..5935523 100644 --- a/src/util/oct_tree.rs +++ b/src/util/oct_tree.rs @@ -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) -> u32, levels: u32) -> OctTree { + Self::from_fn(f_leaf, &mut |_, _| None, levels) + } + pub fn from_fn( f_leaf: &mut impl FnMut(Vector3) -> u32, f_node: &mut impl FnMut(Vector3, u32) -> Option, 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, 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) -> 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> {