Compare commits

..

No commits in common. "master" and "two-way" have entirely different histories.

29 changed files with 487 additions and 1254 deletions

View File

@ -1,17 +1,14 @@
cmake_minimum_required(VERSION 3.18...4.2)
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)
find_package(Qt6 REQUIRED COMPONENTS Gui Widgets)
find_program(CARGO cargo REQUIRED)
set(CARGO_TARGET_DIR "${CMAKE_SOURCE_DIR}/target" CACHE PATH "Path to the Cargo target directory")
include(diplomat.cmake)
set(CARGO_TARGET_DIR "${CMAKE_BINARY_DIR}/cargo")
qt_standard_project_setup()
add_subdirectory(src)
add_subdirectory(ui)

359
Cargo.lock generated
View File

@ -159,15 +159,15 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.19.1"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytemuck"
version = "1.25.2"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
dependencies = [
"bytemuck_derive",
]
@ -180,14 +180,14 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
name = "bytes"
version = "1.11.0"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "calloop"
@ -217,9 +217,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.54"
version = "1.2.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
dependencies = [
"find-msvc-tools",
"jobserver",
@ -354,38 +354,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
[[package]]
name = "diplomat"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a80510e6103d35c5cd031c0e3f21165b32742c301ee3dafdbb5904c6dca46a"
dependencies = [
"diplomat_core",
"proc-macro2",
"quote",
"syn 3.0.9",
]
[[package]]
name = "diplomat-runtime"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9d981121ba76150987fda68185b37853c0b928a55dfdc28897348cd068440be"
[[package]]
name = "diplomat_core"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5c594efd1d097105255fc325c430a517c97b7b7c69f8727f653593e1ce726"
dependencies = [
"proc-macro2",
"quote",
"serde",
"smallvec",
"strck",
"syn 3.0.9",
]
[[package]]
name = "dispatch"
version = "0.2.0"
@ -440,9 +408,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.8"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "foldhash"
@ -474,7 +442,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -483,38 +451,13 @@ 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.3",
"rustix 1.1.2",
"windows-link",
]
@ -543,9 +486,9 @@ dependencies = [
[[package]]
name = "glam"
version = "0.30.10"
version = "0.30.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46"
dependencies = [
"approx",
"bytemuck",
@ -646,9 +589,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.16.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
dependencies = [
"foldhash 0.2.0",
]
@ -667,12 +610,12 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "indexmap"
version = "2.13.0"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"hashbrown 0.16.0",
]
[[package]]
@ -709,9 +652,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.85"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
dependencies = [
"once_cell",
"wasm-bindgen",
@ -736,9 +679,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "libc"
version = "0.2.180"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libloading"
@ -752,19 +695,19 @@ dependencies = [
[[package]]
name = "libm"
version = "0.2.16"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libredox"
version = "0.1.12"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
dependencies = [
"bitflags 2.10.0",
"libc",
"redox_syscall 0.7.0",
"redox_syscall 0.5.18",
]
[[package]]
@ -796,9 +739,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.29"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "malloc_buf"
@ -852,7 +795,7 @@ dependencies = [
"cfg_aliases",
"codespan-reporting",
"half",
"hashbrown 0.16.1",
"hashbrown 0.16.0",
"hexf-parse",
"indexmap",
"libm",
@ -861,7 +804,7 @@ dependencies = [
"once_cell",
"rustc-hash",
"spirv",
"thiserror 2.0.18",
"thiserror 2.0.17",
"unicode-ident",
]
@ -924,7 +867,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -1147,11 +1090,10 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "orbclient"
version = "0.3.50"
version = "0.3.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd"
checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827"
dependencies = [
"libc",
"libredox",
]
@ -1214,8 +1156,6 @@ version = "0.1.0"
dependencies = [
"approx",
"bytemuck",
"diplomat",
"diplomat-runtime",
"glam",
"pollster",
"rand",
@ -1225,6 +1165,17 @@ dependencies = [
"winit",
]
[[package]]
name = "photon-light-impl"
version = "0.1.0"
dependencies = [
"glam",
"photon-light",
"pollster",
"raw-window-handle",
"wgpu",
]
[[package]]
name = "pin-project"
version = "1.1.10"
@ -1242,7 +1193,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -1251,12 +1202,6 @@ 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"
@ -1273,7 +1218,7 @@ dependencies = [
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix 1.1.3",
"rustix 1.1.2",
"windows-sys 0.61.2",
]
@ -1285,9 +1230,9 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
[[package]]
name = "portable-atomic"
version = "1.13.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "portable-atomic-util"
@ -1324,9 +1269,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.106"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
@ -1339,18 +1284,18 @@ checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
[[package]]
name = "quick-xml"
version = "0.38.4"
version = "0.37.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.44"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
@ -1363,9 +1308,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.5"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
@ -1383,9 +1328,9 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.9.5"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
@ -1439,15 +1384,6 @@ 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"
@ -1475,9 +1411,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.1.3"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags 2.10.0",
"errno",
@ -1553,7 +1489,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -1570,9 +1506,9 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "slotmap"
version = "1.1.1"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [
"version_check",
]
@ -1632,15 +1568,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strck"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42316e70da376f3d113a68d138a60d8a9883c604fe97942721ec2068dab13a9f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "strict-num"
version = "0.1.1"
@ -1649,20 +1576,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "syn"
version = "2.0.114"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
@ -1689,11 +1605,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.18"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl 2.0.18",
"thiserror-impl 2.0.17",
]
[[package]]
@ -1704,18 +1620,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -1745,18 +1661,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.23.10+spec-1.0.0"
version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
dependencies = [
"indexmap",
"toml_datetime",
@ -1766,18 +1682,18 @@ dependencies = [
[[package]]
name = "toml_parser"
version = "1.0.6+spec-1.1.0"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
"winnow",
]
[[package]]
name = "tracing"
version = "0.1.44"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-core",
@ -1785,9 +1701,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.36"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
[[package]]
name = "ttf-parser"
@ -1831,18 +1747,18 @@ dependencies = [
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.108"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
dependencies = [
"cfg-if",
"once_cell",
@ -1853,12 +1769,11 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.58"
version = "0.4.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
dependencies = [
"cfg-if",
"futures-util",
"js-sys",
"once_cell",
"wasm-bindgen",
@ -1867,9 +1782,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.108"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1877,35 +1792,35 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.108"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.108"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wayland-backend"
version = "0.3.12"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9"
checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
dependencies = [
"cc",
"downcast-rs",
"rustix 1.1.3",
"rustix 1.1.2",
"scoped-tls",
"smallvec",
"wayland-sys",
@ -1913,12 +1828,12 @@ dependencies = [
[[package]]
name = "wayland-client"
version = "0.31.12"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec"
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
dependencies = [
"bitflags 2.10.0",
"rustix 1.1.3",
"rustix 1.1.2",
"wayland-backend",
"wayland-scanner",
]
@ -1936,20 +1851,20 @@ dependencies = [
[[package]]
name = "wayland-cursor"
version = "0.31.12"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078"
checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29"
dependencies = [
"rustix 1.1.3",
"rustix 1.1.2",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-protocols"
version = "0.32.10"
version = "0.32.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3"
checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901"
dependencies = [
"bitflags 2.10.0",
"wayland-backend",
@ -1959,9 +1874,9 @@ dependencies = [
[[package]]
name = "wayland-protocols-plasma"
version = "0.3.10"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b"
checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032"
dependencies = [
"bitflags 2.10.0",
"wayland-backend",
@ -1972,9 +1887,9 @@ dependencies = [
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.10"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3"
checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec"
dependencies = [
"bitflags 2.10.0",
"wayland-backend",
@ -1985,9 +1900,9 @@ dependencies = [
[[package]]
name = "wayland-scanner"
version = "0.31.8"
version = "0.31.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3"
checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3"
dependencies = [
"proc-macro2",
"quick-xml",
@ -1996,9 +1911,9 @@ dependencies = [
[[package]]
name = "wayland-sys"
version = "0.31.8"
version = "0.31.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd"
checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142"
dependencies = [
"dlib",
"log",
@ -2008,9 +1923,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.85"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -2037,7 +1952,7 @@ dependencies = [
"cfg-if",
"cfg_aliases",
"document-features",
"hashbrown 0.16.1",
"hashbrown 0.16.0",
"js-sys",
"log",
"naga",
@ -2068,7 +1983,7 @@ dependencies = [
"bytemuck",
"cfg_aliases",
"document-features",
"hashbrown 0.16.1",
"hashbrown 0.16.0",
"indexmap",
"log",
"naga",
@ -2079,7 +1994,7 @@ dependencies = [
"raw-window-handle",
"rustc-hash",
"smallvec",
"thiserror 2.0.18",
"thiserror 2.0.17",
"wgpu-core-deps-apple",
"wgpu-core-deps-emscripten",
"wgpu-core-deps-windows-linux-android",
@ -2135,7 +2050,7 @@ dependencies = [
"gpu-alloc",
"gpu-allocator",
"gpu-descriptor",
"hashbrown 0.16.1",
"hashbrown 0.16.0",
"js-sys",
"khronos-egl",
"libc",
@ -2155,7 +2070,7 @@ dependencies = [
"raw-window-handle",
"renderdoc-sys",
"smallvec",
"thiserror 2.0.18",
"thiserror 2.0.17",
"wasm-bindgen",
"web-sys",
"wgpu-types",
@ -2173,7 +2088,7 @@ dependencies = [
"bytemuck",
"js-sys",
"log",
"thiserror 2.0.18",
"thiserror 2.0.17",
"web-sys",
]
@ -2217,7 +2132,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -2228,7 +2143,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]
[[package]]
@ -2467,18 +2382,18 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.7.14"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "x11-dl"
@ -2502,7 +2417,7 @@ dependencies = [
"libc",
"libloading",
"once_cell",
"rustix 1.1.3",
"rustix 1.1.2",
"x11rb-protocol",
]
@ -2545,20 +2460,20 @@ checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "zerocopy"
version = "0.8.33"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.33"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn",
]

View File

@ -1,24 +1,22 @@
[workspace]
members = ["ui"]
[package]
name = "photon-light"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["lib", "staticlib"]
[profile.dev]
panic = 'abort'
[profile.dev.package."*"]
debug = false
opt-level = 3
[profile.test.package."*"]
debug = false
opt-level = 3
[dependencies]
bytemuck = "1.25.2"
bytemuck = "1.24.0"
glam = { version = "0.30.9", features = ["bytemuck", "approx"] }
pollster = "0.4.0"
rand = "0.9.2"
@ -26,8 +24,6 @@ rand_distr = "0.5.1"
rand_pcg = "0.9.0"
wgpu = "27.0.1"
winit = "0.30.12"
diplomat = "0.17.0"
diplomat-runtime = "0.16.1"
[dev-dependencies]
approx = "0.5.1"

View File

@ -1 +0,0 @@
# Photon Light

View File

@ -1,8 +0,0 @@
set(crates "${CMAKE_BINARY_DIR}/crates")
set(diplomat "${crates}/bin/diplomat-tool")
if(NOT IS_EXECUTABLE "${diplomat}")
message(CONFIGURE_LOG "Downloading Diplomat...")
file(MAKE_DIRECTORY "${crates}")
execute_process(COMMAND ${CARGO} install --root "${crates}" diplomat-tool@0.17.0)
endif()

View File

@ -1,11 +0,0 @@
#!/bin/sh
find src/ ui/ \
\( \
-name '*.cxx' -o \
-name '*.hxx' -o \
-name '*.c' -o \
-name '*.h' \
\) \
-print \
-exec 'clang-format' '-i' '{}' \;

View File

@ -1,63 +0,0 @@
# build the Rust side as a static library
set(impl_basename "${CARGO_TARGET_DIR}/release/libphoton_light")
set(cargo_cmake_args)
set(diplomat_cmake_args)
if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.27")
list(APPEND cargo_cmake_args DEPENDS_EXPLICIT_ONLY)
list(APPEND diplomat_cmake_args DEPENDS_EXPLICIT_ONLY)
endif()
if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.28")
list(APPEND cargo_cmake_args JOB_SERVER_AWARE TRUE)
endif()
if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.31")
list(APPEND diplomat_cmake_args CODEGEN)
endif()
add_custom_command(
OUTPUT "${impl_basename}.a"
COMMAND env "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}" ${CARGO} build --release --package photon-light
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
DEPFILE "${impl_basename}.d"
USES_TERMINAL
VERBATIM
${cargo_cmake_args}
)
# HACK ensure CMake *actually adds* the command above
add_custom_target(build_impl
DEPENDS "${impl_basename}.a"
)
add_library(photon_light_impl STATIC IMPORTED GLOBAL)
set_target_properties(photon_light_impl PROPERTIES
IMPORTED_LOCATION "${impl_basename}.a"
)
# generate the bindings
set(bindings_dir "${CMAKE_CURRENT_BINARY_DIR}/include")
file(MAKE_DIRECTORY "${bindings_dir}")
set(bindings
"${bindings_dir}/Core.hpp"
)
add_custom_command(
OUTPUT ${bindings}
COMMAND ${diplomat} cpp "${bindings_dir}"
MAIN_DEPENDENCY "lib.rs"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
VERBATIM
${diplomat_cmake_args}
)
# HACK ensure CMake *actually adds* the command above
add_custom_target(codegen_impl
DEPENDS ${bindings}
)
target_sources(photon_light_impl INTERFACE ${bindings})
target_include_directories(photon_light_impl INTERFACE "${bindings_dir}")

View File

@ -20,12 +20,6 @@ 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
@ -37,8 +31,7 @@ impl OrbitalCamera {
[-1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 0., 1.],
])
* Mat4::from_euler(
]) * Mat4::from_euler(
glam::EulerRot::ZYZ,
0.,
self.position_pitch,

View File

@ -1,22 +0,0 @@
use std::convert::identity;
pub(crate) fn loop_list<T: Clone>(iter: impl IntoIterator<Item = T>) -> impl Iterator<Item = T> {
loop_list_ex(iter, identity, identity)
}
pub(crate) fn loop_list_ex<T: Clone, U>(
iter: impl IntoIterator<Item = T>,
mut fa: impl FnMut(T) -> U,
mut fb: impl FnMut(T) -> U,
) -> impl Iterator<Item = U> {
gen move {
let mut iter = iter.into_iter();
let Some(first) = iter.next() else { return };
yield fa(first.clone());
for item in iter {
yield fb(item.clone());
yield fa(item);
}
yield fb(first);
}
}

View File

@ -1,61 +1,42 @@
#![feature(gen_blocks)]
use std::{error::Error, f32::consts::PI};
use std::{convert::identity, error::Error, f32::consts::PI};
use glam::{Mat4, UVec2, Vec2, Vec3, vec3};
use rand_distr::Distribution as _;
use wgpu::rwh::{RawDisplayHandle, RawWindowHandle};
use crate::{
camera::OrbitalCamera,
iter::loop_list,
ray::Ray,
render::{
DEPTH_FORMAT, OUTPUT_FORMAT,
lines::{LookParams, Mesh, Pipeline, Vertex},
},
render::lines::{LookParams, Mesh, Pipeline, Vertex},
trace::{Hit, Lambertian, Reflector, Scene, Source, Sphere},
};
pub use crate::ffi::{RedrawArgs, SphericalPosition, UseNormal};
mod camera;
mod iter;
mod ray;
mod render;
mod shape;
mod trace;
#[diplomat::bridge]
mod ffi {
use std::{f32::consts::PI, num::NonZero, ptr::NonNull};
const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
use glam::uvec2;
use wgpu::rwh::{XcbDisplayHandle, XcbWindowHandle};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SphericalPosition {
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct SphericalPosition {
pub yaw: f32,
pub pitch: f32,
pub distance: f32,
}
}
#[derive(Debug)]
pub enum UseNormal {
Light,
Camera,
}
#[derive(Debug, Clone, Copy)]
pub struct RedrawArgs {
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct RedrawArgs {
pub camera_position: SphericalPosition,
pub light_position: SphericalPosition,
pub light_radius: f32,
pub light_spread: f32,
pub accum_sigma: f32,
pub accum_scale: f32,
pub reflections: u8,
pub use_normal: UseNormal,
pub reflections: u32,
pub show_axes: bool,
pub show_shapes: bool,
pub show_hit_emission: bool,
@ -63,59 +44,6 @@ mod ffi {
pub show_direct_hit: bool,
pub show_indirect_hit: bool,
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,
}
}
}
#[diplomat::opaque_mut]
pub struct Core(crate::Core);
impl Core {
pub fn from_xcb(connection: usize, window: u32, width: u32, height: u32) -> Box<Core> {
let display =
XcbDisplayHandle::new(Some(NonNull::new(connection as *mut _).unwrap()), 0);
let window = XcbWindowHandle::new(NonZero::new(window).unwrap());
let gpu = unsafe { crate::Gpu::new_raw(display, window) };
let size = uvec2(width, height);
Box::new(Self(crate::Core::new(gpu, size)))
}
pub fn configure(&mut self, width: u32, height: u32) {
self.0.configure(uvec2(width, height));
}
pub fn redraw(&mut self, args: &RedrawArgs) {
self.0.redraw(args);
}
}
}
pub struct Gpu {
@ -124,32 +52,12 @@ pub struct Gpu {
surface: wgpu::Surface<'static>,
}
impl Gpu {
unsafe fn new_raw(
display: impl Into<RawDisplayHandle>,
window: impl Into<RawWindowHandle>,
) -> Self {
let target = wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: display.into(),
raw_window_handle: window.into(),
};
let gpu = pollster::block_on(init_gpu_inner(|instance| unsafe {
instance.create_surface_unsafe(target)
}))
.unwrap();
gpu
}
}
pub struct Core {
device: wgpu::Device,
queue: wgpu::Queue,
surface: wgpu::Surface<'static>,
depth: wgpu::Texture,
pipeline: Pipeline,
mesh_pipe: render::faces::Pipeline,
tripod: Mesh,
}
@ -167,6 +75,27 @@ pub fn new_tripod(device: &wgpu::Device) -> Mesh {
)
}
fn loop_list<T: Clone>(iter: impl IntoIterator<Item = T>) -> impl Iterator<Item = T> {
loop_list_ex(iter, identity, identity)
}
fn loop_list_ex<T: Clone, U>(
iter: impl IntoIterator<Item = T>,
mut fa: impl FnMut(T) -> U,
mut fb: impl FnMut(T) -> U,
) -> impl Iterator<Item = U> {
gen move {
let mut iter = iter.into_iter();
let Some(first) = iter.next() else { return };
yield fa(first.clone());
for item in iter {
yield fb(item.clone());
yield fa(item);
}
yield fb(first);
}
}
impl OrbitalCamera {
fn make_ray(&self, rng: &mut impl rand::Rng) -> Ray {
let off: f32 = rand_distr::StandardUniform.sample(rng);
@ -184,29 +113,23 @@ impl OrbitalCamera {
}
impl Core {
pub fn new(gpu: Gpu, pixel_size: UVec2) -> Self {
pub fn new(gpu: Gpu) -> Self {
let Gpu {
device,
queue,
surface,
} = gpu;
let pipeline = Pipeline::new(&device);
let mesh_pipe = render::faces::Pipeline::new(&device);
let pipeline = Pipeline::new(&device, OUTPUT_FORMAT);
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,
}
}
@ -229,18 +152,9 @@ 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());
@ -254,14 +168,7 @@ impl Core {
store: wgpu::StoreOp::Store,
},
})],
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,
}),
depth_stencil_attachment: None,
..Default::default()
});
if args.show_axes {
@ -384,47 +291,34 @@ impl Core {
hits1 = hits2;
}
}
let light_at = {
let sigma2 = args.accum_sigma.powi(2);
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 {
let d2 = hit.incident.base.distance_squared(light_hit.incident.base);
if d2 > 9. * sigma2 {
continue;
}
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 * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir);
let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp();
total_cd += weight * out_cd;
}
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<Vertex> = Vec::with_capacity(camera_rays.len());
if args.show_light {
let r = args.accum_sigma;
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 light = light_at(hit);
let color = colormap(light);
let mut total_cd = 0.0f32;
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());
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 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.));
let vertex = |off: Vec3| Vertex {
pos: hit.incident.base + r * off,
color,
@ -439,36 +333,6 @@ 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::<Vec<_>>(),
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)]);
@ -482,26 +346,10 @@ impl Core {
self.queue.submit(std::iter::once(encoder.finish()));
}
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,
/// Configures the renderer for a given target size.
pub fn configure(&mut self, pixel_size: UVec2) {
self.surface.configure(
&self.device,
&wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
format: OUTPUT_FORMAT,
@ -515,12 +363,6 @@ 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.

View File

@ -1,6 +1,7 @@
use std::sync::Arc;
use std::{f32::consts::PI, sync::Arc};
use glam::uvec2;
use photon_light::{Core, RedrawArgs, SphericalPosition, init_gpu_inner};
use winit::{
application::ApplicationHandler,
event::WindowEvent,
@ -8,9 +9,7 @@ use winit::{
window::Window,
};
use photon_light::{Core, RedrawArgs, init_gpu_inner};
const TITLE: &str = "Photon Light";
const TITLE: &str = "WGPU example";
struct MainWindow {
window: Arc<Window>,
@ -27,7 +26,8 @@ impl MainWindow {
instance.create_surface(Arc::clone(&window))
}))
.unwrap();
let core = Core::new(gpu, uvec2(1, 1));
let mut core = Core::new(gpu);
core.configure(uvec2(1, 1));
Self { window, core }
}
@ -37,7 +37,30 @@ impl MainWindow {
WindowEvent::Resized(physical_size) => self
.core
.configure(uvec2(physical_size.width, physical_size.height)),
WindowEvent::RedrawRequested => self.core.redraw(&RedrawArgs::default()),
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,
}),
_ => {}
}
}

View File

@ -1,32 +0,0 @@
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<uniform> 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);
}

View File

@ -1,169 +0,0 @@
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::<LookParams>() 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::<Vertex>() 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<Item = &'a Mesh>,
) {
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);
}
}
}

View File

@ -4,8 +4,6 @@ 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 {
@ -51,7 +49,7 @@ pub struct Pipeline {
}
impl Pipeline {
pub fn new(device: &wgpu::Device) -> Self {
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
let look_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: size_of::<LookParams>() as u64,
@ -59,9 +57,10 @@ 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(super::SIMPLE_SHADER.into()),
source: wgpu::ShaderSource::Wgsl(shader.into()),
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
@ -91,17 +90,7 @@ impl Pipeline {
topology: wgpu::PrimitiveTopology::LineList,
..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 {
constant: -2,
slope_scale: 0.,
clamp: 0.,
},
}),
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
@ -112,7 +101,7 @@ impl Pipeline {
entry_point: None,
compilation_options: wgpu::PipelineCompilationOptions::default(),
targets: &[Some(wgpu::ColorTargetState {
format: OUTPUT_FORMAT,
format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],

View File

@ -1,8 +1 @@
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;

View File

@ -1,348 +0,0 @@
use std::f32::consts::PI;
use glam::{Vec3, vec3};
pub type Face = [u16; 3];
#[derive(Debug, Clone)]
pub struct Mesh {
pub vertices: Vec<Vec3>,
pub indices: Vec<Face>,
}
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<Item = Face> {
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<Item = Face> {
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<Item = Face> {
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],
]
);
}
}

View File

@ -1,10 +1,13 @@
qt_add_executable(photon-light
include(impl.cmake)
qt_add_executable(photon_light
src/api.cxx
src/main.cxx
src/main_window.cxx
src/main_window.ui
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)

15
ui/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "photon-light-impl"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["staticlib"]
[dependencies]
photon-light = {path = "../"}
glam = { version = "0.30" }
pollster = "0.4.0"
raw-window-handle = "0.6.2"
wgpu = "27.0.1"

22
ui/impl.cmake Normal file
View File

@ -0,0 +1,22 @@
set(impl_basename "${CARGO_TARGET_DIR}/release/libphoton_light_impl")
add_custom_command(
OUTPUT ${impl_basename}.a
COMMAND env CARGO_TARGET_DIR=${CARGO_TARGET_DIR} ${CARGO} build --release --package photon-light-impl
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPFILE ${impl_basename}.d
USES_TERMINAL
JOB_SERVER_AWARE
DEPENDS_EXPLICIT_ONLY
)
# HACK ensure CMake *actually adds* the command above
add_custom_target(build_impl
DEPENDS ${impl_basename}.a
)
add_library(photon_light_impl STATIC IMPORTED)
set_target_properties(photon_light_impl PROPERTIES
IMPORTED_LOCATION ${impl_basename}.a
)

57
ui/src/api.cxx Normal file
View File

@ -0,0 +1,57 @@
#include "api.hxx"
#include <stdexcept>
#include <utility>
namespace ffi {
extern "C" Core* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window);
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);
} // namespace ffi
BoxCore::BoxCore(BoxCore&& b)
: ptr(std::exchange(b.ptr, {})) {
}
BoxCore::~BoxCore() {
reset();
}
BoxCore& BoxCore::operator=(BoxCore&& b) {
if (&b == this)
return *this;
std::swap(ptr, b.ptr);
b.reset();
return *this;
}
BoxCore BoxCore::from_xcb(xcb_connection_t* connection, std::uint32_t window) {
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);
return out;
}
void BoxCore::reset() {
auto viewport = std::exchange(ptr, {});
if (viewport)
ffi::rt4_viewport_destroy(viewport.use());
}
ffi::Core* MutCore::use() const {
if (!ptr)
throw std::logic_error("attempt to use a null Core");
return ptr;
}
void MutCore::configure(std::uint32_t width, std::uint32_t height) const {
rt4_viewport_configure(use(), width, height);
}
void MutCore::redraw(const RedrawArgs& args) const {
rt4_viewport_redraw(use(), &args);
}

69
ui/src/api.hxx Normal file
View File

@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
struct xcb_connection_t;
namespace ffi {
struct Core;
struct SphericalPosition {
float yaw = 0.;
float pitch = 0.;
float distance = 1.;
};
struct RedrawArgs {
SphericalPosition camera_position;
SphericalPosition light_position;
float light_radius = 1.;
float light_spread = 0.;
float accum_sigma = 1.;
float accum_scale = 1.;
std::uint32_t reflections = 0;
bool show_axes = true;
bool show_shapes = true;
bool show_hit_emission = true;
bool show_miss_emission = true;
bool show_direct_hit = true;
bool show_indirect_hit = true;
bool show_light = true;
};
} // namespace ffi
using ffi::RedrawArgs;
using ffi::SphericalPosition;
class MutCore {
friend class BoxCore;
public:
explicit operator bool() const { return ptr; }
void configure(std::uint32_t width, std::uint32_t height) const;
void redraw(const RedrawArgs& args) const;
private:
ffi::Core* ptr = nullptr;
ffi::Core* use() const;
};
class BoxCore {
public:
BoxCore() = default;
BoxCore(const BoxCore&) = delete;
BoxCore(BoxCore&&);
BoxCore& operator=(const BoxCore&) = delete;
BoxCore& operator=(BoxCore&&);
~BoxCore();
explicit operator bool() const { return !!ptr; }
const MutCore* operator->() const { return &ptr; }
void reset();
static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window);
private:
MutCore ptr;
};

45
ui/src/lib.rs Normal file
View File

@ -0,0 +1,45 @@
use std::{ffi::c_void, num::NonZero, ptr::NonNull};
use glam::uvec2;
use photon_light::{Core, RedrawArgs, init_gpu_inner};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
unsafe fn create_viewport(
display: impl Into<RawDisplayHandle>,
window: impl Into<RawWindowHandle>,
) -> Box<Core> {
let target = wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: display.into(),
raw_window_handle: window.into(),
};
let gpu = pollster::block_on(init_gpu_inner(|instance| unsafe {
instance.create_surface_unsafe(target)
}))
.unwrap();
Box::new(Core::new(gpu))
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rt4_viewport_create(
connection: NonNull<c_void>,
window: NonZero<u32>,
) -> Box<Core> {
let display = XcbDisplayHandle::new(Some(connection), 0);
let window = XcbWindowHandle::new(window);
unsafe { create_viewport(display, window) }
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rt4_viewport_destroy(viewport: Box<Core>) {
drop(viewport);
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rt4_viewport_configure(viewport: &mut Core, width: u32, height: u32) {
viewport.configure(uvec2(width, height));
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rt4_viewport_redraw(viewport: &mut Core, args: &RedrawArgs) {
viewport.redraw(args);
}

View File

@ -3,9 +3,6 @@
#include <QApplication>
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;

View File

@ -16,11 +16,6 @@ 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()),
@ -36,8 +31,7 @@ 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::uint8_t(m_ui->reflections->value()),
.use_normal = use_normal,
.reflections = std::uint32_t(m_ui->reflections->value()),
.show_axes = m_ui->displayAxes->isChecked(),
.show_shapes = m_ui->displayShapes->isChecked(),
.show_hit_emission = m_ui->displayEmitted->isChecked(),
@ -52,14 +46,9 @@ 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', 5)));
m_ui->accumScaleLabel->setText(tr("Brightness: %1").arg(QString::number(args.accum_scale, 'f', 5)));
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->viewport->setView(args);
}
void PhotonLight::updateViewIf(bool update) {
if (update)
updateView();
}
#include "moc_main_window.cpp"

View File

@ -16,7 +16,6 @@ public:
public slots:
void updateView();
void updateViewIf(bool update); // for radio buttons
private:
const std::unique_ptr<Ui::MainWindow> m_ui;

View File

@ -245,7 +245,7 @@
<number>0</number>
</property>
<property name="value">
<number>-45</number>
<number>-40</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -271,7 +271,7 @@
<number>0</number>
</property>
<property name="value">
<number>-85</number>
<number>-50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -298,30 +298,6 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Use normal at</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="normalFromLight">
<property name="text">
<string>Hit position</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="normalFromCamera">
<property name="text">
<string>Show position</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -337,7 +313,7 @@
<string>Axes</string>
</property>
<property name="checked">
<bool>false</bool>
<bool>true</bool>
</property>
</widget>
</item>
@ -387,7 +363,7 @@
<string>Average light</string>
</property>
<property name="checked">
<bool>false</bool>
<bool>true</bool>
</property>
</widget>
</item>
@ -661,41 +637,8 @@
</hint>
</hints>
</connection>
<connection>
<sender>normalFromLight</sender>
<signal>toggled(bool)</signal>
<receiver>MainWindow</receiver>
<slot>updateViewIf(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>1429</x>
<y>787</y>
</hint>
<hint type="destinationlabel">
<x>1381</x>
<y>783</y>
</hint>
</hints>
</connection>
<connection>
<sender>normalFromCamera</sender>
<signal>toggled(bool)</signal>
<receiver>MainWindow</receiver>
<slot>updateViewIf(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>1932</x>
<y>816</y>
</hint>
<hint type="destinationlabel">
<x>1600</x>
<y>874</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>updateView()</slot>
<slot>updateViewIf(bool)</slot>
</slots>
</ui>

View File

@ -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 = Core::from_xcb((std::uintptr_t)xcb_connection, x11_window, device_size.width(), device_size.height());
core = BoxCore::from_xcb(xcb_connection, x11_window);
updateSize();
} catch (const std::exception& e) {
fprintf(stderr, "failed to recreate the viewport: %s", e.what());
}

View File

@ -2,7 +2,7 @@
#include <QWidget>
#include "Core.hpp"
#include "api.hxx"
class Viewport : public QWidget {
Q_OBJECT
@ -20,7 +20,7 @@ protected:
void resizeEvent(QResizeEvent* event) override;
private:
std::unique_ptr<Core> core;
BoxCore core;
RedrawArgs args = {};
void recreate();