diff --git a/Cargo.lock b/Cargo.lock index fe29612..afc6994 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,10 +26,19 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.57" +name = "aho-corasick" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" [[package]] name = "approx" @@ -55,12 +64,135 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bit-set" version = "0.5.2" @@ -88,6 +220,20 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -120,6 +266,18 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "calloop" version = "0.9.3" @@ -211,6 +369,35 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" +dependencies = [ + "log", + "web-sys", +] + [[package]] name = "copyless" version = "0.1.5" @@ -308,6 +495,61 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "ctor" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "cty" version = "0.2.2" @@ -390,6 +632,49 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "event-listener" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + [[package]] name = "fnv" version = "1.0.7" @@ -411,6 +696,82 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "fxhash" version = "0.2.1" @@ -431,6 +792,24 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "gloo-timers" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "glow" version = "0.11.2" @@ -470,7 +849,7 @@ checksum = "a538f217be4d405ff4719a283ca68323cc2384003eca5baaa87501e821c81dda" dependencies = [ "bitflags", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.11.2", ] [[package]] @@ -482,6 +861,25 @@ dependencies = [ "bitflags", ] +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -491,18 +889,121 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hexf-parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.24.2" @@ -521,12 +1022,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" +checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.1", ] [[package]] @@ -547,6 +1048,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + [[package]] name = "itoa" version = "1.0.2" @@ -584,6 +1091,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -623,6 +1139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", + "value-bag", ] [[package]] @@ -634,6 +1151,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.5.0" @@ -672,6 +1195,12 @@ dependencies = [ "objc", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -717,6 +1246,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.5.0" @@ -834,6 +1381,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.7" @@ -855,15 +1412,6 @@ dependencies = [ "syn", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "objc" version = "0.2.7" @@ -889,6 +1437,57 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.11.2" @@ -920,6 +1519,18 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[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.25" @@ -938,6 +1549,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + [[package]] name = "pollster" version = "0.2.5" @@ -971,9 +1595,9 @@ checksum = "2f61dcf0b917cd75d4521d7343d1ffff3d1583054133c9b5cbea3375c703c40d" [[package]] name = "quote" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "f53dc8cf16a769a6f677e09e7ff2cd4be1ea0f48754aac39520536962011de0d" dependencies = [ "proc-macro2", ] @@ -993,6 +1617,30 @@ dependencies = [ "cty", ] +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.13" @@ -1002,18 +1650,97 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "renderdoc-sys" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -1026,6 +1753,29 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "security-framework" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +dependencies = [ + "bitflags", + "core-foundation 0.9.3", + "core-foundation-sys 0.8.3", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + [[package]] name = "serde" version = "1.0.137" @@ -1033,16 +1783,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" [[package]] -name = "simplelog" -version = "0.12.0" +name = "serde_json" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "log", - "termcolor", - "time", + "itoa", + "ryu", + "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + [[package]] name = "slotmap" version = "1.0.6" @@ -1077,6 +1845,16 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -1095,9 +1873,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", @@ -1105,10 +1883,24 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.1.2" +name = "tempfile" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -1134,22 +1926,68 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.9" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "itoa", - "libc", - "num_threads", - "time-macros", + "tinyvec_macros", ] [[package]] -name = "time-macros" -version = "0.2.4" +name = "tinyvec_macros" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tobj" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33477e8d090573560f104bb924ce29e92442a064404b21be6ea59a05c95e0e66" +dependencies = [ + "ahash", +] + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "once_cell", + "pin-project-lite", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] [[package]] name = "toml" @@ -1160,39 +1998,143 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "tuto1" version = "0.1.0" dependencies = [ "anyhow", + "async-std", "bytemuck", + "cfg-if 1.0.0", "cgmath", + "console_error_panic_hook", + "console_log", + "env_logger", + "fs_extra", + "glob", "image", + "instant", "log", "pollster", - "simplelog", + "rayon", + "reqwest", + "tobj", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "wgpu", "winit", ] +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + [[package]] name = "unicode-ident" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1348,6 +2290,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "wgpu" version = "0.12.0" @@ -1546,6 +2497,15 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "x11-dl" version = "2.19.1" diff --git a/Cargo.toml b/Cargo.toml index 738acee..468b152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,47 @@ -[package] -name = "tuto1" -version = "0.1.0" -authors = ["Florian RICHER "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -log = "0.4.17" -image = { version = "0.24.2", features = [ "png", "jpeg" ], default-features = false } -winit = "0.26.1" -cgmath = "0.18" -wgpu = "0.12.0" -bytemuck = { version = "1.9.1", features = [ "derive" ] } -anyhow = "1.0" -simplelog = "0.12.0" -pollster = "0.2" - -# [target.'cfg(target_arch = "wasm32")'.dependencies] -# console_error_panic_hook = "0.1.6" -# console_log = "0.2.0" -# wgpu = { version = "0.12", features = ["webgl"]} -# wasm-bindgen = "0.2" -# wasm-bindgen-futures = "0.4" -# web-sys = { version = "0.3", features = [ -# "Document", -# "Window", -# "Element", -# ]} \ No newline at end of file +[package] +name = "tuto1" +version = "0.1.0" +authors = ["Florian RICHER "] +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cfg-if = "1" +anyhow = "1.0" +bytemuck = { version = "1.4", features = [ "derive" ] } +cgmath = "0.18" +env_logger = "0.9" +pollster = "0.2" +log = "0.4" +rayon = "1.4" +tobj = { version = "3.2", features = ["async"]} +wgpu = { version = "0.12"} +winit = "0.26" +instant = "0.1" +async-std = "1" + +[dependencies.image] +version = "0.24" +default-features = false +features = ["png", "jpeg"] + +[target.'cfg(target_arch = "wasm32")'.dependencies] +reqwest = { version = "0.11" } +console_error_panic_hook = "0.1" +console_log = "0.2" +wgpu = { version = "0.12", features = ["webgl"]} +wasm-bindgen = "0.2" +wasm-bindgen-futures = "0.4" +web-sys = { version = "0.3", features = [ + "Document", + "Window", + "Element", + "Location", +]} + +[build-dependencies] +anyhow = "1.0" +fs_extra = "1.2" +glob = "0.3" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..992e882 --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +use anyhow::*; +use fs_extra::copy_items; +use fs_extra::dir::CopyOptions; +use std::env; + +fn main() -> Result<()> { + // This tells cargo to rerun this script if something in /res/ changes. + println!("cargo:rerun-if-changed=res/*"); + + let out_dir = env::var("OUT_DIR")?; + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; + let mut paths_to_copy = Vec::new(); + paths_to_copy.push("res/"); + copy_items(&paths_to_copy, out_dir, ©_options)?; + + Ok(()) +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..230c63b --- /dev/null +++ b/index.html @@ -0,0 +1,29 @@ + + + + + + + + Tutorial 13 Threading + + + +
+ + + + + \ No newline at end of file diff --git a/res/cobble-diffuse.png b/res/cobble-diffuse.png new file mode 100644 index 0000000..93eda7e Binary files /dev/null and b/res/cobble-diffuse.png differ diff --git a/res/cobble-normal.png b/res/cobble-normal.png new file mode 100644 index 0000000..61de39c Binary files /dev/null and b/res/cobble-normal.png differ diff --git a/res/cube-diffuse.jpg b/res/cube-diffuse.jpg new file mode 100644 index 0000000..c89b31d Binary files /dev/null and b/res/cube-diffuse.jpg differ diff --git a/res/cube-normal.png b/res/cube-normal.png new file mode 100644 index 0000000..5bbac0a Binary files /dev/null and b/res/cube-normal.png differ diff --git a/res/cube.mtl b/res/cube.mtl new file mode 100644 index 0000000..6be388c --- /dev/null +++ b/res/cube.mtl @@ -0,0 +1,14 @@ +# Blender MTL File: 'cube.blend' +# Material Count: 1 + +newmtl Material.001 +Ns 323.999994 +Ka 1.000000 1.000000 1.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +map_Bump cube-normal.png +map_Kd cube-diffuse.jpg diff --git a/res/cube.obj b/res/cube.obj new file mode 100644 index 0000000..03b3bca --- /dev/null +++ b/res/cube.obj @@ -0,0 +1,1143 @@ +# Blender v2.82 (sub 7) OBJ File: 'cube.blend' +# www.blender.org +mtllib cube.mtl +o Cube_Finished_Cube.001 +v 0.900000 0.900000 -1.000000 +v 0.900000 1.000000 -0.900000 +v 1.000000 0.900000 -0.900000 +v 0.900000 0.930907 -0.995104 +v 0.900000 0.958769 -0.980909 +v 0.930907 0.900000 -0.995104 +v 0.931727 0.931906 -0.989305 +v 0.930693 0.957414 -0.975905 +v 0.958769 0.900000 -0.980909 +v 0.957466 0.930772 -0.975834 +v 0.952912 0.952912 -0.966338 +v 0.930907 0.995104 -0.900000 +v 0.958769 0.980909 -0.900000 +v 0.900000 0.995104 -0.930907 +v 0.931906 0.989305 -0.931727 +v 0.957414 0.975905 -0.930693 +v 0.900000 0.980909 -0.958769 +v 0.930772 0.975834 -0.957466 +v 0.952912 0.966338 -0.952912 +v 0.995104 0.900000 -0.930907 +v 0.980909 0.900000 -0.958769 +v 0.995104 0.930907 -0.900000 +v 0.989305 0.931727 -0.931906 +v 0.975905 0.930693 -0.957414 +v 0.980909 0.958769 -0.900000 +v 0.975834 0.957466 -0.930772 +v 0.966338 0.952912 -0.952912 +v 0.900000 -1.000000 -0.900000 +v 0.900000 -0.900000 -1.000000 +v 1.000000 -0.900000 -0.900000 +v 0.900000 -0.995104 -0.930907 +v 0.900000 -0.980909 -0.958769 +v 0.930907 -0.995104 -0.900000 +v 0.931727 -0.989305 -0.931906 +v 0.930693 -0.975905 -0.957414 +v 0.958769 -0.980909 -0.900000 +v 0.957466 -0.975834 -0.930772 +v 0.952912 -0.966338 -0.952912 +v 0.930907 -0.900000 -0.995104 +v 0.958769 -0.900000 -0.980909 +v 0.900000 -0.930907 -0.995104 +v 0.931906 -0.931727 -0.989305 +v 0.957414 -0.930693 -0.975905 +v 0.900000 -0.958769 -0.980909 +v 0.930772 -0.957466 -0.975834 +v 0.952912 -0.952912 -0.966338 +v 0.995104 -0.930907 -0.900000 +v 0.980909 -0.958769 -0.900000 +v 0.995104 -0.900000 -0.930907 +v 0.989305 -0.931906 -0.931727 +v 0.975905 -0.957414 -0.930693 +v 0.980909 -0.900000 -0.958769 +v 0.975834 -0.930772 -0.957466 +v 0.966338 -0.952912 -0.952912 +v 1.000000 0.900000 0.900000 +v 0.900000 1.000000 0.900000 +v 0.900000 0.900000 1.000000 +v 0.995104 0.930907 0.900000 +v 0.980909 0.958769 0.900000 +v 0.995104 0.900000 0.930907 +v 0.989305 0.931906 0.931727 +v 0.975905 0.957414 0.930693 +v 0.980909 0.900000 0.958769 +v 0.975834 0.930772 0.957466 +v 0.966338 0.952912 0.952912 +v 0.900000 0.995104 0.930907 +v 0.900000 0.980909 0.958769 +v 0.930907 0.995104 0.900000 +v 0.931727 0.989305 0.931906 +v 0.930693 0.975905 0.957414 +v 0.958769 0.980909 0.900000 +v 0.957466 0.975834 0.930772 +v 0.952912 0.966338 0.952912 +v 0.930907 0.900000 0.995104 +v 0.958769 0.900000 0.980909 +v 0.900000 0.930907 0.995104 +v 0.931906 0.931727 0.989305 +v 0.957414 0.930693 0.975905 +v 0.900000 0.958769 0.980909 +v 0.930772 0.957466 0.975834 +v 0.952912 0.952912 0.966338 +v 1.000000 -0.900000 0.900000 +v 0.900000 -0.900000 1.000000 +v 0.900000 -1.000000 0.900000 +v 0.995104 -0.900000 0.930907 +v 0.980909 -0.900000 0.958769 +v 0.995104 -0.930907 0.900000 +v 0.989305 -0.931727 0.931906 +v 0.975905 -0.930693 0.957414 +v 0.980909 -0.958769 0.900000 +v 0.975834 -0.957466 0.930772 +v 0.966338 -0.952912 0.952912 +v 0.900000 -0.930907 0.995104 +v 0.900000 -0.958769 0.980909 +v 0.930907 -0.900000 0.995104 +v 0.931727 -0.931906 0.989305 +v 0.930693 -0.957414 0.975905 +v 0.958769 -0.900000 0.980909 +v 0.957466 -0.930772 0.975834 +v 0.952912 -0.952912 0.966338 +v 0.930907 -0.995104 0.900000 +v 0.958769 -0.980909 0.900000 +v 0.900000 -0.995104 0.930907 +v 0.931906 -0.989305 0.931727 +v 0.957414 -0.975905 0.930693 +v 0.900000 -0.980909 0.958769 +v 0.930772 -0.975834 0.957466 +v 0.952912 -0.966338 0.952912 +v -0.900000 0.900000 -1.000000 +v -1.000000 0.900000 -0.900000 +v -0.900000 1.000000 -0.900000 +v -0.930907 0.900000 -0.995104 +v -0.958769 0.900000 -0.980909 +v -0.900000 0.930907 -0.995104 +v -0.931906 0.931727 -0.989305 +v -0.957414 0.930693 -0.975905 +v -0.900000 0.958769 -0.980909 +v -0.930772 0.957466 -0.975834 +v -0.952912 0.952912 -0.966338 +v -0.995104 0.930907 -0.900000 +v -0.980909 0.958769 -0.900000 +v -0.995104 0.900000 -0.930907 +v -0.989305 0.931906 -0.931727 +v -0.975905 0.957414 -0.930693 +v -0.980909 0.900000 -0.958769 +v -0.975834 0.930772 -0.957466 +v -0.966338 0.952912 -0.952912 +v -0.900000 0.995104 -0.930907 +v -0.900000 0.980909 -0.958769 +v -0.930907 0.995104 -0.900000 +v -0.931727 0.989305 -0.931906 +v -0.930693 0.975905 -0.957414 +v -0.958769 0.980909 -0.900000 +v -0.957466 0.975834 -0.930772 +v -0.952912 0.966338 -0.952912 +v -1.000000 -0.900000 -0.900000 +v -0.900000 -0.900000 -1.000000 +v -0.900000 -1.000000 -0.900000 +v -0.995104 -0.900000 -0.930907 +v -0.980909 -0.900000 -0.958769 +v -0.995104 -0.930907 -0.900000 +v -0.989305 -0.931727 -0.931906 +v -0.975905 -0.930693 -0.957414 +v -0.980909 -0.958769 -0.900000 +v -0.975834 -0.957466 -0.930772 +v -0.966338 -0.952912 -0.952912 +v -0.900000 -0.930907 -0.995104 +v -0.900000 -0.958769 -0.980909 +v -0.930907 -0.900000 -0.995104 +v -0.931727 -0.931906 -0.989305 +v -0.930693 -0.957414 -0.975905 +v -0.958769 -0.900000 -0.980909 +v -0.957466 -0.930772 -0.975834 +v -0.952912 -0.952912 -0.966338 +v -0.930907 -0.995104 -0.900000 +v -0.958769 -0.980909 -0.900000 +v -0.900000 -0.995104 -0.930907 +v -0.931906 -0.989305 -0.931727 +v -0.957414 -0.975905 -0.930693 +v -0.900000 -0.980909 -0.958769 +v -0.930772 -0.975834 -0.957466 +v -0.952912 -0.966338 -0.952912 +v -1.000000 0.900000 0.900000 +v -0.900000 0.900000 1.000000 +v -0.900000 1.000000 0.900000 +v -0.995104 0.900000 0.930907 +v -0.980909 0.900000 0.958769 +v -0.995104 0.930907 0.900000 +v -0.989305 0.931727 0.931906 +v -0.975905 0.930693 0.957414 +v -0.980909 0.958769 0.900000 +v -0.975834 0.957466 0.930772 +v -0.966338 0.952912 0.952912 +v -0.900000 0.930907 0.995104 +v -0.900000 0.958769 0.980909 +v -0.930907 0.900000 0.995104 +v -0.931727 0.931906 0.989305 +v -0.930693 0.957414 0.975905 +v -0.958769 0.900000 0.980909 +v -0.957466 0.930772 0.975834 +v -0.952912 0.952912 0.966338 +v -0.930907 0.995104 0.900000 +v -0.958769 0.980909 0.900000 +v -0.900000 0.995104 0.930907 +v -0.931906 0.989305 0.931727 +v -0.957414 0.975905 0.930693 +v -0.900000 0.980909 0.958769 +v -0.930772 0.975834 0.957466 +v -0.952912 0.966338 0.952912 +v -0.900000 -1.000000 0.900000 +v -0.900000 -0.900000 1.000000 +v -1.000000 -0.900000 0.900000 +v -0.900000 -0.995104 0.930907 +v -0.900000 -0.980909 0.958769 +v -0.930907 -0.995104 0.900000 +v -0.931727 -0.989305 0.931906 +v -0.930693 -0.975905 0.957414 +v -0.958769 -0.980909 0.900000 +v -0.957466 -0.975834 0.930772 +v -0.952912 -0.966338 0.952912 +v -0.930907 -0.900000 0.995104 +v -0.958769 -0.900000 0.980909 +v -0.900000 -0.930907 0.995104 +v -0.931906 -0.931727 0.989305 +v -0.957414 -0.930693 0.975905 +v -0.900000 -0.958769 0.980909 +v -0.930772 -0.957466 0.975834 +v -0.952912 -0.952912 0.966338 +v -0.995104 -0.930907 0.900000 +v -0.980909 -0.958769 0.900000 +v -0.995104 -0.900000 0.930907 +v -0.989305 -0.931906 0.931727 +v -0.975905 -0.957414 0.930693 +v -0.980909 -0.900000 0.958769 +v -0.975834 -0.930772 0.957466 +v -0.966338 -0.952912 0.952912 +vt 0.362500 0.512500 +vt 0.137500 0.737500 +vt 0.137500 0.512500 +vt 0.612500 0.012500 +vt 0.387500 0.237500 +vt 0.387500 0.012500 +vt 0.612500 0.762500 +vt 0.387500 0.987500 +vt 0.387500 0.762500 +vt 0.862500 0.512500 +vt 0.637500 0.737500 +vt 0.637500 0.512500 +vt 0.612500 0.512500 +vt 0.387500 0.737500 +vt 0.387500 0.512500 +vt 0.616363 0.487500 +vt 0.612500 0.491363 +vt 0.612500 0.487500 +vt 0.619846 0.487500 +vt 0.616488 0.491466 +vt 0.625000 0.487500 +vt 0.619677 0.491337 +vt 0.612500 0.494846 +vt 0.619114 0.494114 +vt 0.616346 0.494683 +vt 0.625000 0.491347 +vt 0.633637 0.512500 +vt 0.637500 0.508637 +vt 0.630154 0.512500 +vt 0.633512 0.508534 +vt 0.619846 0.512500 +vt 0.630323 0.508663 +vt 0.637500 0.505154 +vt 0.630886 0.505886 +vt 0.633654 0.505317 +vt 0.619683 0.508653 +vt 0.612500 0.508637 +vt 0.616363 0.512500 +vt 0.612500 0.505154 +vt 0.616466 0.508512 +vt 0.616337 0.505323 +vt 0.619114 0.505886 +vt 0.625000 0.494114 +vt 0.619114 0.500000 +vt 0.362500 0.508637 +vt 0.366363 0.512500 +vt 0.362500 0.505154 +vt 0.366466 0.508512 +vt 0.362500 0.500000 +vt 0.366337 0.505323 +vt 0.369846 0.512500 +vt 0.369114 0.505886 +vt 0.369683 0.508653 +vt 0.366347 0.500000 +vt 0.387500 0.491363 +vt 0.383637 0.487500 +vt 0.387500 0.487500 +vt 0.387500 0.494846 +vt 0.383534 0.491488 +vt 0.387500 0.505154 +vt 0.383663 0.494677 +vt 0.380154 0.487500 +vt 0.380886 0.494114 +vt 0.380317 0.491346 +vt 0.383653 0.505317 +vt 0.383637 0.512500 +vt 0.387500 0.508637 +vt 0.380154 0.512500 +vt 0.383512 0.508534 +vt 0.380323 0.508663 +vt 0.380886 0.505886 +vt 0.369114 0.500000 +vt 0.375000 0.505886 +vt 0.616363 0.737500 +vt 0.612500 0.741363 +vt 0.612500 0.737500 +vt 0.619846 0.737500 +vt 0.616488 0.741466 +vt 0.630154 0.737500 +vt 0.619677 0.741337 +vt 0.612500 0.744846 +vt 0.619114 0.744114 +vt 0.616346 0.744683 +vt 0.630317 0.741346 +vt 0.637500 0.741363 +vt 0.633637 0.737500 +vt 0.637500 0.744846 +vt 0.633534 0.741488 +vt 0.637500 0.750000 +vt 0.633664 0.744677 +vt 0.630886 0.744114 +vt 0.633653 0.750000 +vt 0.612500 0.758637 +vt 0.616363 0.762500 +vt 0.612500 0.755154 +vt 0.616466 0.758512 +vt 0.616337 0.755323 +vt 0.619846 0.762500 +vt 0.619114 0.755886 +vt 0.619683 0.758653 +vt 0.625000 0.744114 +vt 0.619114 0.750000 +vt 0.387500 0.741363 +vt 0.383637 0.737500 +vt 0.387500 0.744846 +vt 0.383534 0.741488 +vt 0.387500 0.755154 +vt 0.383663 0.744677 +vt 0.380154 0.737500 +vt 0.380886 0.744114 +vt 0.380317 0.741346 +vt 0.383653 0.755317 +vt 0.383637 0.762500 +vt 0.387500 0.758637 +vt 0.380154 0.762500 +vt 0.383512 0.758534 +vt 0.375000 0.762500 +vt 0.380323 0.758663 +vt 0.380886 0.755886 +vt 0.375000 0.758654 +vt 0.366363 0.737500 +vt 0.362500 0.741363 +vt 0.362500 0.737500 +vt 0.369846 0.737500 +vt 0.366488 0.741466 +vt 0.369677 0.741337 +vt 0.362500 0.744846 +vt 0.369114 0.744114 +vt 0.366347 0.744683 +vt 0.380886 0.750000 +vt 0.375000 0.744114 +vt 0.612500 0.258637 +vt 0.616363 0.262500 +vt 0.612500 0.262500 +vt 0.612500 0.255154 +vt 0.616466 0.258512 +vt 0.612500 0.244846 +vt 0.616337 0.255323 +vt 0.619846 0.262500 +vt 0.619114 0.255886 +vt 0.619683 0.258653 +vt 0.616346 0.244683 +vt 0.616363 0.237500 +vt 0.612500 0.241363 +vt 0.612500 0.237500 +vt 0.619846 0.237500 +vt 0.616488 0.241466 +vt 0.625000 0.237500 +vt 0.619677 0.241337 +vt 0.619114 0.244114 +vt 0.625000 0.241347 +vt 0.862500 0.508637 +vt 0.866363 0.512500 +vt 0.862500 0.505154 +vt 0.866466 0.508512 +vt 0.862500 0.500000 +vt 0.866337 0.505323 +vt 0.869846 0.512500 +vt 0.869114 0.505886 +vt 0.869683 0.508653 +vt 0.866347 0.500000 +vt 0.619114 0.250000 +vt 0.625000 0.255886 +vt 0.387500 0.241363 +vt 0.383637 0.237500 +vt 0.387500 0.244846 +vt 0.383534 0.241488 +vt 0.387500 0.255154 +vt 0.383663 0.244677 +vt 0.380154 0.237500 +vt 0.380886 0.244114 +vt 0.380317 0.241346 +vt 0.383653 0.255317 +vt 0.383637 0.262500 +vt 0.387500 0.258637 +vt 0.387500 0.262500 +vt 0.380154 0.262500 +vt 0.383512 0.258534 +vt 0.375000 0.262500 +vt 0.380323 0.258663 +vt 0.380886 0.255886 +vt 0.375000 0.258653 +vt 0.133637 0.512500 +vt 0.137500 0.508637 +vt 0.130154 0.512500 +vt 0.133512 0.508534 +vt 0.125000 0.512500 +vt 0.130323 0.508663 +vt 0.137500 0.505154 +vt 0.130886 0.505886 +vt 0.133653 0.505317 +vt 0.125000 0.508654 +vt 0.380886 0.250000 +vt 0.375000 0.244114 +vt 0.612500 0.008637 +vt 0.616363 0.012500 +vt 0.612500 0.005154 +vt 0.616466 0.008512 +vt 0.612500 0.000000 +vt 0.616337 0.005323 +vt 0.619846 0.012500 +vt 0.619114 0.005886 +vt 0.619683 0.008654 +vt 0.616346 0.000000 +vt 0.616363 0.987500 +vt 0.612500 0.991363 +vt 0.612500 0.987500 +vt 0.619846 0.987500 +vt 0.616488 0.991466 +vt 0.625000 0.987500 +vt 0.619677 0.991337 +vt 0.612500 0.994846 +vt 0.619114 0.994114 +vt 0.616346 0.994683 +vt 0.625000 0.991346 +vt 0.866363 0.737500 +vt 0.862500 0.741363 +vt 0.862500 0.737500 +vt 0.869846 0.737500 +vt 0.866488 0.741466 +vt 0.875000 0.737500 +vt 0.869677 0.741337 +vt 0.862500 0.744846 +vt 0.869114 0.744114 +vt 0.866346 0.744683 +vt 0.875000 0.741347 +vt 0.619114 0.000000 +vt 0.625000 0.005886 +vt 0.137500 0.741363 +vt 0.133637 0.737500 +vt 0.137500 0.744846 +vt 0.133534 0.741488 +vt 0.137500 0.750000 +vt 0.133663 0.744677 +vt 0.130154 0.737500 +vt 0.130886 0.744114 +vt 0.130317 0.741346 +vt 0.133653 0.750000 +vt 0.387500 0.991363 +vt 0.383637 0.987500 +vt 0.387500 0.994846 +vt 0.383534 0.991488 +vt 0.387500 1.000000 +vt 0.383663 0.994677 +vt 0.380154 0.987500 +vt 0.380886 0.994114 +vt 0.380317 0.991346 +vt 0.383654 1.000000 +vt 0.383637 0.012500 +vt 0.387500 0.008637 +vt 0.380154 0.012500 +vt 0.383512 0.008534 +vt 0.375000 0.012500 +vt 0.380323 0.008663 +vt 0.387500 0.005154 +vt 0.380886 0.005886 +vt 0.383653 0.005317 +vt 0.375000 0.008653 +vt 0.130886 0.750000 +vt 0.125000 0.744114 +vt 0.630886 0.750000 +vt 0.375000 0.755886 +vt 0.625000 0.244114 +vt 0.869114 0.500000 +vt 0.375000 0.255886 +vt 0.125000 0.505886 +vt 0.625000 0.994114 +vt 0.875000 0.744114 +vt 0.380886 1.000000 +vt 0.375000 0.005886 +vt 0.125000 0.737500 +vt 0.137500 0.500000 +vt 0.612500 1.000000 +vt 0.862500 0.750000 +vt 0.362500 0.750000 +vt 0.875000 0.512500 +vt 0.637500 0.500000 +vn 0.0779 -0.9939 -0.0779 +vn -0.0779 -0.9939 0.0779 +vn -0.0779 -0.9939 -0.0779 +vn -0.9939 0.0779 0.0779 +vn -0.9939 -0.0779 -0.0779 +vn -0.9939 -0.0779 0.0779 +vn 0.0779 0.0779 0.9939 +vn -0.0779 -0.0779 0.9939 +vn 0.0779 -0.0779 0.9939 +vn -0.0779 0.9939 -0.0779 +vn 0.0779 0.9939 0.0779 +vn 0.0779 0.9939 -0.0779 +vn 0.9939 0.0779 -0.0779 +vn 0.9939 -0.0779 0.0779 +vn 0.9939 -0.0779 -0.0779 +vn 0.0783 0.3064 -0.9486 +vn 0.3066 0.0787 -0.9485 +vn 0.0779 0.0779 -0.9939 +vn 0.0754 0.5855 -0.8071 +vn 0.3089 0.3098 -0.8992 +vn 0.0757 0.8072 -0.5853 +vn 0.2866 0.5718 -0.7687 +vn 0.5853 0.0757 -0.8072 +vn 0.5154 0.5156 -0.6844 +vn 0.5719 0.2870 -0.7685 +vn 0.2870 0.7685 -0.5719 +vn 0.3064 0.9486 -0.0783 +vn 0.0787 0.9485 -0.3066 +vn 0.5855 0.8071 -0.0754 +vn 0.3098 0.8992 -0.3089 +vn 0.8072 0.5853 -0.0757 +vn 0.5718 0.7687 -0.2866 +vn 0.5156 0.6844 -0.5154 +vn 0.7685 0.5719 -0.2870 +vn 0.9486 0.0783 -0.3064 +vn 0.9485 0.3066 -0.0787 +vn 0.8071 0.0754 -0.5855 +vn 0.8992 0.3089 -0.3098 +vn 0.7687 0.2866 -0.5718 +vn 0.6844 0.5154 -0.5156 +vn 0.0783 -0.9486 -0.3064 +vn 0.3066 -0.9485 -0.0787 +vn 0.0754 -0.8071 -0.5855 +vn 0.3089 -0.8992 -0.3098 +vn 0.0757 -0.5853 -0.8072 +vn 0.2866 -0.7687 -0.5718 +vn 0.5853 -0.8072 -0.0757 +vn 0.5154 -0.6844 -0.5156 +vn 0.5719 -0.7685 -0.2870 +vn 0.2870 -0.5719 -0.7685 +vn 0.3064 -0.0783 -0.9486 +vn 0.0787 -0.3066 -0.9485 +vn 0.0779 -0.0779 -0.9939 +vn 0.5855 -0.0754 -0.8071 +vn 0.3098 -0.3089 -0.8992 +vn 0.8072 -0.0757 -0.5853 +vn 0.5718 -0.2866 -0.7687 +vn 0.5156 -0.5154 -0.6844 +vn 0.7685 -0.2870 -0.5719 +vn 0.9486 -0.3064 -0.0783 +vn 0.9485 -0.0787 -0.3066 +vn 0.8071 -0.5855 -0.0754 +vn 0.8992 -0.3098 -0.3089 +vn 0.7687 -0.5718 -0.2866 +vn 0.6844 -0.5156 -0.5154 +vn 0.9486 0.3064 0.0783 +vn 0.9485 0.0787 0.3066 +vn 0.9939 0.0779 0.0779 +vn 0.8071 0.5855 0.0754 +vn 0.8992 0.3098 0.3089 +vn 0.5853 0.8072 0.0757 +vn 0.7687 0.5718 0.2866 +vn 0.8072 0.0757 0.5853 +vn 0.6844 0.5156 0.5154 +vn 0.7685 0.2870 0.5719 +vn 0.5719 0.7685 0.2870 +vn 0.0783 0.9486 0.3064 +vn 0.3066 0.9485 0.0787 +vn 0.0754 0.8071 0.5855 +vn 0.3089 0.8992 0.3098 +vn 0.0757 0.5853 0.8072 +vn 0.2866 0.7687 0.5718 +vn 0.5154 0.6844 0.5156 +vn 0.2870 0.5719 0.7685 +vn 0.3064 0.0783 0.9486 +vn 0.0787 0.3066 0.9485 +vn 0.5855 0.0754 0.8071 +vn 0.3098 0.3089 0.8992 +vn 0.5718 0.2866 0.7687 +vn 0.5156 0.5154 0.6844 +vn 0.9486 -0.0783 0.3064 +vn 0.9485 -0.3066 0.0787 +vn 0.8071 -0.0754 0.5855 +vn 0.8992 -0.3089 0.3098 +vn 0.5853 -0.0757 0.8072 +vn 0.7687 -0.2866 0.5718 +vn 0.8072 -0.5853 0.0757 +vn 0.6844 -0.5154 0.5156 +vn 0.7685 -0.5719 0.2870 +vn 0.5719 -0.2870 0.7685 +vn 0.0783 -0.3064 0.9486 +vn 0.3066 -0.0787 0.9485 +vn 0.0754 -0.5855 0.8071 +vn 0.3089 -0.3098 0.8992 +vn 0.0757 -0.8072 0.5853 +vn 0.2866 -0.5718 0.7687 +vn 0.5154 -0.5156 0.6844 +vn 0.2870 -0.7685 0.5719 +vn 0.3064 -0.9486 0.0783 +vn 0.0787 -0.9485 0.3066 +vn 0.0779 -0.9939 0.0779 +vn 0.5855 -0.8071 0.0754 +vn 0.3098 -0.8992 0.3089 +vn 0.5718 -0.7687 0.2866 +vn 0.5156 -0.6844 0.5154 +vn -0.3064 0.0783 -0.9486 +vn -0.0787 0.3066 -0.9485 +vn -0.0779 0.0779 -0.9939 +vn -0.5855 0.0754 -0.8071 +vn -0.3098 0.3089 -0.8992 +vn -0.8072 0.0757 -0.5853 +vn -0.5718 0.2866 -0.7687 +vn -0.0757 0.5853 -0.8072 +vn -0.5156 0.5154 -0.6844 +vn -0.2870 0.5719 -0.7685 +vn -0.7685 0.2870 -0.5719 +vn -0.9486 0.3064 -0.0783 +vn -0.9485 0.0787 -0.3066 +vn -0.9939 0.0779 -0.0779 +vn -0.8071 0.5855 -0.0754 +vn -0.8992 0.3098 -0.3089 +vn -0.5853 0.8072 -0.0757 +vn -0.7687 0.5718 -0.2866 +vn -0.6844 0.5156 -0.5154 +vn -0.5719 0.7685 -0.2870 +vn -0.0783 0.9486 -0.3064 +vn -0.3066 0.9485 -0.0787 +vn -0.0754 0.8071 -0.5855 +vn -0.3089 0.8992 -0.3098 +vn -0.2866 0.7687 -0.5718 +vn -0.5154 0.6844 -0.5156 +vn -0.9486 -0.0783 -0.3064 +vn -0.9485 -0.3066 -0.0787 +vn -0.8071 -0.0754 -0.5855 +vn -0.8992 -0.3089 -0.3098 +vn -0.5853 -0.0757 -0.8072 +vn -0.7687 -0.2866 -0.5718 +vn -0.8072 -0.5853 -0.0757 +vn -0.6844 -0.5154 -0.5156 +vn -0.7685 -0.5719 -0.2870 +vn -0.5719 -0.2870 -0.7685 +vn -0.0783 -0.3064 -0.9486 +vn -0.3066 -0.0787 -0.9485 +vn -0.0779 -0.0779 -0.9939 +vn -0.0754 -0.5855 -0.8071 +vn -0.3089 -0.3098 -0.8992 +vn -0.0757 -0.8072 -0.5853 +vn -0.2866 -0.5718 -0.7687 +vn -0.5154 -0.5156 -0.6844 +vn -0.2870 -0.7685 -0.5719 +vn -0.3064 -0.9486 -0.0783 +vn -0.0787 -0.9485 -0.3066 +vn -0.5855 -0.8071 -0.0754 +vn -0.3098 -0.8992 -0.3089 +vn -0.5718 -0.7687 -0.2866 +vn -0.5156 -0.6844 -0.5154 +vn -0.9486 0.0783 0.3064 +vn -0.9485 0.3066 0.0787 +vn -0.8071 0.0754 0.5855 +vn -0.8992 0.3089 0.3098 +vn -0.5853 0.0757 0.8072 +vn -0.7687 0.2866 0.5718 +vn -0.8072 0.5853 0.0757 +vn -0.6844 0.5154 0.5156 +vn -0.7685 0.5719 0.2870 +vn -0.5719 0.2870 0.7685 +vn -0.0783 0.3064 0.9486 +vn -0.3066 0.0787 0.9485 +vn -0.0779 0.0779 0.9939 +vn -0.0754 0.5855 0.8071 +vn -0.3089 0.3098 0.8992 +vn -0.0757 0.8072 0.5853 +vn -0.2866 0.5718 0.7687 +vn -0.5154 0.5156 0.6844 +vn -0.2870 0.7685 0.5719 +vn -0.3064 0.9486 0.0783 +vn -0.0787 0.9485 0.3066 +vn -0.0779 0.9939 0.0779 +vn -0.5855 0.8071 0.0754 +vn -0.3098 0.8992 0.3089 +vn -0.5718 0.7687 0.2866 +vn -0.5156 0.6844 0.5154 +vn -0.0783 -0.9486 0.3064 +vn -0.3066 -0.9485 0.0787 +vn -0.0754 -0.8071 0.5855 +vn -0.3089 -0.8992 0.3098 +vn -0.0757 -0.5853 0.8072 +vn -0.2866 -0.7687 0.5718 +vn -0.5853 -0.8072 0.0757 +vn -0.5154 -0.6844 0.5156 +vn -0.5719 -0.7685 0.2870 +vn -0.2870 -0.5719 0.7685 +vn -0.3064 -0.0783 0.9486 +vn -0.0787 -0.3066 0.9485 +vn -0.5855 -0.0754 0.8071 +vn -0.3098 -0.3089 0.8992 +vn -0.8072 -0.0757 0.5853 +vn -0.5718 -0.2866 0.7687 +vn -0.5156 -0.5154 0.6844 +vn -0.7685 -0.2870 0.5719 +vn -0.9486 -0.3064 0.0783 +vn -0.9485 -0.0787 0.3066 +vn -0.8071 -0.5855 0.0754 +vn -0.8992 -0.3098 0.3089 +vn -0.7687 -0.5718 0.2866 +vn -0.6844 -0.5156 0.5154 +usemtl Material.001 +s 1 +f 28/1/1 190/2/2 138/3/3 +f 163/4/4 136/5/5 192/6/6 +f 57/7/7 191/8/8 83/9/9 +f 111/10/10 56/11/11 2/12/12 +f 3/13/13 82/14/14 30/15/15 +f 4/16/16 6/17/17 1/18/18 +f 5/19/19 7/20/20 4/16/16 +f 17/21/21 8/22/22 5/19/19 +f 7/20/20 9/23/23 6/17/17 +f 7/20/20 11/24/24 10/25/25 +f 18/26/26 11/24/24 8/22/22 +f 12/27/27 14/28/28 2/12/12 +f 13/29/29 15/30/30 12/27/27 +f 25/31/31 16/32/32 13/29/29 +f 15/30/30 17/33/21 14/28/28 +f 15/30/30 19/34/33 18/35/26 +f 26/36/34 19/34/33 16/32/32 +f 20/37/35 22/38/36 3/13/13 +f 21/39/37 23/40/38 20/37/35 +f 9/23/23 24/41/39 21/39/37 +f 23/40/38 25/31/31 22/38/36 +f 23/40/38 27/42/40 26/36/34 +f 10/25/25 27/42/40 24/41/39 +f 11/24/24 19/43/33 27/44/40 +f 31/45/41 33/46/42 28/1/1 +f 32/47/43 34/48/44 31/45/41 +f 44/49/45 35/50/46 32/47/43 +f 34/48/44 36/51/47 33/46/42 +f 34/48/44 38/52/48 37/53/49 +f 45/54/50 38/52/48 35/50/46 +f 39/55/51 41/56/52 29/57/53 +f 40/58/54 42/59/55 39/55/51 +f 52/60/56 43/61/57 40/58/54 +f 42/59/55 44/62/45 41/56/52 +f 42/59/55 46/63/58 45/64/50 +f 53/65/59 46/63/58 43/61/57 +f 47/66/60 49/67/61 30/15/15 +f 48/68/62 50/69/63 47/66/60 +f 36/51/47 51/70/64 48/68/62 +f 50/69/63 52/60/56 49/67/61 +f 50/69/63 54/71/65 53/65/59 +f 37/53/49 54/71/65 51/70/64 +f 38/52/48 46/72/58 54/73/65 +f 58/74/66 60/75/67 55/76/68 +f 59/77/69 61/78/70 58/74/66 +f 71/79/71 62/80/72 59/77/69 +f 61/78/70 63/81/73 60/75/67 +f 61/78/70 65/82/74 64/83/75 +f 72/84/76 65/82/74 62/80/72 +f 66/85/77 68/86/78 56/11/11 +f 67/87/79 69/88/80 66/85/77 +f 79/89/81 70/90/82 67/87/79 +f 69/88/80 71/79/71 68/86/78 +f 69/88/80 73/91/83 72/84/76 +f 80/92/84 73/91/83 70/90/82 +f 74/93/85 76/94/86 57/7/7 +f 75/95/87 77/96/88 74/93/85 +f 63/81/73 78/97/89 75/95/87 +f 77/96/88 79/98/81 76/94/86 +f 77/96/88 81/99/90 80/100/84 +f 64/83/75 81/99/90 78/97/89 +f 65/82/74 73/101/83 81/102/90 +f 85/103/91 87/104/92 82/14/14 +f 86/105/93 88/106/94 85/103/91 +f 98/107/95 89/108/96 86/105/93 +f 88/106/94 90/109/97 87/104/92 +f 88/106/94 92/110/98 91/111/99 +f 99/112/100 92/110/98 89/108/96 +f 93/113/101 95/114/102 83/9/9 +f 94/115/103 96/116/104 93/113/101 +f 106/117/105 97/118/106 94/115/103 +f 96/116/104 98/107/95 95/114/102 +f 96/116/104 100/119/107 99/112/100 +f 107/120/108 100/119/107 97/118/106 +f 101/121/109 103/122/110 84/123/111 +f 102/124/112 104/125/113 101/121/109 +f 90/109/97 105/126/114 102/124/112 +f 104/125/113 106/127/105 103/122/110 +f 104/125/113 108/128/115 107/129/108 +f 91/111/99 108/128/115 105/126/114 +f 92/110/98 100/130/107 108/131/115 +f 112/132/116 114/133/117 109/134/118 +f 113/135/119 115/136/120 112/132/116 +f 125/137/121 116/138/122 113/135/119 +f 115/136/120 117/139/123 114/133/117 +f 115/136/120 119/140/124 118/141/125 +f 126/142/126 119/140/124 116/138/122 +f 120/143/127 122/144/128 110/145/129 +f 121/146/130 123/147/131 120/143/127 +f 133/148/132 124/149/133 121/146/130 +f 123/147/131 125/137/121 122/144/128 +f 123/147/131 127/150/134 126/142/126 +f 134/151/135 127/150/134 124/149/133 +f 128/152/136 130/153/137 111/10/10 +f 129/154/138 131/155/139 128/152/136 +f 117/156/123 132/157/140 129/154/138 +f 131/155/139 133/158/132 130/153/137 +f 131/155/139 135/159/141 134/160/135 +f 118/161/125 135/159/141 132/157/140 +f 119/140/124 127/162/134 135/163/141 +f 139/164/142 141/165/143 136/5/5 +f 140/166/144 142/167/145 139/164/142 +f 152/168/146 143/169/147 140/166/144 +f 142/167/145 144/170/148 141/165/143 +f 142/167/145 146/171/149 145/172/150 +f 153/173/151 146/171/149 143/169/147 +f 147/174/152 149/175/153 137/176/154 +f 148/177/155 150/178/156 147/174/152 +f 160/179/157 151/180/158 148/177/155 +f 150/178/156 152/168/146 149/175/153 +f 150/178/156 154/181/159 153/173/151 +f 161/182/160 154/181/159 151/180/158 +f 155/183/161 157/184/162 138/3/3 +f 156/185/163 158/186/164 155/183/161 +f 144/187/148 159/188/165 156/185/163 +f 158/186/164 160/189/157 157/184/162 +f 158/186/164 162/190/166 161/191/160 +f 145/192/150 162/190/166 159/188/165 +f 146/171/149 154/193/159 162/194/166 +f 166/195/167 168/196/168 163/4/4 +f 167/197/169 169/198/170 166/195/167 +f 179/199/171 170/200/172 167/197/169 +f 169/198/170 171/201/173 168/196/168 +f 169/198/170 173/202/174 172/203/175 +f 180/204/176 173/202/174 170/200/172 +f 174/205/177 176/206/178 164/207/179 +f 175/208/180 177/209/181 174/205/177 +f 187/210/182 178/211/183 175/208/180 +f 177/209/181 179/212/171 176/206/178 +f 177/209/181 181/213/184 180/214/176 +f 188/215/185 181/213/184 178/211/183 +f 182/216/186 184/217/187 165/218/188 +f 183/219/189 185/220/190 182/216/186 +f 171/221/173 186/222/191 183/219/189 +f 185/220/190 187/223/182 184/217/187 +f 185/220/190 189/224/192 188/225/185 +f 172/226/175 189/224/192 186/222/191 +f 173/202/174 181/227/184 189/228/192 +f 193/229/193 195/230/194 190/2/2 +f 194/231/195 196/232/196 193/229/193 +f 206/233/197 197/234/198 194/231/195 +f 196/232/196 198/235/199 195/230/194 +f 196/232/196 200/236/200 199/237/201 +f 207/238/202 200/236/200 197/234/198 +f 201/239/203 203/240/204 191/8/8 +f 202/241/205 204/242/206 201/239/203 +f 214/243/207 205/244/208 202/241/205 +f 204/242/206 206/245/197 203/240/204 +f 204/242/206 208/246/209 207/247/202 +f 215/248/210 208/246/209 205/244/208 +f 209/249/211 211/250/212 192/6/6 +f 210/251/213 212/252/214 209/249/211 +f 198/253/199 213/254/215 210/251/213 +f 212/252/214 214/255/207 211/250/212 +f 212/252/214 216/256/216 215/257/210 +f 199/258/201 216/256/216 213/254/215 +f 200/236/200 208/259/209 216/260/216 +f 190/2/2 155/183/161 138/3/3 +f 195/230/194 156/185/163 155/183/161 +f 198/235/199 144/187/148 156/185/163 +f 210/251/213 141/165/143 144/170/148 +f 209/249/211 136/5/5 141/165/143 +f 138/3/3 31/45/41 28/1/1 +f 157/184/162 32/47/43 31/45/41 +f 160/189/157 44/49/45 32/47/43 +f 148/177/155 41/56/52 44/62/45 +f 147/174/152 29/57/53 41/56/52 +f 30/15/15 20/37/35 3/13/13 +f 49/67/61 21/39/37 20/37/35 +f 52/60/56 9/23/23 21/39/37 +f 40/58/54 6/17/17 9/23/23 +f 39/55/51 1/18/18 6/17/17 +f 164/207/179 201/239/203 191/8/8 +f 176/206/178 202/241/205 201/239/203 +f 179/212/171 214/243/207 202/241/205 +f 167/197/169 211/250/212 214/255/207 +f 166/195/167 192/6/6 211/250/212 +f 83/9/9 74/93/85 57/7/7 +f 95/114/102 75/95/87 74/93/85 +f 98/107/95 63/81/73 75/95/87 +f 86/105/93 60/75/67 63/81/73 +f 85/103/91 55/76/68 60/75/67 +f 137/176/154 112/132/116 109/134/118 +f 149/175/153 113/135/119 112/132/116 +f 152/168/146 125/137/121 113/135/119 +f 140/166/144 122/144/128 125/137/121 +f 139/164/142 110/145/129 122/144/128 +f 165/218/188 66/85/77 56/11/11 +f 184/217/187 67/87/79 66/85/77 +f 187/223/182 79/89/81 67/87/79 +f 175/208/180 76/94/86 79/98/81 +f 174/205/177 57/7/7 76/94/86 +f 56/11/11 12/27/27 2/12/12 +f 68/86/78 13/29/29 12/27/27 +f 71/79/71 25/31/31 13/29/29 +f 59/77/69 22/38/36 25/31/31 +f 58/74/66 3/13/13 22/38/36 +f 84/123/111 193/229/193 190/2/2 +f 103/122/110 194/231/195 193/229/193 +f 106/127/105 206/233/197 194/231/195 +f 94/115/103 203/240/204 206/245/197 +f 93/113/101 191/8/8 203/240/204 +f 111/10/10 182/216/186 165/218/188 +f 130/153/137 183/219/189 182/216/186 +f 133/158/132 171/221/173 183/219/189 +f 121/146/130 168/196/168 171/201/173 +f 120/143/127 163/4/4 168/196/168 +f 2/12/12 128/152/136 111/10/10 +f 14/28/28 129/154/138 128/152/136 +f 17/33/21 117/156/123 129/154/138 +f 5/19/19 114/133/117 117/139/123 +f 4/16/16 109/134/118 114/133/117 +f 28/1/1 101/121/109 84/123/111 +f 33/46/42 102/124/112 101/121/109 +f 36/51/47 90/109/97 102/124/112 +f 48/68/62 87/104/92 90/109/97 +f 47/66/60 82/14/14 87/104/92 +f 109/134/118 29/57/53 137/176/154 +f 28/1/1 84/123/111 190/2/2 +f 163/4/4 110/145/129 136/5/5 +f 57/7/7 164/207/179 191/8/8 +f 111/10/10 165/218/188 56/11/11 +f 3/13/13 55/76/68 82/14/14 +f 4/16/16 7/20/20 6/17/17 +f 5/19/19 8/22/22 7/20/20 +f 17/21/21 18/26/26 8/22/22 +f 7/20/20 10/25/25 9/23/23 +f 7/20/20 8/22/22 11/24/24 +f 18/26/26 19/43/33 11/24/24 +f 12/27/27 15/30/30 14/28/28 +f 13/29/29 16/32/32 15/30/30 +f 25/31/31 26/36/34 16/32/32 +f 15/30/30 18/35/26 17/33/21 +f 15/30/30 16/32/32 19/34/33 +f 26/36/34 27/42/40 19/34/33 +f 20/37/35 23/40/38 22/38/36 +f 21/39/37 24/41/39 23/40/38 +f 9/23/23 10/25/25 24/41/39 +f 23/40/38 26/36/34 25/31/31 +f 23/40/38 24/41/39 27/42/40 +f 10/25/25 11/24/24 27/42/40 +f 31/45/41 34/48/44 33/46/42 +f 32/47/43 35/50/46 34/48/44 +f 44/49/45 45/54/50 35/50/46 +f 34/48/44 37/53/49 36/51/47 +f 34/48/44 35/50/46 38/52/48 +f 45/54/50 46/72/58 38/52/48 +f 39/55/51 42/59/55 41/56/52 +f 40/58/54 43/61/57 42/59/55 +f 52/60/56 53/65/59 43/61/57 +f 42/59/55 45/64/50 44/62/45 +f 42/59/55 43/61/57 46/63/58 +f 53/65/59 54/71/65 46/63/58 +f 47/66/60 50/69/63 49/67/61 +f 48/68/62 51/70/64 50/69/63 +f 36/51/47 37/53/49 51/70/64 +f 50/69/63 53/65/59 52/60/56 +f 50/69/63 51/70/64 54/71/65 +f 37/53/49 38/52/48 54/71/65 +f 58/74/66 61/78/70 60/75/67 +f 59/77/69 62/80/72 61/78/70 +f 71/79/71 72/84/76 62/80/72 +f 61/78/70 64/83/75 63/81/73 +f 61/78/70 62/80/72 65/82/74 +f 72/84/76 73/91/83 65/82/74 +f 66/85/77 69/88/80 68/86/78 +f 67/87/79 70/90/82 69/88/80 +f 79/89/81 80/92/84 70/90/82 +f 69/88/80 72/84/76 71/79/71 +f 69/88/80 70/90/82 73/91/83 +f 80/92/84 81/261/90 73/91/83 +f 74/93/85 77/96/88 76/94/86 +f 75/95/87 78/97/89 77/96/88 +f 63/81/73 64/83/75 78/97/89 +f 77/96/88 80/100/84 79/98/81 +f 77/96/88 78/97/89 81/99/90 +f 64/83/75 65/82/74 81/99/90 +f 85/103/91 88/106/94 87/104/92 +f 86/105/93 89/108/96 88/106/94 +f 98/107/95 99/112/100 89/108/96 +f 88/106/94 91/111/99 90/109/97 +f 88/106/94 89/108/96 92/110/98 +f 99/112/100 100/119/107 92/110/98 +f 93/113/101 96/116/104 95/114/102 +f 94/115/103 97/118/106 96/116/104 +f 106/117/105 107/120/108 97/118/106 +f 96/116/104 99/112/100 98/107/95 +f 96/116/104 97/118/106 100/119/107 +f 107/120/108 108/262/115 100/119/107 +f 101/121/109 104/125/113 103/122/110 +f 102/124/112 105/126/114 104/125/113 +f 90/109/97 91/111/99 105/126/114 +f 104/125/113 107/129/108 106/127/105 +f 104/125/113 105/126/114 108/128/115 +f 91/111/99 92/110/98 108/128/115 +f 112/132/116 115/136/120 114/133/117 +f 113/135/119 116/138/122 115/136/120 +f 125/137/121 126/142/126 116/138/122 +f 115/136/120 118/141/125 117/139/123 +f 115/136/120 116/138/122 119/140/124 +f 126/142/126 127/150/134 119/140/124 +f 120/143/127 123/147/131 122/144/128 +f 121/146/130 124/149/133 123/147/131 +f 133/148/132 134/151/135 124/149/133 +f 123/147/131 126/142/126 125/137/121 +f 123/147/131 124/149/133 127/150/134 +f 134/151/135 135/263/141 127/150/134 +f 128/152/136 131/155/139 130/153/137 +f 129/154/138 132/157/140 131/155/139 +f 117/156/123 118/161/125 132/157/140 +f 131/155/139 134/160/135 133/158/132 +f 131/155/139 132/157/140 135/159/141 +f 118/161/125 119/264/124 135/159/141 +f 139/164/142 142/167/145 141/165/143 +f 140/166/144 143/169/147 142/167/145 +f 152/168/146 153/173/151 143/169/147 +f 142/167/145 145/172/150 144/170/148 +f 142/167/145 143/169/147 146/171/149 +f 153/173/151 154/181/159 146/171/149 +f 147/174/152 150/178/156 149/175/153 +f 148/177/155 151/180/158 150/178/156 +f 160/179/157 161/182/160 151/180/158 +f 150/178/156 153/173/151 152/168/146 +f 150/178/156 151/180/158 154/181/159 +f 161/182/160 162/265/166 154/181/159 +f 155/183/161 158/186/164 157/184/162 +f 156/185/163 159/188/165 158/186/164 +f 144/187/148 145/192/150 159/188/165 +f 158/186/164 161/191/160 160/189/157 +f 158/186/164 159/188/165 162/190/166 +f 145/192/150 146/266/149 162/190/166 +f 166/195/167 169/198/170 168/196/168 +f 167/197/169 170/200/172 169/198/170 +f 179/199/171 180/204/176 170/200/172 +f 169/198/170 172/203/175 171/201/173 +f 169/198/170 170/200/172 173/202/174 +f 180/204/176 181/227/184 173/202/174 +f 174/205/177 177/209/181 176/206/178 +f 175/208/180 178/211/183 177/209/181 +f 187/210/182 188/215/185 178/211/183 +f 177/209/181 180/214/176 179/212/171 +f 177/209/181 178/211/183 181/213/184 +f 188/215/185 189/267/192 181/213/184 +f 182/216/186 185/220/190 184/217/187 +f 183/219/189 186/222/191 185/220/190 +f 171/221/173 172/226/175 186/222/191 +f 185/220/190 188/225/185 187/223/182 +f 185/220/190 186/222/191 189/224/192 +f 172/226/175 173/268/174 189/224/192 +f 193/229/193 196/232/196 195/230/194 +f 194/231/195 197/234/198 196/232/196 +f 206/233/197 207/238/202 197/234/198 +f 196/232/196 199/237/201 198/235/199 +f 196/232/196 197/234/198 200/236/200 +f 207/238/202 208/259/209 200/236/200 +f 201/239/203 204/242/206 203/240/204 +f 202/241/205 205/244/208 204/242/206 +f 214/243/207 215/248/210 205/244/208 +f 204/242/206 207/247/202 206/245/197 +f 204/242/206 205/244/208 208/246/209 +f 215/248/210 216/269/216 208/246/209 +f 209/249/211 212/252/214 211/250/212 +f 210/251/213 213/254/215 212/252/214 +f 198/253/199 199/258/201 213/254/215 +f 212/252/214 215/257/210 214/255/207 +f 212/252/214 213/254/215 216/256/216 +f 199/258/201 200/270/200 216/256/216 +f 190/2/2 195/230/194 155/183/161 +f 195/230/194 198/235/199 156/185/163 +f 198/235/199 210/271/213 144/187/148 +f 210/251/213 209/249/211 141/165/143 +f 209/249/211 192/6/6 136/5/5 +f 138/3/3 157/184/162 31/45/41 +f 157/184/162 160/189/157 32/47/43 +f 160/189/157 148/272/155 44/49/45 +f 148/177/155 147/174/152 41/56/52 +f 147/174/152 137/176/154 29/57/53 +f 30/15/15 49/67/61 20/37/35 +f 49/67/61 52/60/56 21/39/37 +f 52/60/56 40/58/54 9/23/23 +f 40/58/54 39/55/51 6/17/17 +f 39/55/51 29/57/53 1/18/18 +f 164/207/179 176/206/178 201/239/203 +f 176/206/178 179/212/171 202/241/205 +f 179/212/171 167/273/169 214/243/207 +f 167/197/169 166/195/167 211/250/212 +f 166/195/167 163/4/4 192/6/6 +f 83/9/9 95/114/102 74/93/85 +f 95/114/102 98/107/95 75/95/87 +f 98/107/95 86/105/93 63/81/73 +f 86/105/93 85/103/91 60/75/67 +f 85/103/91 82/14/14 55/76/68 +f 137/176/154 149/175/153 112/132/116 +f 149/175/153 152/168/146 113/135/119 +f 152/168/146 140/166/144 125/137/121 +f 140/166/144 139/164/142 122/144/128 +f 139/164/142 136/5/5 110/145/129 +f 165/218/188 184/217/187 66/85/77 +f 184/217/187 187/223/182 67/87/79 +f 187/223/182 175/274/180 79/89/81 +f 175/208/180 174/205/177 76/94/86 +f 174/205/177 164/207/179 57/7/7 +f 56/11/11 68/86/78 12/27/27 +f 68/86/78 71/79/71 13/29/29 +f 71/79/71 59/77/69 25/31/31 +f 59/77/69 58/74/66 22/38/36 +f 58/74/66 55/76/68 3/13/13 +f 84/123/111 103/122/110 193/229/193 +f 103/122/110 106/127/105 194/231/195 +f 106/127/105 94/275/103 206/233/197 +f 94/115/103 93/113/101 203/240/204 +f 93/113/101 83/9/9 191/8/8 +f 111/10/10 130/153/137 182/216/186 +f 130/153/137 133/158/132 183/219/189 +f 133/158/132 121/276/130 171/221/173 +f 121/146/130 120/143/127 168/196/168 +f 120/143/127 110/145/129 163/4/4 +f 2/12/12 14/28/28 128/152/136 +f 14/28/28 17/33/21 129/154/138 +f 17/33/21 5/277/19 117/156/123 +f 5/19/19 4/16/16 114/133/117 +f 4/16/16 1/18/18 109/134/118 +f 28/1/1 33/46/42 101/121/109 +f 33/46/42 36/51/47 102/124/112 +f 36/51/47 48/68/62 90/109/97 +f 48/68/62 47/66/60 87/104/92 +f 47/66/60 30/15/15 82/14/14 +f 109/134/118 1/18/18 29/57/53 diff --git a/res/cube.zip b/res/cube.zip new file mode 100644 index 0000000..857be55 Binary files /dev/null and b/res/cube.zip differ diff --git a/res/images/happy-tree.png b/res/images/happy-tree.png deleted file mode 100644 index fc86db3..0000000 Binary files a/res/images/happy-tree.png and /dev/null differ diff --git a/res/images/pikachu.png b/res/images/pikachu.png deleted file mode 100644 index 5b24825..0000000 Binary files a/res/images/pikachu.png and /dev/null differ diff --git a/res/shaders/main.wgsl b/res/shaders/main.wgsl deleted file mode 100644 index 1e8fed4..0000000 --- a/res/shaders/main.wgsl +++ /dev/null @@ -1,49 +0,0 @@ -struct InstanceInput { - [[location(5)]] model_matrix_0: vec4; - [[location(6)]] model_matrix_1: vec4; - [[location(7)]] model_matrix_2: vec4; - [[location(8)]] model_matrix_3: vec4; -}; - -struct CameraUniform { - view_proj: mat4x4; -}; -[[group(1), binding(0)]] -var camera: CameraUniform; - -struct VertexInput { - [[location(0)]] position: vec3; - [[location(1)]] tex_coords: vec2; -}; - -struct VertexOutput { - [[builtin(position)]] clip_position: vec4; - [[location(0)]] tex_coords: vec2; -}; - -[[stage(vertex)]] -fn vs_main( - model: VertexInput, - instance: InstanceInput, -) -> VertexOutput { - let model_matrix = mat4x4( - instance.model_matrix_0, - instance.model_matrix_1, - instance.model_matrix_2, - instance.model_matrix_3, - ); - var out: VertexOutput; - out.tex_coords = model.tex_coords; - out.clip_position = camera.view_proj * model_matrix * vec4(model.position, 1.0); - return out; -} - -[[group(0), binding(0)]] -var t_diffuse: texture_2d; -[[group(0), binding(1)]] -var s_diffuse: sampler; - -[[stage(fragment)]] -fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { - return textureSample(t_diffuse, s_diffuse, in.tex_coords); -} \ No newline at end of file diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..633cf71 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,196 @@ +use cgmath::*; +use std::f32::consts::FRAC_PI_2; +use std::time::Duration; +use winit::dpi::PhysicalPosition; +use winit::event::*; + +#[rustfmt::skip] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001; + +#[derive(Debug)] +pub struct Camera { + pub position: Point3, + yaw: Rad, + pitch: Rad, +} + +impl Camera { + pub fn new>, Y: Into>, P: Into>>( + position: V, + yaw: Y, + pitch: P, + ) -> Self { + Self { + position: position.into(), + yaw: yaw.into(), + pitch: pitch.into(), + } + } + + pub fn calc_matrix(&self) -> Matrix4 { + let (sin_pitch, cos_pitch) = self.pitch.0.sin_cos(); + let (sin_yaw, cos_yaw) = self.yaw.0.sin_cos(); + + Matrix4::look_to_rh( + self.position, + Vector3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(), + Vector3::unit_y(), + ) + } +} + +pub struct Projection { + aspect: f32, + fovy: Rad, + znear: f32, + zfar: f32, +} + +impl Projection { + pub fn new>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self { + Self { + aspect: width as f32 / height as f32, + fovy: fovy.into(), + znear, + zfar, + } + } + + pub fn resize(&mut self, width: u32, height: u32) { + self.aspect = width as f32 / height as f32; + } + + pub fn calc_matrix(&self) -> Matrix4 { + OPENGL_TO_WGPU_MATRIX * perspective(self.fovy, self.aspect, self.znear, self.zfar) + } +} + +#[derive(Debug)] +pub struct CameraController { + amount_left: f32, + amount_right: f32, + amount_forward: f32, + amount_backward: f32, + amount_up: f32, + amount_down: f32, + rotate_horizontal: f32, + rotate_vertical: f32, + scroll: f32, + speed: f32, + sensitivity: f32, +} + +impl CameraController { + pub fn new(speed: f32, sensitivity: f32) -> Self { + Self { + amount_left: 0.0, + amount_right: 0.0, + amount_forward: 0.0, + amount_backward: 0.0, + amount_up: 0.0, + amount_down: 0.0, + rotate_horizontal: 0.0, + rotate_vertical: 0.0, + scroll: 0.0, + speed, + sensitivity, + } + } + + pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool { + let amount = if state == ElementState::Pressed { + 1.0 + } else { + 0.0 + }; + match key { + VirtualKeyCode::W | VirtualKeyCode::Up => { + self.amount_forward = amount; + true + } + VirtualKeyCode::S | VirtualKeyCode::Down => { + self.amount_backward = amount; + true + } + VirtualKeyCode::A | VirtualKeyCode::Left => { + self.amount_left = amount; + true + } + VirtualKeyCode::D | VirtualKeyCode::Right => { + self.amount_right = amount; + true + } + VirtualKeyCode::Space => { + self.amount_up = amount; + true + } + VirtualKeyCode::LShift => { + self.amount_down = amount; + true + } + _ => false, + } + } + + pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) { + self.rotate_horizontal = mouse_dx as f32; + self.rotate_vertical = mouse_dy as f32; + } + + pub fn process_scroll(&mut self, delta: &MouseScrollDelta) { + self.scroll = match delta { + // I'm assuming a line is about 100 pixels + MouseScrollDelta::LineDelta(_, scroll) => -scroll * 0.5, + MouseScrollDelta::PixelDelta(PhysicalPosition { y: scroll, .. }) => -*scroll as f32, + }; + } + + pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) { + let dt = dt.as_secs_f32(); + + // Move forward/backward and left/right + let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos(); + let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize(); + let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize(); + camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt; + camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt; + + // Move in/out (aka. "zoom") + // Note: this isn't an actual zoom. The camera's position + // changes when zooming. I've added this to make it easier + // to get closer to an object you want to focus on. + let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos(); + let scrollward = + Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize(); + camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt; + self.scroll = 0.0; + + // Move up/down. Since we don't use roll, we can just + // modify the y coordinate directly. + camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt; + + // Rotate + camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt; + camera.pitch += Rad(-self.rotate_vertical) * self.sensitivity * dt; + + // If process_mouse isn't called every frame, these values + // will not get set to zero, and the camera will rotate + // when moving in a non cardinal direction. + self.rotate_horizontal = 0.0; + self.rotate_vertical = 0.0; + + // Keep the camera's angle from going too high/low. + if camera.pitch < -Rad(SAFE_FRAC_PI_2) { + camera.pitch = -Rad(SAFE_FRAC_PI_2); + } else if camera.pitch > Rad(SAFE_FRAC_PI_2) { + camera.pitch = Rad(SAFE_FRAC_PI_2); + } + } +} diff --git a/src/input/mod.rs b/src/input/mod.rs deleted file mode 100644 index 951b594..0000000 --- a/src/input/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -use winit::event::WindowEvent; - -pub trait Controllable { - fn process_events(&mut self, event: &WindowEvent) -> bool; -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..66b7da2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,750 @@ +use cgmath::prelude::*; +use rayon::prelude::*; +use std::iter; +use wgpu::util::DeviceExt; +use winit::{ + event::*, + event_loop::{ControlFlow, EventLoop}, + window::Window, +}; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +mod camera; +mod model; +mod resources; +mod texture; + +use model::{DrawLight, DrawModel, Vertex}; + +const NUM_INSTANCES_PER_ROW: u32 = 10; + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct CameraUniform { + view_position: [f32; 4], + view_proj: [[f32; 4]; 4], +} + +impl CameraUniform { + fn new() -> Self { + Self { + view_position: [0.0; 4], + view_proj: cgmath::Matrix4::identity().into(), + } + } + + fn update_view_proj(&mut self, camera: &camera::Camera, projection: &camera::Projection) { + self.view_position = camera.position.to_homogeneous().into(); + self.view_proj = (projection.calc_matrix() * camera.calc_matrix()).into() + } +} + +struct Instance { + position: cgmath::Vector3, + rotation: cgmath::Quaternion, +} + +impl Instance { + fn to_raw(&self) -> InstanceRaw { + InstanceRaw { + model: (cgmath::Matrix4::from_translation(self.position) + * cgmath::Matrix4::from(self.rotation)) + .into(), + normal: cgmath::Matrix3::from(self.rotation).into(), + } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[allow(dead_code)] +struct InstanceRaw { + model: [[f32; 4]; 4], + normal: [[f32; 3]; 3], +} + +impl model::Vertex for InstanceRaw { + fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + use std::mem; + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + // We need to switch from using a step mode of Vertex to Instance + // This means that our shaders will only change to use the next + // instance when the shader starts processing a new instance + step_mode: wgpu::VertexStepMode::Instance, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + // While our vertex shader only uses locations 0, and 1 now, in later tutorials we'll + // be using 2, 3, and 4, for Vertex. We'll start at slot 5 not conflict with them later + shader_location: 5, + format: wgpu::VertexFormat::Float32x4, + }, + // A mat4 takes up 4 vertex slots as it is technically 4 vec4s. We need to define a slot + // for each vec4. We don't have to do this in code though. + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress, + shader_location: 6, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, + shader_location: 7, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress, + shader_location: 8, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 9, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 19]>() as wgpu::BufferAddress, + shader_location: 10, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 22]>() as wgpu::BufferAddress, + shader_location: 11, + format: wgpu::VertexFormat::Float32x3, + }, + ], + } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct LightUniform { + position: [f32; 3], + // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here + _padding: u32, + color: [f32; 3], + _padding2: u32, +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + render_pipeline: wgpu::RenderPipeline, + obj_model: model::Model, + camera: camera::Camera, + projection: camera::Projection, + camera_controller: camera::CameraController, + camera_uniform: CameraUniform, + camera_buffer: wgpu::Buffer, + camera_bind_group: wgpu::BindGroup, + instances: Vec, + #[allow(dead_code)] + instance_buffer: wgpu::Buffer, + depth_texture: texture::Texture, + size: winit::dpi::PhysicalSize, + light_uniform: LightUniform, + light_buffer: wgpu::Buffer, + light_bind_group: wgpu::BindGroup, + light_render_pipeline: wgpu::RenderPipeline, + #[allow(dead_code)] + debug_material: model::Material, + mouse_pressed: bool, +} + +fn create_render_pipeline( + device: &wgpu::Device, + layout: &wgpu::PipelineLayout, + color_format: wgpu::TextureFormat, + depth_format: Option, + vertex_layouts: &[wgpu::VertexBufferLayout], + shader: wgpu::ShaderModuleDescriptor, +) -> wgpu::RenderPipeline { + let shader = device.create_shader_module(&shader); + + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some(&format!("{:?}", shader)), + layout: Some(layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: vertex_layouts, + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[wgpu::ColorTargetState { + format: color_format, + blend: Some(wgpu::BlendState { + alpha: wgpu::BlendComponent::REPLACE, + color: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::ALL, + }], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE + polygon_mode: wgpu::PolygonMode::Fill, + // Requires Features::DEPTH_CLIP_CONTROL + unclipped_depth: false, + // Requires Features::CONSERVATIVE_RASTERIZATION + conservative: false, + }, + depth_stencil: depth_format.map(|format| wgpu::DepthStencilState { + format, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + // If the pipeline will be used with a multiview render pass, this + // indicates how many array layers the attachments will have. + multiview: None, + }) +} + +impl State { + async fn new(window: &Window) -> Self { + let size = window.inner_size(); + + // The instance is a handle to our GPU + // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU + let instance = wgpu::Instance::new(wgpu::Backends::all()); + let surface = unsafe { instance.create_surface(window) }; + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + // WebGL doesn't support all of wgpu's features, so if + // we're building for the web we'll have to disable some. + limits: if cfg!(target_arch = "wasm32") { + wgpu::Limits::downlevel_webgl2_defaults() + } else { + wgpu::Limits::default() + }, + }, + None, // Trace path + ) + .await + .unwrap(); + + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface.get_preferred_format(&adapter).unwrap(), + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + }; + + surface.configure(&device, &config); + + let texture_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + // normal map + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + label: Some("texture_bind_group_layout"), + }); + + // UPDATED! + let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0)); + let projection = + camera::Projection::new(config.width, config.height, cgmath::Deg(45.0), 0.1, 100.0); + let camera_controller = camera::CameraController::new(4.0, 0.4); + + let mut camera_uniform = CameraUniform::new(); + camera_uniform.update_view_proj(&camera, &projection); + + let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + const SPACE_BETWEEN: f32 = 3.0; + let iter = { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + (0..NUM_INSTANCES_PER_ROW) + .into_iter() + } else { + (0..NUM_INSTANCES_PER_ROW) + .into_par_iter() + } + } + }; + let instances = iter + .clone() + .flat_map(|z| { + // UPDATED! + iter.clone().map(move |x| { + let x = SPACE_BETWEEN * (x as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0); + let z = SPACE_BETWEEN * (z as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0); + + let position = cgmath::Vector3 { x, y: 0.0, z }; + + let rotation = if position.is_zero() { + cgmath::Quaternion::from_axis_angle( + cgmath::Vector3::unit_z(), + cgmath::Deg(0.0), + ) + } else { + cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0)) + }; + + Instance { position, rotation } + }) + }) + .collect::>(); + + let instance_data = instances.iter().map(Instance::to_raw).collect::>(); + let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Instance Buffer"), + contents: bytemuck::cast_slice(&instance_data), + usage: wgpu::BufferUsages::VERTEX, + }); + + let camera_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("camera_bind_group_layout"), + }); + + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + label: Some("camera_bind_group"), + }); + + let obj_model = + resources::load_model("cube.obj", &device, &queue, &texture_bind_group_layout) + .await + .unwrap(); + + let light_uniform = LightUniform { + position: [2.0, 2.0, 2.0], + _padding: 0, + color: [1.0, 1.0, 1.0], + _padding2: 0, + }; + + let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Light VB"), + contents: bytemuck::cast_slice(&[light_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let light_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: None, + }); + + let light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &light_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: light_buffer.as_entire_binding(), + }], + label: None, + }); + + let depth_texture = + texture::Texture::create_depth_texture(&device, &config, "depth_texture"); + + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[ + &texture_bind_group_layout, + &camera_bind_group_layout, + &light_bind_group_layout, + ], + push_constant_ranges: &[], + }); + + let render_pipeline = { + let shader = wgpu::ShaderModuleDescriptor { + label: Some("Normal Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }; + create_render_pipeline( + &device, + &render_pipeline_layout, + config.format, + Some(texture::Texture::DEPTH_FORMAT), + &[model::ModelVertex::desc(), InstanceRaw::desc()], + shader, + ) + }; + + let light_render_pipeline = { + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Light Pipeline Layout"), + bind_group_layouts: &[&camera_bind_group_layout, &light_bind_group_layout], + push_constant_ranges: &[], + }); + let shader = wgpu::ShaderModuleDescriptor { + label: Some("Light Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("light.wgsl").into()), + }; + create_render_pipeline( + &device, + &layout, + config.format, + Some(texture::Texture::DEPTH_FORMAT), + &[model::ModelVertex::desc()], + shader, + ) + }; + + let debug_material = { + let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png"); + let normal_bytes = include_bytes!("../res/cobble-normal.png"); + + let diffuse_texture = texture::Texture::from_bytes( + &device, + &queue, + diffuse_bytes, + "res/alt-diffuse.png", + false, + ) + .unwrap(); + let normal_texture = texture::Texture::from_bytes( + &device, + &queue, + normal_bytes, + "res/alt-normal.png", + true, + ) + .unwrap(); + + model::Material::new( + &device, + "alt-material", + diffuse_texture, + normal_texture, + &texture_bind_group_layout, + ) + }; + + Self { + surface, + device, + queue, + config, + render_pipeline, + obj_model, + camera, + projection, + camera_controller, + camera_buffer, + camera_bind_group, + camera_uniform, + instances, + instance_buffer, + depth_texture, + size, + light_uniform, + light_buffer, + light_bind_group, + light_render_pipeline, + #[allow(dead_code)] + debug_material, + mouse_pressed: false, + } + } + + fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.projection.resize(new_size.width, new_size.height); + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + self.depth_texture = + texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture"); + } + } + + fn input(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(key), + state, + .. + }, + .. + } => self.camera_controller.process_keyboard(*key, *state), + WindowEvent::MouseWheel { delta, .. } => { + self.camera_controller.process_scroll(delta); + true + } + WindowEvent::MouseInput { + button: MouseButton::Left, + state, + .. + } => { + self.mouse_pressed = *state == ElementState::Pressed; + true + } + _ => false, + } + } + + fn update(&mut self, dt: instant::Duration) { + self.camera_controller.update_camera(&mut self.camera, dt); + self.camera_uniform + .update_view_proj(&self.camera, &self.projection); + self.queue.write_buffer( + &self.camera_buffer, + 0, + bytemuck::cast_slice(&[self.camera_uniform]), + ); + + // Update the light + let old_position: cgmath::Vector3<_> = self.light_uniform.position.into(); + self.light_uniform.position = + (cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) + * old_position) + .into(); + self.queue.write_buffer( + &self.light_buffer, + 0, + bytemuck::cast_slice(&[self.light_uniform]), + ); + } + + fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + let output = self.surface.get_current_texture()?; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: true, + }, + }], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), + }); + + render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); + render_pass.set_pipeline(&self.light_render_pipeline); + render_pass.draw_light_model( + &self.obj_model, + &self.camera_bind_group, + &self.light_bind_group, + ); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.draw_model_instanced( + &self.obj_model, + 0..self.instances.len() as u32, + &self.camera_bind_group, + &self.light_bind_group, + ); + } + self.queue.submit(iter::once(encoder.finish())); + output.present(); + + Ok(()) + } +} + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] +pub async fn run() { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init_with_level(log::Level::Info).expect("Could't initialize logger"); + } else { + env_logger::init(); + } + } + + let event_loop = EventLoop::new(); + let title = env!("CARGO_PKG_NAME"); + let window = winit::window::WindowBuilder::new() + .with_title(title) + .build(&event_loop) + .unwrap(); + + #[cfg(target_arch = "wasm32")] + { + // Winit prevents sizing with CSS, so we have to set + // the size manually when on web. + use winit::dpi::PhysicalSize; + window.set_inner_size(PhysicalSize::new(450, 400)); + + use winit::platform::web::WindowExtWebSys; + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| { + let dst = doc.get_element_by_id("wasm-example")?; + let canvas = web_sys::Element::from(window.canvas()); + dst.append_child(&canvas).ok()?; + Some(()) + }) + .expect("Couldn't append canvas to document body."); + } + + let mut state = State::new(&window).await; // NEW! + let mut last_render_time = instant::Instant::now(); + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Poll; + match event { + Event::MainEventsCleared => window.request_redraw(), + // NEW! + Event::DeviceEvent { + event: DeviceEvent::MouseMotion{ delta, }, + .. // We're not using device_id currently + } => if state.mouse_pressed { + state.camera_controller.process_mouse(delta.0, delta.1) + } + // UPDATED! + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() && !state.input(event) => { + match event { + #[cfg(not(target_arch="wasm32"))] + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + } => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(physical_size) => { + state.resize(*physical_size); + } + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + state.resize(**new_inner_size); + } + _ => {} + } + } + Event::RedrawRequested(window_id) if window_id == window.id() => { + let now = instant::Instant::now(); + let dt = now - last_render_time; + last_render_time = now; + state.update(dt); + match state.render() { + Ok(_) => {} + // Reconfigure the surface if it's lost or outdated + Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size), + // The system is out of memory, we should probably quit + Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, + // We're ignoring timeouts + Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"), + } + } + _ => {} + } + }); +} diff --git a/src/light.frag b/src/light.frag new file mode 100644 index 0000000..a7ae2f2 --- /dev/null +++ b/src/light.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location=0) in vec3 v_color; +layout(location=0) out vec4 f_color; + +void main() { + f_color = vec4(v_color, 1.0); +} \ No newline at end of file diff --git a/src/light.frag.spv b/src/light.frag.spv new file mode 100644 index 0000000..199e2f4 Binary files /dev/null and b/src/light.frag.spv differ diff --git a/src/light.vert b/src/light.vert new file mode 100644 index 0000000..101505e --- /dev/null +++ b/src/light.vert @@ -0,0 +1,27 @@ +#version 450 + +layout(location=0) in vec3 a_position; + +layout(location=0) out vec3 v_color; + +layout(set=0, binding=0) +uniform Camera { + vec3 u_view_position; + mat4 u_view_proj; +}; + +layout(set=1, binding=0) +uniform Light { + vec3 u_position; + vec3 u_color; +}; + +// Let's keep our light smaller than our other objects +float scale = 0.25; + +void main() { + vec3 v_position = a_position * scale + u_position; + gl_Position = u_view_proj * vec4(v_position, 1); + + v_color = u_color; +} diff --git a/src/light.vert.spv b/src/light.vert.spv new file mode 100644 index 0000000..7b8ff81 Binary files /dev/null and b/src/light.vert.spv differ diff --git a/src/light.wgsl b/src/light.wgsl new file mode 100644 index 0000000..2f8f176 --- /dev/null +++ b/src/light.wgsl @@ -0,0 +1,42 @@ +// Vertex shader + +struct Camera { + view_pos: vec4; + view_proj: mat4x4; +}; +[[group(0), binding(0)]] +var camera: Camera; + +struct Light { + position: vec3; + color: vec3; +}; +[[group(1), binding(0)]] +var light: Light; + +struct VertexInput { + [[location(0)]] position: vec3; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; + [[location(0)]] color: vec3; +}; + +[[stage(vertex)]] +fn vs_main( + model: VertexInput, +) -> VertexOutput { + let scale = 0.25; + var out: VertexOutput; + out.clip_position = camera.view_proj * vec4(model.position * scale + light.position, 1.0); + out.color = light.color; + return out; +} + +// Fragment shader + +[[stage(fragment)]] +fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { + return vec4(in.color, 1.0); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ec2220c..66a139e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,5 @@ -mod state; -pub use state::State; - -pub mod input; -pub mod meshs; -pub mod render; - -use simplelog::{ColorChoice, Config, LevelFilter, TermLogger, TerminalMode}; - -fn main() { - if let Err(err) = TermLogger::init( - LevelFilter::Info, - Config::default(), - TerminalMode::Mixed, - ColorChoice::Auto, - ) { - println!("Failed to start logger : {}", err); - } - - let engine = render::Window::new("Test 123"); - pollster::block_on(engine.run()); -} +use tuto1::run; + +fn main() { + async_std::task::block_on(run()); +} \ No newline at end of file diff --git a/src/meshs/default_mesh.rs b/src/meshs/default_mesh.rs deleted file mode 100644 index fe1b501..0000000 --- a/src/meshs/default_mesh.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::sync::Arc; - -use cgmath::prelude::*; -use winit::event::{WindowEvent, KeyboardInput, VirtualKeyCode, ElementState}; - -use crate::{render::{Mesh, Renderable, Vertex, Instance}, input::Controllable}; - -const VERTICES: &[Vertex] = &[ - Vertex { - position: [-0.0868241, 0.49240386, 0.0], - tex_coords: [0.4131759, 0.00759614], - }, // A - Vertex { - position: [-0.49513406, 0.06958647, 0.0], - tex_coords: [0.0048659444, 0.43041354], - }, // B - Vertex { - position: [-0.21918549, -0.44939706, 0.0], - tex_coords: [0.28081453, 0.949397], - }, // C - Vertex { - position: [0.35966998, -0.3473291, 0.0], - tex_coords: [0.85967, 0.84732914], - }, // D - Vertex { - position: [0.44147372, 0.2347359, 0.0], - tex_coords: [0.9414737, 0.2652641], - }, // E -]; - -const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4]; - -const NUM_INSTANCES_PER_ROW: u32 = 10; -const INSTANCE_DISPLACEMENT: cgmath::Vector3 = cgmath::Vector3::new( - NUM_INSTANCES_PER_ROW as f32 * 0.5, - 0.0, - NUM_INSTANCES_PER_ROW as f32 * 0.5, -); - -const FRAME_TIME: f32 = 1.0 / 60.0; -const ROTATION_SPEED: f32 = std::f32::consts::PI * FRAME_TIME * 0.5; - -pub struct DefaultMesh { - mesh: Mesh, - toggle: bool, - texture1_bind_group: Arc, - texture2_bind_group: Arc, -} - -impl DefaultMesh { - pub fn new(texture1_bind_group: wgpu::BindGroup, texture2_bind_group: wgpu::BindGroup) -> Self { - let texture1_bind_group = Arc::new(texture1_bind_group); - let texture2_bind_group = Arc::new(texture2_bind_group); - - let instances = (0..NUM_INSTANCES_PER_ROW) - .flat_map(|z| { - (0..NUM_INSTANCES_PER_ROW).map(move |x| { - let position = cgmath::Vector3 { - x: x as f32, - y: 0.0, - z: z as f32, - } - INSTANCE_DISPLACEMENT; - - let rotation = if position.is_zero() { - // this is needed so an object at (0, 0, 0) won't get scaled to zero - // as Quaternions can effect scale if they're not created correctly - cgmath::Quaternion::from_axis_angle( - cgmath::Vector3::unit_z(), - cgmath::Deg(0.0), - ) - } else { - cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0)) - }; - - Instance { position, rotation } - }) - }) - .collect::>(); - - let mesh = Mesh { - vertex_array: VERTICES.to_vec(), - index_array: INDICES.to_vec(), - num_indices: INDICES.len() as u32, - instance_array: instances, - texture_bind_group: Some(texture1_bind_group.clone()), - vertex_buffer: None, - index_buffer: None, - instance_buffer: None, - }; - DefaultMesh { - mesh, - toggle: false, - texture1_bind_group, - texture2_bind_group, - } - } - - pub fn toggle(&mut self, toggle: bool) { - self.toggle = toggle; - if !self.toggle { - self.mesh.texture_bind_group = Some(self.texture1_bind_group.clone()); - } else { - self.mesh.texture_bind_group = Some(self.texture2_bind_group.clone()); - } - } -} - -impl Renderable for DefaultMesh { - fn initialize(&mut self, device: &wgpu::Device) { - self.mesh.initialize(device); - } - - fn update_instances(&mut self, device: &wgpu::Queue) { - for instance in self.mesh.instance_array.iter_mut() { - let amount = cgmath::Quaternion::from_angle_y(cgmath::Rad(ROTATION_SPEED)); - let current = instance.rotation; - instance.rotation = amount * current; - } - self.mesh.update_instances(device); - } - - fn prepare<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - self.mesh.prepare(render_pass); - } - - fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - self.mesh.draw(render_pass); - } -} - -impl Controllable for DefaultMesh { - fn process_events(&mut self, event: &winit::event::WindowEvent) -> bool { - match event { - WindowEvent::KeyboardInput { - input: - KeyboardInput { - state, - virtual_keycode: Some(keycode), - .. - }, - .. - } => { - let is_pressed = *state == ElementState::Pressed; - match keycode { - VirtualKeyCode::Space => { - self.toggle(is_pressed); - true - } - _ => false, - } - } - _ => false, - } - } -} \ No newline at end of file diff --git a/src/meshs/mod.rs b/src/meshs/mod.rs deleted file mode 100644 index 8593de2..0000000 --- a/src/meshs/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod default_mesh; -pub use default_mesh::DefaultMesh; \ No newline at end of file diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..12df125 --- /dev/null +++ b/src/model.rs @@ -0,0 +1,316 @@ +use std::ops::Range; + +use crate::texture; + +pub trait Vertex { + fn desc<'a>() -> wgpu::VertexBufferLayout<'a>; +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +pub struct ModelVertex { + pub position: [f32; 3], + pub tex_coords: [f32; 2], + pub normal: [f32; 3], + pub tangent: [f32; 3], + pub bitangent: [f32; 3], +} + +impl Vertex for ModelVertex { + fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + use std::mem; + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x2, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress, + shader_location: 2, + format: wgpu::VertexFormat::Float32x3, + }, + // Tangent and bitangent + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, + shader_location: 3, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress, + shader_location: 4, + format: wgpu::VertexFormat::Float32x3, + }, + ], + } + } +} + +pub struct Material { + pub name: String, + pub diffuse_texture: texture::Texture, + pub normal_texture: texture::Texture, + pub bind_group: wgpu::BindGroup, +} + +impl Material { + pub fn new( + device: &wgpu::Device, + name: &str, + diffuse_texture: texture::Texture, + normal_texture: texture::Texture, + layout: &wgpu::BindGroupLayout, + ) -> Self { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&normal_texture.view), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::Sampler(&normal_texture.sampler), + }, + ], + label: Some(name), + }); + + Self { + name: String::from(name), + diffuse_texture, + normal_texture, + bind_group, + } + } +} + +pub struct Mesh { + pub name: String, + pub vertex_buffer: wgpu::Buffer, + pub index_buffer: wgpu::Buffer, + pub num_elements: u32, + pub material: usize, +} + +pub struct Model { + pub meshes: Vec, + pub materials: Vec, +} + +pub trait DrawModel<'a> { + fn draw_mesh( + &mut self, + mesh: &'a Mesh, + material: &'a Material, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_mesh_instanced( + &mut self, + mesh: &'a Mesh, + material: &'a Material, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + + fn draw_model( + &mut self, + model: &'a Model, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_model_instanced( + &mut self, + model: &'a Model, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_model_instanced_with_material( + &mut self, + model: &'a Model, + material: &'a Material, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); +} + +impl<'a, 'b> DrawModel<'b> for wgpu::RenderPass<'a> +where + 'b: 'a, +{ + fn draw_mesh( + &mut self, + mesh: &'b Mesh, + material: &'b Material, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_mesh_instanced(mesh, material, 0..1, camera_bind_group, light_bind_group); + } + + fn draw_mesh_instanced( + &mut self, + mesh: &'b Mesh, + material: &'b Material, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); + self.set_bind_group(0, &material.bind_group, &[]); + self.set_bind_group(1, camera_bind_group, &[]); + self.set_bind_group(2, light_bind_group, &[]); + self.draw_indexed(0..mesh.num_elements, 0, instances); + } + + fn draw_model( + &mut self, + model: &'b Model, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_model_instanced(model, 0..1, camera_bind_group, light_bind_group); + } + + fn draw_model_instanced( + &mut self, + model: &'b Model, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + for mesh in &model.meshes { + let material = &model.materials[mesh.material]; + self.draw_mesh_instanced( + mesh, + material, + instances.clone(), + camera_bind_group, + light_bind_group, + ); + } + } + + fn draw_model_instanced_with_material( + &mut self, + model: &'b Model, + material: &'b Material, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + for mesh in &model.meshes { + self.draw_mesh_instanced( + mesh, + material, + instances.clone(), + camera_bind_group, + light_bind_group, + ); + } + } +} + +pub trait DrawLight<'a> { + fn draw_light_mesh( + &mut self, + mesh: &'a Mesh, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_light_mesh_instanced( + &mut self, + mesh: &'a Mesh, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + + fn draw_light_model( + &mut self, + model: &'a Model, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_light_model_instanced( + &mut self, + model: &'a Model, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); +} + +impl<'a, 'b> DrawLight<'b> for wgpu::RenderPass<'a> +where + 'b: 'a, +{ + fn draw_light_mesh( + &mut self, + mesh: &'b Mesh, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_light_mesh_instanced(mesh, 0..1, camera_bind_group, light_bind_group); + } + + fn draw_light_mesh_instanced( + &mut self, + mesh: &'b Mesh, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); + self.set_bind_group(0, camera_bind_group, &[]); + self.set_bind_group(1, light_bind_group, &[]); + self.draw_indexed(0..mesh.num_elements, 0, instances); + } + + fn draw_light_model( + &mut self, + model: &'b Model, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_light_model_instanced(model, 0..1, camera_bind_group, light_bind_group); + } + fn draw_light_model_instanced( + &mut self, + model: &'b Model, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + for mesh in &model.meshes { + self.draw_light_mesh_instanced( + mesh, + instances.clone(), + camera_bind_group, + light_bind_group, + ); + } + } +} diff --git a/src/render/camera.rs b/src/render/camera.rs deleted file mode 100644 index 0cec10a..0000000 --- a/src/render/camera.rs +++ /dev/null @@ -1,212 +0,0 @@ -use wgpu::util::DeviceExt; -use winit::event::{ElementState, KeyboardInput, VirtualKeyCode, WindowEvent}; - -use crate::input::Controllable; - -use super::Renderable; - -#[rustfmt::skip] -pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.0, - 0.0, 0.0, 0.5, 1.0, -); - -// We need this for Rust to store our data correctly for the shaders -#[repr(C)] -// This is so we can store this in a buffer -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)] -struct CameraUniform { - // We can't use cgmath with bytemuck directly so we'll have - // to convert the Matrix4 into a 4x4 f32 array - view_proj: [[f32; 4]; 4], -} - -pub struct Camera { - eye: cgmath::Point3, - target: cgmath::Point3, - up: cgmath::Vector3, - aspect: f32, - fovy: f32, - znear: f32, - zfar: f32, - controller: CameraController, - uniform: CameraUniform, - bind_group: Option, - bind_group_layout: Option, - buffer: Option, -} - -impl Camera { - pub fn new(width: f32, height: f32, speed: f32) -> Self { - Self { - eye: (0.0, 1.0, 2.0).into(), - target: (0.0, 0.0, 0.0).into(), - up: cgmath::Vector3::unit_y(), - aspect: width / height, - fovy: 45.0, - znear: 0.1, - zfar: 100.0, - controller: CameraController::new(speed), - bind_group: None, - bind_group_layout: None, - uniform: CameraUniform::default(), - buffer: None, - } - } - - fn update_uniform(&mut self) { - let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up); - let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); - - self.uniform.view_proj = (OPENGL_TO_WGPU_MATRIX * proj * view).into(); - } - - pub fn get_bind_group_layout(&self) -> &wgpu::BindGroupLayout { - &self.bind_group_layout.as_ref().unwrap() - } - - fn update_camera(&mut self) { - use cgmath::InnerSpace; - let forward = self.target - self.eye; - let forward_norm = forward.normalize(); - let forward_mag = forward.magnitude(); - - if self.controller.is_forward_pressed && forward_mag > self.controller.speed { - self.eye += forward_norm * self.controller.speed; - } - if self.controller.is_backward_pressed { - self.eye -= forward_norm * self.controller.speed; - } - - let right = forward_norm.cross(self.up); - - let forward = self.target - self.eye; - let forward_mag = forward.magnitude(); - - if self.controller.is_right_pressed { - self.eye = self.target - (forward + right * self.controller.speed).normalize() * forward_mag; - } - if self.controller.is_left_pressed { - self.eye = self.target - (forward - right * self.controller.speed).normalize() * forward_mag; - } - } -} - -impl Renderable for Camera { - fn initialize(&mut self, device: &wgpu::Device) { - self.update_uniform(); - - self.buffer = Some( - device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[self.uniform]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }), - ); - - self.bind_group_layout = Some(device.create_bind_group_layout( - &wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("camera_bind_group_layout"), - }, - )); - - self.bind_group = Some(device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &self.bind_group_layout.as_ref().unwrap(), - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: self.buffer.as_ref().unwrap().as_entire_binding(), - }], - label: Some("camera_bind_group"), - })); - } - - fn update_instances(&mut self, queue: &wgpu::Queue) { - self.update_camera(); - self.update_uniform(); - queue.write_buffer( - &self.buffer.as_ref().unwrap(), - 0, - bytemuck::cast_slice(&[self.uniform]), - ); - } - - fn prepare<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - render_pass.set_bind_group(1, &self.bind_group.as_ref().unwrap(), &[]); - } - - fn draw<'a>(&'a self, _render_pass: &mut wgpu::RenderPass<'a>) { } -} - -impl Controllable for Camera { - fn process_events(&mut self, event: &WindowEvent) -> bool { - self.controller.process_events(event) - } -} - -struct CameraController { - speed: f32, - is_forward_pressed: bool, - is_backward_pressed: bool, - is_left_pressed: bool, - is_right_pressed: bool, -} - -impl CameraController { - pub fn new(speed: f32) -> Self { - Self { - speed, - is_forward_pressed: false, - is_backward_pressed: false, - is_left_pressed: false, - is_right_pressed: false, - } - } - - pub fn process_events(&mut self, event: &WindowEvent) -> bool { - match event { - WindowEvent::KeyboardInput { - input: - KeyboardInput { - state, - virtual_keycode: Some(keycode), - .. - }, - .. - } => { - let is_pressed = *state == ElementState::Pressed; - match keycode { - VirtualKeyCode::W | VirtualKeyCode::Up => { - self.is_forward_pressed = is_pressed; - true - } - VirtualKeyCode::A | VirtualKeyCode::Left => { - self.is_left_pressed = is_pressed; - true - } - VirtualKeyCode::S | VirtualKeyCode::Down => { - self.is_backward_pressed = is_pressed; - true - } - VirtualKeyCode::D | VirtualKeyCode::Right => { - self.is_right_pressed = is_pressed; - true - } - _ => false, - } - } - _ => false, - } - } -} \ No newline at end of file diff --git a/src/render/instance.rs b/src/render/instance.rs deleted file mode 100644 index 46f789d..0000000 --- a/src/render/instance.rs +++ /dev/null @@ -1,60 +0,0 @@ -pub struct Instance { - pub position: cgmath::Vector3, - pub rotation: cgmath::Quaternion, -} - -#[repr(C)] -#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct InstanceRaw { - model: [[f32; 4]; 4], -} - -impl Instance { - pub fn to_raw(&self) -> InstanceRaw { - InstanceRaw { - model: (cgmath::Matrix4::from_translation(self.position) - * cgmath::Matrix4::from(self.rotation)) - .into(), - } - } -} - -impl InstanceRaw { - pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - use std::mem; - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - // We need to switch from using a step mode of Vertex to Instance - // This means that our shaders will only change to use the next - // instance when the shader starts processing a new instance - step_mode: wgpu::VertexStepMode::Instance, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - // While our vertex shader only uses locations 0, and 1 now, in later tutorials we'll - // be using 2, 3, and 4, for Vertex. We'll start at slot 5 not conflict with them later - shader_location: 5, - format: wgpu::VertexFormat::Float32x4, - }, - // A mat4 takes up 4 vertex slots as it is technically 4 vec4s. We need to define a slot - // for each vec4. We'll have to reassemble the mat4 in - // the shader. - wgpu::VertexAttribute { - offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress, - shader_location: 6, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, - shader_location: 7, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress, - shader_location: 8, - format: wgpu::VertexFormat::Float32x4, - }, - ], - } - } -} diff --git a/src/render/mesh.rs b/src/render/mesh.rs deleted file mode 100644 index 9d8a959..0000000 --- a/src/render/mesh.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::Arc; - -use wgpu::{Device, util::DeviceExt, Queue}; - -use super::{Vertex, Renderable, Instance}; - -pub struct Mesh { - pub vertex_array: Vec, - pub index_array: Vec, - pub num_indices: u32, - pub instance_array: Vec, - pub texture_bind_group: Option>, - pub vertex_buffer: Option, - pub index_buffer: Option, - pub instance_buffer: Option, -} - -impl Renderable for Mesh { - fn initialize(&mut self, device: &Device) { - self.vertex_buffer = Some(device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&self.vertex_array), - usage: wgpu::BufferUsages::VERTEX, - })); - - self.index_buffer = Some(device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(&self.index_array), - usage: wgpu::BufferUsages::INDEX, - })); - self.num_indices = self.index_array.len() as u32; - - let instance_data = self.instance_array - .iter() - .map(Instance::to_raw) - .collect::>(); - self.instance_buffer = Some(device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Instance Buffer"), - contents: bytemuck::cast_slice(&instance_data), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - })); - } - - fn update_instances(&mut self, queue: &Queue) { - let instance_data = self - .instance_array - .iter() - .map(Instance::to_raw) - .collect::>(); - queue.write_buffer( - &self.instance_buffer.as_ref().unwrap(), - 0, - bytemuck::cast_slice(&instance_data), - ); - } - - fn prepare<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - render_pass.set_bind_group(0, &self.texture_bind_group.as_ref().unwrap(), &[]); - render_pass.set_vertex_buffer(0, self.vertex_buffer.as_ref().unwrap().slice(..)); - render_pass.set_vertex_buffer(1, self.instance_buffer.as_ref().unwrap().slice(..)); - render_pass.set_index_buffer(self.index_buffer.as_ref().unwrap().slice(..), wgpu::IndexFormat::Uint16); - } - - fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - render_pass.draw_indexed(0..self.num_indices as _, 0, 0..self.instance_array.len() as _); - } -} \ No newline at end of file diff --git a/src/render/mod.rs b/src/render/mod.rs deleted file mode 100644 index 1b60676..0000000 --- a/src/render/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -mod vertex; -pub use vertex::Vertex; - -mod camera; -pub use camera::Camera; - -mod texture; -pub use texture::{Texture, TextureManager}; - -mod instance; -pub use instance::{ - Instance, InstanceRaw -}; -use wgpu::{Device, Queue}; - -mod mesh; -pub use mesh::Mesh; - -mod window; -pub use window::Window; - -mod pipelines; - -pub trait Renderable { - fn initialize(&mut self, device: &Device); - fn update_instances(&mut self, queue: &Queue); - fn prepare<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>); - fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>); -} \ No newline at end of file diff --git a/src/render/pipelines/3d.rs b/src/render/pipelines/3d.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/render/pipelines/mod.rs b/src/render/pipelines/mod.rs deleted file mode 100644 index 46521a8..0000000 --- a/src/render/pipelines/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -use wgpu::{Device, Queue}; - -use super::Renderable; - -pub trait Processable { - fn initialize(&mut self, device: &Device, queue: &Queue, renderable_entities: Vec>); - fn resize(&mut self, new_size: winit::dpi::PhysicalSize, renderable_entities: Vec>); - fn render(&mut self, renderable_entities: Vec>) -> Result<(), wgpu::SurfaceError>; -} \ No newline at end of file diff --git a/src/render/texture/mod.rs b/src/render/texture/mod.rs deleted file mode 100644 index eaec782..0000000 --- a/src/render/texture/mod.rs +++ /dev/null @@ -1,66 +0,0 @@ -mod texture; -pub use texture::Texture; -use wgpu::{BindGroup, Device, Queue}; - -pub struct TextureManager { - texture_bind_group_layout: wgpu::BindGroupLayout, -} - -impl TextureManager { - pub fn new(device: &Device) -> Self { - let texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); - Self { - texture_bind_group_layout, - } - } - - pub fn create_texture_from_bytes( - &self, - device: &Device, - queue: &Queue, - bytes: &[u8], - label: &str, - ) -> BindGroup { - let diffuse_texture = Texture::from_bytes(&device, &queue, bytes, label).unwrap(); - - device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &self.texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), - }, - ], - label: Some(&format!("diffuse_bind_group_{}", label)), - }) - } - - pub fn get_texture_bind_group_layout(&self) -> &wgpu::BindGroupLayout { - &self.texture_bind_group_layout - } -} diff --git a/src/render/vertex.rs b/src/render/vertex.rs deleted file mode 100644 index fca62c5..0000000 --- a/src/render/vertex.rs +++ /dev/null @@ -1,41 +0,0 @@ -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Vertex { - pub position: [f32; 3], - pub tex_coords: [f32; 2], -} - -impl Vertex { - - const ATTRIBS: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2]; - - pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - use std::mem; - - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBS, - } - } - - // pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - // wgpu::VertexBufferLayout { - // array_stride: std::mem::size_of::() as wgpu::BufferAddress, - // step_mode: wgpu::VertexStepMode::Vertex, - // attributes: &[ - // wgpu::VertexAttribute { - // offset: 0, - // shader_location: 0, - // format: wgpu::VertexFormat::Float32x3, - // }, - // wgpu::VertexAttribute { - // offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, - // shader_location: 1, - // format: wgpu::VertexFormat::Float32x2, - // } - // ] - // } - // } -} \ No newline at end of file diff --git a/src/render/window.rs b/src/render/window.rs deleted file mode 100644 index 4edb999..0000000 --- a/src/render/window.rs +++ /dev/null @@ -1,74 +0,0 @@ -use winit::{ - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; - -pub struct Window { - title: &'static str, -} - -impl Window { - pub fn new(title: &'static str) -> Self { - Self { title } - } - - pub async fn run(self) { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title(self.title) - .build(&event_loop) - .unwrap(); - - let mut state = crate::State::new(&window).await; - - event_loop.run( - move |event: Event<()>, _, control_flow: &mut ControlFlow| match event { - Event::WindowEvent { - ref event, - window_id, - } if window_id == window.id() => { - if !state.input(&event) { - match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { input, .. } => match input { - KeyboardInput { - state: ElementState::Pressed, - virtual_keycode: Some(VirtualKeyCode::Escape), - .. - } => *control_flow = ControlFlow::Exit, - _ => {} - }, - WindowEvent::Resized(physical_size) => { - state.resize(*physical_size); - } - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - // new_inner_size is &&mut so we have to dereference it twice - state.resize(**new_inner_size); - } - _ => {} - } - } - } - Event::RedrawRequested(window_id) if window_id == window.id() => { - state.update(); - match state.render() { - Ok(_) => {} - // Reconfigure the surface if lost - Err(wgpu::SurfaceError::Lost) => state.resize(state.size), - // The system is out of memory, we should probably quit - Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, - // All other errors (Outdated, Timeout) should be resolved by the next frame - Err(e) => eprintln!("{:?}", e), - } - } - Event::MainEventsCleared => { - // RedrawRequested will only trigger once, unless we manually - // request it. - window.request_redraw(); - } - _ => {} - }, - ); - } -} diff --git a/src/resources.rs b/src/resources.rs new file mode 100644 index 0000000..9748884 --- /dev/null +++ b/src/resources.rs @@ -0,0 +1,219 @@ +use std::io::{BufReader, Cursor}; + +use cfg_if::cfg_if; +use wgpu::util::DeviceExt; + +use crate::{model, texture}; + +#[cfg(target_arch = "wasm32")] +fn format_url(file_name: &str) -> reqwest::Url { + let window = web_sys::window().unwrap(); + let location = window.location(); + let base = reqwest::Url::parse(&format!( + "{}/{}/", + location.origin().unwrap(), + option_env!("RES_PATH").unwrap_or("res"), + )).unwrap(); + base.join(file_name).unwrap() +} + +pub async fn load_string(file_name: &str) -> anyhow::Result { + cfg_if! { + if #[cfg(target_arch = "wasm32")] { + let url = format_url(file_name); + let txt = reqwest::get(url) + .await? + .text() + .await?; + } else { + let path = std::path::Path::new(env!("OUT_DIR")) + .join("res") + .join(file_name); + let txt = std::fs::read_to_string(path)?; + } + } + + Ok(txt) +} + +pub async fn load_binary(file_name: &str) -> anyhow::Result> { + cfg_if! { + if #[cfg(target_arch = "wasm32")] { + let url = format_url(file_name); + let data = reqwest::get(url) + .await? + .bytes() + .await? + .to_vec(); + } else { + let path = std::path::Path::new(env!("OUT_DIR")) + .join("res") + .join(file_name); + let data = std::fs::read(path)?; + } + } + + Ok(data) +} + +pub async fn load_texture( + file_name: &str, + is_normal_map: bool, + device: &wgpu::Device, + queue: &wgpu::Queue, +) -> anyhow::Result { + let data = load_binary(file_name).await?; + texture::Texture::from_bytes(device, queue, &data, file_name, is_normal_map) +} + +pub async fn load_model( + file_name: &str, + device: &wgpu::Device, + queue: &wgpu::Queue, + layout: &wgpu::BindGroupLayout, +) -> anyhow::Result { + let obj_text = load_string(file_name).await?; + let obj_cursor = Cursor::new(obj_text); + let mut obj_reader = BufReader::new(obj_cursor); + + let (models, obj_materials) = tobj::load_obj_buf_async( + &mut obj_reader, + &tobj::LoadOptions { + triangulate: true, + single_index: true, + ..Default::default() + }, + |p| async move { + let mat_text = load_string(&p).await.unwrap(); + tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text))) + }, + ) + .await?; + + let mut materials = Vec::new(); + for m in obj_materials? { + let diffuse_texture = load_texture(&m.diffuse_texture, false, device, queue).await?; + let normal_texture = load_texture(&m.normal_texture, true, device, queue).await?; + + materials.push(model::Material::new( + device, + &m.name, + diffuse_texture, + normal_texture, + layout, + )); + } + + let meshes = models + .into_iter() + .map(|m| { + let mut vertices = (0..m.mesh.positions.len() / 3) + .map(|i| model::ModelVertex { + position: [ + m.mesh.positions[i * 3], + m.mesh.positions[i * 3 + 1], + m.mesh.positions[i * 3 + 2], + ], + tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]], + normal: [ + m.mesh.normals[i * 3], + m.mesh.normals[i * 3 + 1], + m.mesh.normals[i * 3 + 2], + ], + // We'll calculate these later + tangent: [0.0; 3], + bitangent: [0.0; 3], + }) + .collect::>(); + + let indices = &m.mesh.indices; + let mut triangles_included = vec![0; vertices.len()]; + + // Calculate tangents and bitangets. We're going to + // use the triangles, so we need to loop through the + // indices in chunks of 3 + for c in indices.chunks(3) { + let v0 = vertices[c[0] as usize]; + let v1 = vertices[c[1] as usize]; + let v2 = vertices[c[2] as usize]; + + let pos0: cgmath::Vector3<_> = v0.position.into(); + let pos1: cgmath::Vector3<_> = v1.position.into(); + let pos2: cgmath::Vector3<_> = v2.position.into(); + + let uv0: cgmath::Vector2<_> = v0.tex_coords.into(); + let uv1: cgmath::Vector2<_> = v1.tex_coords.into(); + let uv2: cgmath::Vector2<_> = v2.tex_coords.into(); + + // Calculate the edges of the triangle + let delta_pos1 = pos1 - pos0; + let delta_pos2 = pos2 - pos0; + + // This will give us a direction to calculate the + // tangent and bitangent + let delta_uv1 = uv1 - uv0; + let delta_uv2 = uv2 - uv0; + + // Solving the following system of equations will + // give us the tangent and bitangent. + // delta_pos1 = delta_uv1.x * T + delta_u.y * B + // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B + // Luckily, the place I found this equation provided + // the solution! + let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); + let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; + // We flip the bitangent to enable right-handed normal + // maps with wgpu texture coordinate system + let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r; + + // We'll use the same tangent/bitangent for each vertex in the triangle + vertices[c[0] as usize].tangent = + (tangent + cgmath::Vector3::from(vertices[c[0] as usize].tangent)).into(); + vertices[c[1] as usize].tangent = + (tangent + cgmath::Vector3::from(vertices[c[1] as usize].tangent)).into(); + vertices[c[2] as usize].tangent = + (tangent + cgmath::Vector3::from(vertices[c[2] as usize].tangent)).into(); + vertices[c[0] as usize].bitangent = + (bitangent + cgmath::Vector3::from(vertices[c[0] as usize].bitangent)).into(); + vertices[c[1] as usize].bitangent = + (bitangent + cgmath::Vector3::from(vertices[c[1] as usize].bitangent)).into(); + vertices[c[2] as usize].bitangent = + (bitangent + cgmath::Vector3::from(vertices[c[2] as usize].bitangent)).into(); + + // Used to average the tangents/bitangents + triangles_included[c[0] as usize] += 1; + triangles_included[c[1] as usize] += 1; + triangles_included[c[2] as usize] += 1; + } + + // Average the tangents/bitangents + for (i, n) in triangles_included.into_iter().enumerate() { + let denom = 1.0 / n as f32; + let mut v = &mut vertices[i]; + v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into(); + v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); + } + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Vertex Buffer", file_name)), + contents: bytemuck::cast_slice(&vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Index Buffer", file_name)), + contents: bytemuck::cast_slice(&m.mesh.indices), + usage: wgpu::BufferUsages::INDEX, + }); + + model::Mesh { + name: file_name.to_string(), + vertex_buffer, + index_buffer, + num_elements: m.mesh.indices.len() as u32, + material: m.mesh.material_id.unwrap_or(0), + } + }) + .collect::>(); + + Ok(model::Model { meshes, materials }) +} diff --git a/src/shader.frag b/src/shader.frag new file mode 100644 index 0000000..33137a0 --- /dev/null +++ b/src/shader.frag @@ -0,0 +1,40 @@ +#version 450 + +layout(location=0) in vec2 v_tex_coords; +layout(location=1) in vec3 v_position; // UPDATED! +layout(location=2) in vec3 v_light_position; // NEW! +layout(location=3) in vec3 v_view_position; // NEW! + +layout(location=0) out vec4 f_color; + +layout(set = 0, binding = 0) uniform texture2D t_diffuse; +layout(set = 0, binding = 1) uniform sampler s_diffuse; +layout(set = 0, binding = 2) uniform texture2D t_normal; +layout(set = 0, binding = 3) uniform sampler s_normal; + +layout(set = 2, binding = 0) uniform Light { + vec3 light_position; + vec3 light_color; +}; + +void main() { + vec4 object_color = texture(sampler2D(t_diffuse, s_diffuse), v_tex_coords); + vec4 object_normal = texture(sampler2D(t_normal, s_normal), v_tex_coords); + + float ambient_strength = 0.1; + vec3 ambient_color = light_color * ambient_strength; + + vec3 normal = normalize(object_normal.rgb * 2.0 - 1.0); // UPDATED! + vec3 light_dir = normalize(v_light_position - v_position); // UPDATED! + + float diffuse_strength = max(dot(normal, light_dir), 0.0); + vec3 diffuse_color = light_color * diffuse_strength; + + vec3 view_dir = normalize(v_view_position - v_position); // UPDATED! + vec3 half_dir = normalize(view_dir + light_dir); + float specular_strength = pow(max(dot(normal, half_dir), 0.0), 32); + vec3 specular_color = specular_strength * light_color; + + vec3 result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; + f_color = vec4(result, object_color.a); +} \ No newline at end of file diff --git a/src/shader.frag.spv b/src/shader.frag.spv new file mode 100644 index 0000000..45ccebc Binary files /dev/null and b/src/shader.frag.spv differ diff --git a/src/shader.vert b/src/shader.vert new file mode 100644 index 0000000..be49b4e --- /dev/null +++ b/src/shader.vert @@ -0,0 +1,61 @@ +#version 450 + +layout(location=0) in vec3 a_position; +layout(location=1) in vec2 a_tex_coords; +layout(location=2) in vec3 a_normal; +layout(location=3) in vec3 a_tangent; +layout(location=4) in vec3 a_bitangent; + +layout(location=0) out vec2 v_tex_coords; +layout(location=1) out vec3 v_position; // UPDATED! +layout(location=2) out vec3 v_light_position; // NEW! +layout(location=3) out vec3 v_view_position; // NEW! + +layout(set=1, binding=0) +uniform Camera { + vec3 u_view_position; + mat4 u_view_proj; +}; + +layout(location=5) in vec4 model_matrix_0; +layout(location=6) in vec4 model_matrix_1; +layout(location=7) in vec4 model_matrix_2; +layout(location=8) in vec4 model_matrix_3; + +// NEW! +layout(set=2, binding=0) uniform Light { + vec3 light_position; + vec3 light_color; +}; + +void main() { + mat4 model_matrix = mat4( + model_matrix_0, + model_matrix_1, + model_matrix_2, + model_matrix_3 + ); + v_tex_coords = a_tex_coords; + + mat3 normal_matrix = mat3(transpose(inverse(model_matrix))); + vec3 normal = normalize(normal_matrix * a_normal); + vec3 tangent = normalize(normal_matrix * a_tangent); + vec3 bitangent = normalize(normal_matrix * a_bitangent); + + // UDPATED! + mat3 tangent_matrix = transpose(mat3( + tangent, + bitangent, + normal + )); + + vec4 model_space = model_matrix * vec4(a_position, 1.0); + v_position = model_space.xyz; + + // NEW! + v_position = tangent_matrix * model_space.xyz; + v_light_position = tangent_matrix * light_position; + v_view_position = tangent_matrix * u_view_position; + + gl_Position = u_view_proj * model_space; +} \ No newline at end of file diff --git a/src/shader.vert.spv b/src/shader.vert.spv new file mode 100644 index 0000000..84e6c50 Binary files /dev/null and b/src/shader.vert.spv differ diff --git a/src/shader.wgsl b/src/shader.wgsl new file mode 100644 index 0000000..837ee23 --- /dev/null +++ b/src/shader.wgsl @@ -0,0 +1,115 @@ +// Vertex shader + +struct Camera { + view_pos: vec4; + view_proj: mat4x4; +}; +[[group(1), binding(0)]] +var camera: Camera; + +struct Light { + position: vec3; + color: vec3; +}; +[[group(2), binding(0)]] +var light: Light; + +struct VertexInput { + [[location(0)]] position: vec3; + [[location(1)]] tex_coords: vec2; + [[location(2)]] normal: vec3; + [[location(3)]] tangent: vec3; + [[location(4)]] bitangent: vec3; +}; +struct InstanceInput { + [[location(5)]] model_matrix_0: vec4; + [[location(6)]] model_matrix_1: vec4; + [[location(7)]] model_matrix_2: vec4; + [[location(8)]] model_matrix_3: vec4; + [[location(9)]] normal_matrix_0: vec3; + [[location(10)]] normal_matrix_1: vec3; + [[location(11)]] normal_matrix_2: vec3; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; + [[location(0)]] tex_coords: vec2; + [[location(1)]] tangent_position: vec3; + [[location(2)]] tangent_light_position: vec3; + [[location(3)]] tangent_view_position: vec3; +}; + +[[stage(vertex)]] +fn vs_main( + model: VertexInput, + instance: InstanceInput, +) -> VertexOutput { + let model_matrix = mat4x4( + instance.model_matrix_0, + instance.model_matrix_1, + instance.model_matrix_2, + instance.model_matrix_3, + ); + let normal_matrix = mat3x3( + instance.normal_matrix_0, + instance.normal_matrix_1, + instance.normal_matrix_2, + ); + + // Construct the tangent matrix + let world_normal = normalize(normal_matrix * model.normal); + let world_tangent = normalize(normal_matrix * model.tangent); + let world_bitangent = normalize(normal_matrix * model.bitangent); + let tangent_matrix = transpose(mat3x3( + world_tangent, + world_bitangent, + world_normal, + )); + + let world_position = model_matrix * vec4(model.position, 1.0); + + var out: VertexOutput; + out.clip_position = camera.view_proj * world_position; + out.tex_coords = model.tex_coords; + out.tangent_position = tangent_matrix * world_position.xyz; + out.tangent_view_position = tangent_matrix * camera.view_pos.xyz; + out.tangent_light_position = tangent_matrix * light.position; + return out; +} + +// Fragment shader + +[[group(0), binding(0)]] +var t_diffuse: texture_2d; +[[group(0), binding(1)]] +var s_diffuse: sampler; +[[group(0), binding(2)]] +var t_normal: texture_2d; +[[group(0), binding(3)]] +var s_normal: sampler; + +[[stage(fragment)]] +fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { + let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); + let object_normal: vec4 = textureSample(t_normal, s_normal, in.tex_coords); + + // We don't need (or want) much ambient light, so 0.1 is fine + let ambient_strength = 0.1; + let ambient_color = light.color * ambient_strength; + + // Create the lighting vectors + let tangent_normal = object_normal.xyz * 2.0 - 1.0; + let light_dir = normalize(in.tangent_light_position - in.tangent_position); + let view_dir = normalize(in.tangent_view_position - in.tangent_position); + let half_dir = normalize(view_dir + light_dir); + + let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); + let diffuse_color = light.color * diffuse_strength; + + let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); + let specular_color = specular_strength * light.color; + + let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; + + return vec4(result, object_color.a); +} \ No newline at end of file diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index 8a642c0..0000000 --- a/src/state.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::{ - input::Controllable, - meshs::DefaultMesh, - render::{Renderable, TextureManager}, -}; - -use super::render::{Camera, InstanceRaw, Texture, Vertex}; -use winit::{event::WindowEvent, window::Window}; - -pub struct State { - pub surface: wgpu::Surface, - pub device: wgpu::Device, - pub queue: wgpu::Queue, - pub config: wgpu::SurfaceConfiguration, - pub size: winit::dpi::PhysicalSize, - render_pipeline: wgpu::RenderPipeline, - camera: Camera, - depth_texture: Texture, - mesh: DefaultMesh, - #[allow(dead_code)] - texture_manager: TextureManager, -} - -impl State { - // Creating some of the wgpu types requires async code - pub async fn new(window: &Window) -> Self { - let size = window.inner_size(); - - // The instance is a handle to our GPU - // Backends::all => Vulkan + Metal + DX12 + Browser WebGPU - let instance = wgpu::Instance::new(wgpu::Backends::all()); - let surface = unsafe { instance.create_surface(window) }; - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - - // let adapter = instance - // .enumerate_adapters(wgpu::Backends::all()) - // .filter(|adapter| { - // // Check if this adapter supports our surface - // surface.get_preferred_format(&adapter).is_some() - // }) - // .next() - // .unwrap(); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), - // WebGL doesn't support all of wgpu's features, so if - // we're building for the web we'll have to disable some. - limits: if cfg!(target_arch = "wasm32") { - wgpu::Limits::downlevel_webgl2_defaults() - } else { - wgpu::Limits::default() - }, - label: None, - }, - None, // Trace path - ) - .await - .unwrap(); - - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface.get_preferred_format(&adapter).unwrap(), - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Fifo, - }; - surface.configure(&device, &config); - - let texture_manager = TextureManager::new(&device); - - let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl( - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/res/shaders/main.wgsl" - )) - .into(), - ), - }); - - let mut camera = Camera::new(config.width as f32, config.height as f32, 0.2); - camera.initialize(&device); - - let depth_texture = Texture::create_depth_texture(&device, &config, "depth_texture"); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[ - &texture_manager.get_texture_bind_group_layout(), - camera.get_bind_group_layout(), - ], - push_constant_ranges: &[], - }); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc(), InstanceRaw::desc()], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - }], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: Texture::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }); - - let diffuse_bind_group = texture_manager.create_texture_from_bytes( - &device, - &queue, - include_bytes!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/res/images/happy-tree.png" - )), - "happy-tree.png", - ); - - let diffuse_bind_group_pikachu = texture_manager.create_texture_from_bytes( - &device, - &queue, - include_bytes!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/res/images/pikachu.png" - )), - "pikachu.png", - ); - - let mut mesh = DefaultMesh::new(diffuse_bind_group, diffuse_bind_group_pikachu); - mesh.initialize(&device); - - Self { - surface, - device, - queue, - config, - size, - render_pipeline, - camera, - depth_texture, - mesh, - texture_manager, - } - } - - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - } - self.depth_texture = - Texture::create_depth_texture(&self.device, &self.config, "depth_texture"); - } - - pub fn input(&mut self, event: &WindowEvent) -> bool { - self.mesh.process_events(&event) || self.camera.process_events(&event) - } - - pub fn update(&mut self) { - self.camera.update_instances(&self.queue); - self.mesh.update_instances(&self.queue); - } - - pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - let output = self.surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[ - // This is what [[location(0)]] in the fragment shader targets - wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: true, - }, - }, - ], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }); - - render_pass.set_pipeline(&self.render_pipeline); - self.camera.prepare(&mut render_pass); - self.mesh.prepare(&mut render_pass); - self.camera.draw(&mut render_pass); - self.mesh.draw(&mut render_pass); - } - - // submit will accept anything that implements IntoIter - self.queue.submit(std::iter::once(encoder.finish())); - output.present(); - - Ok(()) - } -} diff --git a/src/render/texture/texture.rs b/src/texture.rs similarity index 66% rename from src/render/texture/texture.rs rename to src/texture.rs index a6f9165..d066b4b 100644 --- a/src/render/texture/texture.rs +++ b/src/texture.rs @@ -1,5 +1,6 @@ use anyhow::*; use image::GenericImageView; +use std::num::NonZeroU32; pub struct Texture { pub texture: wgpu::Texture, @@ -9,14 +10,58 @@ pub struct Texture { impl Texture { pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + + pub fn create_depth_texture( + device: &wgpu::Device, + config: &wgpu::SurfaceConfiguration, + label: &str, + ) -> Self { + let size = wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }; + let desc = wgpu::TextureDescriptor { + label: Some(label), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + }; + let texture = device.create_texture(&desc); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Nearest, + compare: Some(wgpu::CompareFunction::LessEqual), + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + ..Default::default() + }); + + Self { + texture, + view, + sampler, + } + } + + #[allow(dead_code)] pub fn from_bytes( device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str, + is_normal_map: bool, ) -> Result { let img = image::load_from_memory(bytes)?; - Self::from_image(device, queue, &img, Some(label)) + Self::from_image(device, queue, &img, Some(label), is_normal_map) } pub fn from_image( @@ -24,9 +69,10 @@ impl Texture { queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str>, + is_normal_map: bool, ) -> Result { - let rgba = img.to_rgba8(); let dimensions = img.dimensions(); + let rgba = img.to_rgba8(); let size = wgpu::Extent3d { width: dimensions.0, @@ -39,7 +85,11 @@ impl Texture { mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, + format: if is_normal_map { + wgpu::TextureFormat::Rgba8Unorm + } else { + wgpu::TextureFormat::Rgba8UnormSrgb + }, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, }); @@ -53,8 +103,8 @@ impl Texture { &rgba, wgpu::ImageDataLayout { offset: 0, - bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0), - rows_per_image: std::num::NonZeroU32::new(dimensions.1), + bytes_per_row: NonZeroU32::new(4 * dimensions.0), + rows_per_image: NonZeroU32::new(dimensions.1), }, size, ); @@ -76,41 +126,4 @@ impl Texture { sampler, }) } - - pub fn create_depth_texture(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, label: &str) -> Self { - let size = wgpu::Extent3d { - width: config.width, - height: config.height, - depth_or_array_layers: 1, - }; - let desc = wgpu::TextureDescriptor { - label: Some(label), - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::TEXTURE_BINDING, - }; - let texture = device.create_texture(&desc); - - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let sampler = device.create_sampler( - &wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), - lod_min_clamp: -100.0, - lod_max_clamp: 100.0, - ..Default::default() - } - ); - - Self { texture, view, sampler } - } }