diff --git a/CMakeLists.txt b/CMakeLists.txt index ac986b0..097b292 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) -project(photon_light VERSION 1.0.0 LANGUAGES CXX) +project(photon-light VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/Cargo.lock b/Cargo.lock index 0d4a66e..879b203 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "calloop" @@ -217,9 +217,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.45" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ "find-msvc-tools", "jobserver", @@ -408,9 +408,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "foldhash" @@ -451,13 +451,38 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "gethostname" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.3", "windows-link", ] @@ -486,9 +511,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.9" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" dependencies = [ "approx", "bytemuck", @@ -589,9 +614,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "foldhash 0.2.0", ] @@ -610,12 +635,12 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "indexmap" -version = "2.12.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -652,9 +677,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -679,9 +704,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" @@ -695,19 +720,19 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.5.18", + "redox_syscall 0.7.0", ] [[package]] @@ -739,9 +764,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "malloc_buf" @@ -795,7 +820,7 @@ dependencies = [ "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "hexf-parse", "indexmap", "libm", @@ -804,7 +829,7 @@ dependencies = [ "once_cell", "rustc-hash", "spirv", - "thiserror 2.0.17", + "thiserror 2.0.18", "unicode-ident", ] @@ -1090,10 +1115,11 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "orbclient" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827" +checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd" dependencies = [ + "libc", "libredox", ] @@ -1202,6 +1228,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.32" @@ -1218,7 +1250,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -1230,9 +1262,9 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -1269,9 +1301,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1284,18 +1316,18 @@ checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1308,9 +1340,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha", "rand_core", @@ -1328,9 +1360,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom", ] @@ -1384,6 +1416,15 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_syscall" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -1411,9 +1452,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -1506,9 +1547,9 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" -version = "1.0.7" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" dependencies = [ "version_check", ] @@ -1576,9 +1617,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1605,11 +1646,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -1625,9 +1666,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1661,18 +1702,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -1682,18 +1723,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-core", @@ -1701,9 +1742,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" [[package]] name = "ttf-parser" @@ -1747,18 +1788,18 @@ dependencies = [ [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -1769,11 +1810,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -1782,9 +1824,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1792,9 +1834,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -1805,22 +1847,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] name = "wayland-backend" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.2", + "rustix 1.1.3", "scoped-tls", "smallvec", "wayland-sys", @@ -1828,12 +1870,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.11" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" dependencies = [ "bitflags 2.10.0", - "rustix 1.1.2", + "rustix 1.1.3", "wayland-backend", "wayland-scanner", ] @@ -1851,20 +1893,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.11" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" +checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.3", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.9" +version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" dependencies = [ "bitflags 2.10.0", "wayland-backend", @@ -1874,9 +1916,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" +checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" dependencies = [ "bitflags 2.10.0", "wayland-backend", @@ -1887,9 +1929,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" dependencies = [ "bitflags 2.10.0", "wayland-backend", @@ -1900,9 +1942,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" dependencies = [ "proc-macro2", "quick-xml", @@ -1911,9 +1953,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" dependencies = [ "dlib", "log", @@ -1923,9 +1965,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -1952,7 +1994,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "log", "naga", @@ -1983,7 +2025,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "indexmap", "log", "naga", @@ -1994,7 +2036,7 @@ dependencies = [ "raw-window-handle", "rustc-hash", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", @@ -2050,7 +2092,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", @@ -2070,7 +2112,7 @@ dependencies = [ "raw-window-handle", "renderdoc-sys", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "wasm-bindgen", "web-sys", "wgpu-types", @@ -2088,7 +2130,7 @@ dependencies = [ "bytemuck", "js-sys", "log", - "thiserror 2.0.17", + "thiserror 2.0.18", "web-sys", ] @@ -2382,18 +2424,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "x11-dl" @@ -2417,7 +2459,7 @@ dependencies = [ "libc", "libloading", "once_cell", - "rustix 1.1.2", + "rustix 1.1.3", "x11rb-protocol", ] @@ -2460,18 +2502,18 @@ checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 04dba8e..5c3ab54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,11 @@ edition = "2024" panic = 'abort' [profile.dev.package."*"] +debug = false opt-level = 3 [profile.test.package."*"] +debug = false opt-level = 3 [dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..69f2a64 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Photon Light diff --git a/src/camera.rs b/src/camera.rs index a91e1a7..88badff 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -20,6 +20,12 @@ impl OrbitalCamera { self.distance * vec3(xy * x, xy * y, z) } + pub fn direction(&self) -> Vec3 { + let (y, x) = self.position_yaw.sin_cos(); + let (z, xy) = self.position_pitch.sin_cos(); + -vec3(xy * x, xy * y, z) + } + pub fn transform(&self) -> Mat4 { // for yaw=0, pitch=0: // X -> -Z @@ -31,12 +37,13 @@ impl OrbitalCamera { [-1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 0., 1.], - ]) * Mat4::from_euler( - glam::EulerRot::ZYZ, - 0., - self.position_pitch, - -self.position_yaw, - ) + ]) + * Mat4::from_euler( + glam::EulerRot::ZYZ, + 0., + self.position_pitch, + -self.position_yaw, + ) } } diff --git a/src/lib.rs b/src/lib.rs index 8b7384d..0873c0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,17 +8,19 @@ use rand_distr::Distribution as _; use crate::{ camera::OrbitalCamera, ray::Ray, - render::lines::{LookParams, Mesh, Pipeline, Vertex}, + render::{ + DEPTH_FORMAT, OUTPUT_FORMAT, + lines::{LookParams, Mesh, Pipeline, Vertex}, + }, trace::{Hit, Lambertian, Reflector, Scene, Source, Sphere}, }; mod camera; mod ray; mod render; +mod shape; mod trace; -const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb; - #[derive(Debug, Clone, Copy, PartialEq)] #[repr(C)] pub struct SphericalPosition { @@ -27,6 +29,13 @@ pub struct SphericalPosition { pub distance: f32, } +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum UseNormal { + Light, + Camera, +} + #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RedrawArgs { @@ -36,7 +45,8 @@ pub struct RedrawArgs { pub light_spread: f32, pub accum_sigma: f32, pub accum_scale: f32, - pub reflections: u32, + pub reflections: u8, + pub use_normal: UseNormal, pub show_axes: bool, pub show_shapes: bool, pub show_hit_emission: bool, @@ -46,6 +56,36 @@ pub struct RedrawArgs { pub show_light: bool, } +impl Default for RedrawArgs { + fn default() -> Self { + Self { + camera_position: SphericalPosition { + yaw: PI / 4., + pitch: 0.5f32.sqrt().atan(), + distance: 3.0, + }, + light_position: SphericalPosition { + yaw: 0.0, + pitch: PI / 3., + distance: 1.0, + }, + light_radius: 0.125, + light_spread: 0.125, + accum_sigma: 0.025, + accum_scale: 0.01, + reflections: 2, + use_normal: UseNormal::Light, + show_axes: true, + show_shapes: true, + show_hit_emission: false, + show_miss_emission: false, + show_direct_hit: false, + show_indirect_hit: false, + show_light: true, + } + } +} + pub struct Gpu { device: wgpu::Device, queue: wgpu::Queue, @@ -57,7 +97,10 @@ pub struct Core { queue: wgpu::Queue, surface: wgpu::Surface<'static>, + depth: wgpu::Texture, + pipeline: Pipeline, + mesh_pipe: render::faces::Pipeline, tripod: Mesh, } @@ -113,23 +156,29 @@ impl OrbitalCamera { } impl Core { - pub fn new(gpu: Gpu) -> Self { + pub fn new(gpu: Gpu, pixel_size: UVec2) -> Self { let Gpu { device, queue, surface, } = gpu; - let pipeline = Pipeline::new(&device, OUTPUT_FORMAT); + let pipeline = Pipeline::new(&device); + let mesh_pipe = render::faces::Pipeline::new(&device); let tripod = new_tripod(&device); + let depth = Self::create_depth_buffer(&device, pixel_size); queue.submit([]); // flush buffer updates + Self::configure_surface(&surface, &device, pixel_size); + Self { device, queue, surface, + depth, pipeline, tripod, + mesh_pipe, } } @@ -152,9 +201,18 @@ impl Core { m: perspective * camera.transform(), }, ); + self.mesh_pipe.set_look( + &self.queue, + render::faces::LookParams { + m: perspective * camera.transform(), + }, + ); self.queue.submit([]); // flush buffer updates let view = output.create_view(&wgpu::TextureViewDescriptor::default()); + let depth_view = self + .depth + .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = self .device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); @@ -168,7 +226,14 @@ impl Core { store: wgpu::StoreOp::Store, }, })], - depth_stencil_attachment: None, + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.), + store: wgpu::StoreOp::Discard, + }), + stencil_ops: None, + }), ..Default::default() }); if args.show_axes { @@ -291,34 +356,47 @@ impl Core { hits1 = hits2; } } - let mut camera_ray_display: Vec = Vec::with_capacity(camera_rays.len()); - if args.show_light { + let light_at = { let sigma2 = args.accum_sigma.powi(2); - let accum_normalizator = (2. * PI * sigma2).sqrt().recip(); - for ray in camera_rays { - let Some(hit) = scene.trace_ray(ray) else { - continue; - }; + let accum_normalizator = (2. * PI * sigma2).recip(); + let hits = &hits; + move |hit: Hit| -> f32 { let mut total_cd = 0.0f32; - for light_hit in &hits { + for light_hit in hits { let d2 = hit.incident.base.distance_squared(light_hit.incident.base); if d2 > 9. * sigma2 { continue; } - assert!(hit.normal.is_normalized()); + let normal = match args.use_normal { + UseNormal::Light => light_hit.normal, + UseNormal::Camera => hit.normal, + }; + assert!(normal.is_normalized()); assert!(hit.incident.dir.is_normalized()); let reflector = Lambertian; let in_lm = 1.0; - let out_cd = in_lm - * hit.normal.dot(-hit.incident.dir) - * reflector.brdf(hit.normal, hit.incident.dir, -ray.dir); + let out_cd = + in_lm * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir); let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp(); total_cd += weight * out_cd; } - let brightness = 3. * (1. - (1. + total_cd * args.accum_scale).recip()); - let r = args.accum_sigma; - let color = vec3(brightness, brightness - 1., brightness - 2.) - .clamp(Vec3::splat(0.), Vec3::splat(1.)); + total_cd * args.accum_scale + } + }; + let colormap = |light: f32| { + let brightness = 3. * (1. - (1. + light).recip()); + vec3(brightness, brightness - 1., brightness - 2.) + .clamp(Vec3::splat(0.), Vec3::splat(1.)) + }; + let mut camera_ray_display: Vec = Vec::with_capacity(camera_rays.len()); + if args.show_light { + let r = args.accum_sigma; + for ray in camera_rays { + let Some(hit) = scene.trace_ray(ray) else { + continue; + }; + let light = light_at(hit); + let color = colormap(light); let vertex = |off: Vec3| Vertex { pos: hit.incident.base + r * off, color, @@ -333,6 +411,36 @@ impl Core { ]); } } + if args.show_shapes { + let mut meshes = Vec::new(); + for obj in &scene.objects { + let mesh = shape::octo_sphere((obj.radius * 45.) as usize + 7); + let obj_mesh = render::faces::Mesh::new( + &self.device, + &mesh + .vertices + .iter() + .map(|&v| { + let pos = obj.position + obj.radius * v; + let normal = v; + let mut dir = (pos - camera.position()).normalize(); + if !dir.is_finite() { + dir = camera.direction(); + } + let light = light_at(Hit { + incident: Ray::new(pos, dir), + normal, + }); + let color = Vec3::splat(light); + render::faces::Vertex { pos, color } + }) + .collect::>(), + bytemuck::cast_slice(&mesh.indices), + ); + meshes.push(obj_mesh); + } + self.mesh_pipe.render(&mut pass, &meshes); + } if !source_ray_display.is_empty() { self.pipeline .render(&mut pass, [&Mesh::new(&self.device, &source_ray_display)]); @@ -346,10 +454,26 @@ impl Core { self.queue.submit(std::iter::once(encoder.finish())); } - /// Configures the renderer for a given target size. - pub fn configure(&mut self, pixel_size: UVec2) { - self.surface.configure( - &self.device, + fn create_depth_buffer(device: &wgpu::Device, pixel_size: UVec2) -> wgpu::Texture { + device.create_texture(&wgpu::TextureDescriptor { + label: Some("depth buffer"), + size: wgpu::Extent3d { + width: pixel_size.x, + height: pixel_size.y, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }) + } + + fn configure_surface(surface: &wgpu::Surface, device: &wgpu::Device, pixel_size: UVec2) { + surface.configure( + device, &wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, format: OUTPUT_FORMAT, @@ -363,6 +487,12 @@ impl Core { ); } + /// Configures the renderer for a given target size. + pub fn configure(&mut self, pixel_size: UVec2) { + Self::configure_surface(&self.surface, &self.device, pixel_size); + self.depth = Self::create_depth_buffer(&self.device, pixel_size); + } + /// Redraws the entire surface. /// /// [`Self::configure`] must be called at least once before this. diff --git a/src/main.rs b/src/main.rs index f08bf16..da72f20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ -use std::{f32::consts::PI, sync::Arc}; +use std::sync::Arc; use glam::uvec2; -use photon_light::{Core, RedrawArgs, SphericalPosition, init_gpu_inner}; use winit::{ application::ApplicationHandler, event::WindowEvent, @@ -9,7 +8,9 @@ use winit::{ window::Window, }; -const TITLE: &str = "WGPU example"; +use photon_light::{Core, RedrawArgs, init_gpu_inner}; + +const TITLE: &str = "Photon Light"; struct MainWindow { window: Arc, @@ -26,8 +27,7 @@ impl MainWindow { instance.create_surface(Arc::clone(&window)) })) .unwrap(); - let mut core = Core::new(gpu); - core.configure(uvec2(1, 1)); + let core = Core::new(gpu, uvec2(1, 1)); Self { window, core } } @@ -37,30 +37,7 @@ impl MainWindow { WindowEvent::Resized(physical_size) => self .core .configure(uvec2(physical_size.width, physical_size.height)), - WindowEvent::RedrawRequested => self.core.redraw(&RedrawArgs { - camera_position: SphericalPosition { - yaw: PI / 4., - pitch: 0.5f32.sqrt().atan(), - distance: 3.0, - }, - light_position: SphericalPosition { - yaw: 0.0, - pitch: PI / 3., - distance: 1.0, - }, - light_radius: 0.125, - light_spread: 0.125, - accum_sigma: 0.025, - accum_scale: 0.01, - reflections: 2, - show_axes: true, - show_shapes: true, - show_hit_emission: false, - show_miss_emission: false, - show_direct_hit: false, - show_indirect_hit: false, - show_light: true, - }), + WindowEvent::RedrawRequested => self.core.redraw(&RedrawArgs::default()), _ => {} } } diff --git a/src/render/colormap.wgsl b/src/render/colormap.wgsl new file mode 100644 index 0000000..e2af29d --- /dev/null +++ b/src/render/colormap.wgsl @@ -0,0 +1,32 @@ +struct LookParams { + m: mat4x4f, +} + +struct Vertex { + @location(0) pos: vec3f, + @location(1) color: vec3f, +} + +struct Varying { + @builtin(position) screen: vec4f, + @location(0) color: vec4f, +} + +@group(0) @binding(0) var look: LookParams; + +fn colormap(light: f32) -> vec3f { + let brightness = 3. * (1. - 1. / (1. + light)); + return vec3(brightness, brightness - 1., brightness - 2.); +} + +@vertex +fn on_vertex(in: Vertex) -> Varying { + let pos = look.m * vec4f(in.pos, 1.0); + let color = vec4f(in.color, 1.0); + return Varying(pos, color); +} + +@fragment +fn on_fragment(in: Varying) -> @location(0) vec4f { + return vec4f(colormap(in.color.x), 1.0); +} diff --git a/src/render/faces.rs b/src/render/faces.rs new file mode 100644 index 0000000..7879c30 --- /dev/null +++ b/src/render/faces.rs @@ -0,0 +1,169 @@ +use std::mem::offset_of; + +use bytemuck::{Pod, Zeroable, bytes_of, cast_slice}; +use glam::{Mat4, Vec3}; +use wgpu::util::DeviceExt as _; + +use crate::render::{DEPTH_FORMAT, OUTPUT_FORMAT}; + +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct LookParams { + pub m: Mat4, +} + +#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct Vertex { + pub pos: Vec3, + pub color: Vec3, +} + +impl Vertex { + pub fn new(pos: Vec3, color: Vec3) -> Self { + Self { pos, color } + } +} + +pub struct Mesh { + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + index_count: u32, +} + +impl Mesh { + pub fn new(device: &wgpu::Device, vertices: &[Vertex], indices: &[u16]) -> Self { + if vertices.len() >= 1 << 16 { + panic!("too many vertices"); + } + for index in indices { + if *index as usize >= vertices.len() { + panic!( + "vertex index out of bounds: {index} out of {}", + vertices.len() + ); + } + } + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::VERTEX, + contents: cast_slice(vertices), + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::INDEX, + contents: cast_slice(indices), + }); + Self { + vertex_buffer, + index_buffer, + index_count: indices.len().try_into().expect("too many indices"), + } + } +} + +pub struct Pipeline { + look_buf: wgpu::Buffer, + bindings: wgpu::BindGroup, + pipeline: wgpu::RenderPipeline, +} + +impl Pipeline { + pub fn new(device: &wgpu::Device) -> Self { + let look_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: size_of::() as u64, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(super::COLORMAP_SHADER.into()), + }); + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + buffers: &[wgpu::VertexBufferLayout { + array_stride: size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + shader_location: 0, + offset: offset_of!(Vertex, pos) as u64, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + shader_location: 1, + offset: offset_of!(Vertex, color) as u64, + format: wgpu::VertexFormat::Float32x3, + }, + ], + }], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + ..Default::default() + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: None, + compilation_options: wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format: OUTPUT_FORMAT, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); + let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: look_buf.as_entire_binding(), + }], + }); + Self { + look_buf, + bindings, + pipeline, + } + } + + pub fn set_look(&self, queue: &wgpu::Queue, look: LookParams) { + queue.write_buffer(&self.look_buf, 0, bytes_of(&look)); + } + + pub fn render<'a>( + &self, + pass: &mut wgpu::RenderPass, + meshes: impl IntoIterator, + ) { + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, &self.bindings, &[]); + for mesh in meshes { + pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16); + pass.draw_indexed(0..mesh.index_count, 0, 0..1); + } + } +} diff --git a/src/render/lines.rs b/src/render/lines.rs index 1b5f3fc..b68fb86 100644 --- a/src/render/lines.rs +++ b/src/render/lines.rs @@ -4,6 +4,8 @@ use bytemuck::{Pod, Zeroable, bytes_of, cast_slice}; use glam::{Mat4, Vec3}; use wgpu::util::DeviceExt as _; +use crate::render::{DEPTH_FORMAT, OUTPUT_FORMAT}; + #[derive(Debug, Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct LookParams { @@ -49,7 +51,7 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { + pub fn new(device: &wgpu::Device) -> Self { let look_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: size_of::() as u64, @@ -57,10 +59,9 @@ impl Pipeline { mapped_at_creation: false, }); - let shader = std::fs::read_to_string("shaders/line.wgsl").unwrap(); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, - source: wgpu::ShaderSource::Wgsl(shader.into()), + source: wgpu::ShaderSource::Wgsl(super::SIMPLE_SHADER.into()), }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, @@ -90,7 +91,17 @@ impl Pipeline { topology: wgpu::PrimitiveTopology::LineList, ..Default::default() }, - depth_stencil: None, + depth_stencil: Some(wgpu::DepthStencilState { + format: DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState { + constant: -2, + slope_scale: 0., + clamp: 0., + }, + }), multisample: wgpu::MultisampleState { count: 1, mask: !0, @@ -101,7 +112,7 @@ impl Pipeline { entry_point: None, compilation_options: wgpu::PipelineCompilationOptions::default(), targets: &[Some(wgpu::ColorTargetState { - format, + format: OUTPUT_FORMAT, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, })], diff --git a/src/render/mod.rs b/src/render/mod.rs index ef14730..3601ec0 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1 +1,8 @@ +pub mod faces; pub mod lines; + +static SIMPLE_SHADER: &str = include_str!("simple.wgsl"); +static COLORMAP_SHADER: &str = include_str!("colormap.wgsl"); + +pub const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb; +pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus; diff --git a/shaders/line.wgsl b/src/render/simple.wgsl similarity index 100% rename from shaders/line.wgsl rename to src/render/simple.wgsl diff --git a/src/shape.rs b/src/shape.rs new file mode 100644 index 0000000..311076d --- /dev/null +++ b/src/shape.rs @@ -0,0 +1,348 @@ +use std::f32::consts::PI; + +use glam::{Vec3, vec3}; + +pub type Face = [u16; 3]; + +#[derive(Debug, Clone)] +pub struct Mesh { + pub vertices: Vec, + pub indices: Vec, +} + +pub fn sphere(subdiv: usize) -> Mesh { + assert!(subdiv >= 2); + assert!(2 * subdiv * (subdiv - 1) + 2 <= 1 << 16); + + let mut vertices = Vec::new(); + vertices.push(Vec3::Z); + for j in 1..subdiv { + let v = j as f32 / subdiv as f32; + let theta = PI * v; + let (xy, z) = theta.sin_cos(); + for i in 0..2 * subdiv { + let u = i as f32 / (2 * subdiv) as f32; + let phi = 2. * PI * u; + let (y, x) = phi.sin_cos(); + vertices.push(vec3(xy * x, xy * y, z)); + } + } + vertices.push(-Vec3::Z); + assert_eq!(vertices.len(), 2 * subdiv * (subdiv - 1) + 2); + + let mut indices = Vec::new(); + let subdiv = subdiv as u16; + for i in 0..2 * subdiv - 1 { + indices.push([0, i + 1, i + 2]); + } + indices.push([0, 2 * subdiv, 1]); + for j in 0..subdiv - 2 { + let k1 = j * 2 * subdiv + 1; + let k2 = k1 + 2 * subdiv; + for i in 0..2 * subdiv - 1 { + let a = k1 + i; + let b = k2 + i; + indices.push([a, b, b + 1]); + indices.push([a, b + 1, a + 1]); + } + let a = k1 + 2 * subdiv - 1; + let b = k2 + 2 * subdiv - 1; + indices.push([a, b, k2]); + indices.push([a, k2, k1]); + } + let k1 = 2 * subdiv * (subdiv - 2) + 1; + let k2 = 2 * subdiv * (subdiv - 1) + 1; + for i in 0..2 * subdiv - 1 { + indices.push([k1 + i, k2, k1 + i + 1]); + } + indices.push([k1 + 2 * subdiv - 1, k2, k1]); + + Mesh { vertices, indices } +} + +pub fn octo_sphere(subdiv: usize) -> Mesh { + assert!(subdiv >= 2); + + let mut vertices = Vec::new(); + let theta_step = PI / subdiv as f32; + let v_belt = |j: usize, n: usize| { + let phi_step = 2. * PI / n as f32; + let theta = j as f32 * theta_step; + let (xy, z) = theta.sin_cos(); + (0..n).map(move |i| { + let phi = i as f32 * phi_step; + let (y, x) = phi.sin_cos(); + vec3(xy * x, xy * y, z) + }) + }; + vertices.push(Vec3::Z); + for j in 1..subdiv / 2 { + vertices.extend(v_belt(j, 4 * j)); + } + { + let j = subdiv / 2; + vertices.extend(v_belt(j, 4 * j)); + } + for j in (subdiv + 2) / 2..subdiv { + vertices.extend(v_belt(j, 4 * (subdiv - j))); + } + vertices.push(-Vec3::Z); + + fn i_cap(top: usize, start: usize) -> impl Iterator { + gen move { + yield [top, start, start + 1]; + yield [top, start + 1, start + 2]; + yield [top, start + 2, start + 3]; + yield [top, start + 3, start]; + } + .map(face) + } + fn i_skew_belt( + short_start: usize, + long_start: usize, + seg_short_face: usize, + ) -> impl Iterator { + gen move { + let seg_long_face = seg_short_face + 1; + let n_short_total = 4 * seg_short_face; + let n_long_total = 4 * seg_long_face; + for face in 0..4 { + let short = move |k| short_start + (face * seg_short_face + k) % n_short_total; + let long = move |k| long_start + (face * seg_long_face + k) % n_long_total; + yield [short(0), long(0), long(1)]; + for k in 0..seg_short_face { + yield [short(k), long(k + 1), short(k + 1)]; + yield [short(k + 1), long(k + 1), long(k + 2)]; + } + } + } + .map(face) + } + fn i_belt(start1: usize, start2: usize, seg_per_face: usize) -> impl Iterator { + gen move { + let n = 4 * seg_per_face; + for k in 0..n { + let k2 = (k + 1) % n; + let a = start1 + k; + let b = start2 + k; + let c = start1 + k2; + let d = start2 + k2; + yield [a, b, c]; + yield [c, b, d]; + } + } + .map(face) + } + fn face(f: [usize; 3]) -> Face { + f.map(|i| i as u16) + } + fn flip(f: Face) -> Face { + [f[1], f[0], f[2]] + } + + let mut indices = Vec::new(); + indices.extend(i_cap(0, 1)); + let mut k = 1; + for j in 1..subdiv - 1 { + let j2 = (subdiv - 1) - j; + match j.cmp(&j2) { + std::cmp::Ordering::Less => { + let n = 4 * j; + indices.extend(i_skew_belt(k, k + n, j)); + k += n; + } + std::cmp::Ordering::Equal => { + let n = 4 * j; + indices.extend(i_belt(k, k + n, j)); + k += n; + } + std::cmp::Ordering::Greater => { + let n = 4 * (j2 + 1); + indices.extend(i_skew_belt(k + n, k, j2).map(flip)); + k += n; + } + } + } + indices.extend(i_cap(k + 4, k).map(flip)); + + Mesh { vertices, indices } +} + +#[cfg(test)] +mod tests { + use approx::abs_diff_eq; + + use super::*; + + #[test] + fn test_sphere_2_topology() { + assert_eq!( + sphere(2).indices, + vec![ + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + [1, 5, 2], + [2, 5, 3], + [3, 5, 4], + [4, 5, 1], + ] + ); + } + + fn are_equal_eps(left: &[Vec3], right: &[Vec3], eps: f32) -> bool { + if left.len() != right.len() { + return false; + } + left.iter() + .zip(right.iter()) + .all(|(a, b)| abs_diff_eq!(a, b, epsilon = eps)) + } + + #[test] + fn test_sphere_2_geometry() { + let left = sphere(2).vertices; + let right = [Vec3::Z, Vec3::X, Vec3::Y, -Vec3::X, -Vec3::Y, -Vec3::Z]; + assert!( + are_equal_eps(&left, &right, 1e-6), + "assertion `left == right` failed\n left: {left:?}\n right: {right:?}" + ); + } + + #[test] + fn test_sphere_3_topology() { + assert_eq!( + sphere(3).indices, + vec![ + // top + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 6], + [0, 6, 1], + // belt + [1, 7, 8], + [1, 8, 2], + [2, 8, 9], + [2, 9, 3], + [3, 9, 10], + [3, 10, 4], + [4, 10, 11], + [4, 11, 5], + [5, 11, 12], + [5, 12, 6], + [6, 12, 7], + [6, 7, 1], + // bottom + [7, 13, 8], + [8, 13, 9], + [9, 13, 10], + [10, 13, 11], + [11, 13, 12], + [12, 13, 7], + ] + ); + } + + #[test] + fn test_octo_sphere_2_topology() { + assert_eq!( + octo_sphere(2).indices, + vec![ + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + [1, 5, 2], + [2, 5, 3], + [3, 5, 4], + [4, 5, 1], + ] + ); + } + + #[test] + fn test_octo_sphere_2_geometry() { + let left = octo_sphere(2).vertices; + let right = [Vec3::Z, Vec3::X, Vec3::Y, -Vec3::X, -Vec3::Y, -Vec3::Z]; + assert!( + are_equal_eps(&left, &right, 1e-6), + "assertion `left == right` failed\n left: {left:?}\n right: {right:?}" + ); + } + + #[test] + fn test_octo_sphere_3_topology() { + assert_eq!( + octo_sphere(3).indices, + vec![ + // top + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + // belt + [1, 5, 2], + [2, 5, 6], + [2, 6, 3], + [3, 6, 7], + [3, 7, 4], + [4, 7, 8], + [4, 8, 1], + [1, 8, 5], + // bottom + [5, 9, 6], + [6, 9, 7], + [7, 9, 8], + [8, 9, 5], + ] + ); + } + + #[test] + fn test_octo_sphere_4_topology() { + assert_eq!( + octo_sphere(4).indices, + vec![ + // top + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + // belt 1 + [1, 5, 6], + [1, 6, 2], + [2, 6, 7], + [2, 7, 8], + [2, 8, 3], + [3, 8, 9], + [3, 9, 10], + [3, 10, 4], + [4, 10, 11], + [4, 11, 12], + [4, 12, 1], + [1, 12, 5], + // belt 2 + [5, 13, 6], + [6, 13, 14], + [6, 14, 7], + [7, 14, 8], + [8, 14, 15], + [8, 15, 9], + [9, 15, 10], + [10, 15, 16], + [10, 16, 11], + [11, 16, 12], + [12, 16, 13], + [12, 13, 5], + // bottom + [13, 17, 14], + [14, 17, 15], + [15, 17, 16], + [16, 17, 13], + ] + ); + } +} diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 0fd6cba..191380f 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -1,6 +1,6 @@ include(impl.cmake) -qt_add_executable(photon_light +qt_add_executable(photon-light src/api.cxx src/main.cxx src/main_window.cxx @@ -8,6 +8,6 @@ qt_add_executable(photon_light src/viewport.cxx ) -target_link_libraries(photon_light PRIVATE Qt6::Gui Qt6::Widgets) -target_link_libraries(photon_light PRIVATE photon_light_impl) -target_include_directories(photon_light PRIVATE src) +target_link_libraries(photon-light PRIVATE Qt6::Gui Qt6::Widgets) +target_link_libraries(photon-light PRIVATE photon_light_impl) +target_include_directories(photon-light PRIVATE src) diff --git a/ui/src/api.cxx b/ui/src/api.cxx index d65a690..aaa2b64 100644 --- a/ui/src/api.cxx +++ b/ui/src/api.cxx @@ -4,7 +4,7 @@ #include namespace ffi { -extern "C" Core* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window); +extern "C" Core* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height); extern "C" void rt4_viewport_destroy(Core* viewport); extern "C" void rt4_viewport_configure(Core* viewport, std::uint32_t width, std::uint32_t height); extern "C" void rt4_viewport_redraw(Core* viewport, const RedrawArgs* args); @@ -26,13 +26,13 @@ BoxCore& BoxCore::operator=(BoxCore&& b) { return *this; } -BoxCore BoxCore::from_xcb(xcb_connection_t* connection, std::uint32_t window) { +BoxCore BoxCore::from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height) { if (!connection) throw std::logic_error("attempt to use a null connection"); if (!window) throw std::logic_error("attempt to use a null window"); BoxCore out; - out.ptr.ptr = ffi::rt4_viewport_create(connection, window); + out.ptr.ptr = ffi::rt4_viewport_create(connection, window, width, height); return out; } diff --git a/ui/src/api.hxx b/ui/src/api.hxx index caf32cd..d46272e 100644 --- a/ui/src/api.hxx +++ b/ui/src/api.hxx @@ -13,6 +13,11 @@ struct SphericalPosition { float distance = 1.; }; +enum class UseNormal: std::uint8_t { + Light, + Camera, +}; + struct RedrawArgs { SphericalPosition camera_position; SphericalPosition light_position; @@ -20,7 +25,8 @@ struct RedrawArgs { float light_spread = 0.; float accum_sigma = 1.; float accum_scale = 1.; - std::uint32_t reflections = 0; + std::uint8_t reflections = 0; + UseNormal use_normal = UseNormal::Light; bool show_axes = true; bool show_shapes = true; bool show_hit_emission = true; @@ -31,6 +37,7 @@ struct RedrawArgs { }; } // namespace ffi +using ffi::UseNormal; using ffi::RedrawArgs; using ffi::SphericalPosition; @@ -62,7 +69,7 @@ public: void reset(); - static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window); + static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height); private: MutCore ptr; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 9ee96ef..ed5dce1 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -1,12 +1,14 @@ use std::{ffi::c_void, num::NonZero, ptr::NonNull}; -use glam::uvec2; -use photon_light::{Core, RedrawArgs, init_gpu_inner}; +use glam::{UVec2, uvec2}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle}; +use photon_light::{Core, RedrawArgs, init_gpu_inner}; + unsafe fn create_viewport( display: impl Into, window: impl Into, + size: UVec2, ) -> Box { let target = wgpu::SurfaceTargetUnsafe::RawHandle { raw_display_handle: display.into(), @@ -16,17 +18,19 @@ unsafe fn create_viewport( instance.create_surface_unsafe(target) })) .unwrap(); - Box::new(Core::new(gpu)) + Box::new(Core::new(gpu, size)) } #[unsafe(no_mangle)] unsafe extern "C" fn rt4_viewport_create( connection: NonNull, window: NonZero, + width: u32, + height: u32, ) -> Box { let display = XcbDisplayHandle::new(Some(connection), 0); let window = XcbWindowHandle::new(window); - unsafe { create_viewport(display, window) } + unsafe { create_viewport(display, window, uvec2(width, height)) } } #[unsafe(no_mangle)] diff --git a/ui/src/main.cxx b/ui/src/main.cxx index f8ab482..03115ce 100644 --- a/ui/src/main.cxx +++ b/ui/src/main.cxx @@ -3,6 +3,9 @@ #include int main(int argc, char* argv[]) { + // only X11 is supported so, use XCB unless overriden + setenv("QT_QPA_PLATFORM", "xcb", 0); + QApplication app(argc, argv); auto w = new PhotonLight; diff --git a/ui/src/main_window.cxx b/ui/src/main_window.cxx index dff2f7d..75861e6 100644 --- a/ui/src/main_window.cxx +++ b/ui/src/main_window.cxx @@ -16,6 +16,11 @@ float deg_to_rad(float val) { } void PhotonLight::updateView() { + UseNormal use_normal = {}; + if (m_ui->normalFromLight->isChecked()) + use_normal = UseNormal::Light; + else if (m_ui->normalFromCamera->isChecked()) + use_normal = UseNormal::Camera; RedrawArgs args{ .camera_position = SphericalPosition{ .yaw = deg_to_rad(m_ui->cameraYaw->value()), @@ -31,7 +36,8 @@ void PhotonLight::updateView() { .light_spread = 0.125, .accum_sigma = exp10f(m_ui->accumSigma->value() / 25.0), .accum_scale = exp10f(m_ui->accumScale->value() / 25.0), - .reflections = std::uint32_t(m_ui->reflections->value()), + .reflections = std::uint8_t(m_ui->reflections->value()), + .use_normal = use_normal, .show_axes = m_ui->displayAxes->isChecked(), .show_shapes = m_ui->displayShapes->isChecked(), .show_hit_emission = m_ui->displayEmitted->isChecked(), @@ -46,9 +52,14 @@ void PhotonLight::updateView() { m_ui->lightYawLabel->setText(tr("Yaw: %1 deg").arg(QString::number(qRadiansToDegrees(args.light_position.yaw)))); m_ui->lightPitchLabel->setText(tr("Pitch: %1 deg").arg(QString::number(qRadiansToDegrees(args.light_position.pitch)))); m_ui->lightDistanceLabel->setText(tr("Distance: %1").arg(QString::number(args.light_position.distance))); - m_ui->accumSigmaLabel->setText(tr("Averaging radius: %1").arg(QString::number(args.accum_sigma, 'f', 3))); - m_ui->accumScaleLabel->setText(tr("Brightness: %1").arg(QString::number(args.accum_scale, 'f', 3))); + m_ui->accumSigmaLabel->setText(tr("Averaging radius: %1").arg(QString::number(args.accum_sigma, 'f', 5))); + m_ui->accumScaleLabel->setText(tr("Brightness: %1").arg(QString::number(args.accum_scale, 'f', 5))); m_ui->viewport->setView(args); } +void PhotonLight::updateViewIf(bool update) { + if (update) + updateView(); +} + #include "moc_main_window.cpp" diff --git a/ui/src/main_window.hxx b/ui/src/main_window.hxx index 1ca73cc..27b63ff 100644 --- a/ui/src/main_window.hxx +++ b/ui/src/main_window.hxx @@ -16,6 +16,7 @@ public: public slots: void updateView(); + void updateViewIf(bool update); // for radio buttons private: const std::unique_ptr m_ui; diff --git a/ui/src/main_window.ui b/ui/src/main_window.ui index 697c566..9c4f169 100644 --- a/ui/src/main_window.ui +++ b/ui/src/main_window.ui @@ -245,7 +245,7 @@ 0 - -40 + -45 Qt::Horizontal @@ -271,7 +271,7 @@ 0 - -50 + -85 Qt::Horizontal @@ -298,6 +298,30 @@ + + + + Use normal at + + + + + + + Hit position + + + true + + + + + + + Show position + + + @@ -313,7 +337,7 @@ Axes - true + false @@ -363,7 +387,7 @@ Average light - true + false @@ -637,8 +661,41 @@ + + normalFromLight + toggled(bool) + MainWindow + updateViewIf(bool) + + + 1429 + 787 + + + 1381 + 783 + + + + + normalFromCamera + toggled(bool) + MainWindow + updateViewIf(bool) + + + 1932 + 816 + + + 1600 + 874 + + + updateView() + updateViewIf(bool) diff --git a/ui/src/viewport.cxx b/ui/src/viewport.cxx index 483ba07..e681bd9 100644 --- a/ui/src/viewport.cxx +++ b/ui/src/viewport.cxx @@ -61,9 +61,9 @@ void Viewport::recreate() try { fprintf(stderr, "connection %p, window %#08x\n", xcb_connection, x11_window); + const QSize device_size = size() * devicePixelRatio(); core.reset(); - core = BoxCore::from_xcb(xcb_connection, x11_window); - updateSize(); + core = BoxCore::from_xcb(xcb_connection, x11_window, device_size.width(), device_size.height()); } catch (const std::exception& e) { fprintf(stderr, "failed to recreate the viewport: %s", e.what()); }