diff --git a/.gitignore b/.gitignore index ea8c4bf..1de5659 100755 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +target \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 521ee82..cdff0ce 100755 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,20 +7,20 @@ { "type": "lldb", "request": "launch", - "name": "Debug executable 'lyra-engine'", + "name": "Debug lyra testbed", "cargo": { "args": [ "build", - "--bin=lyra-engine", - "--package=lyra-engine" + "--manifest-path", "${workspaceFolder}/examples/testbed/Cargo.toml" + //"--bin=testbed", ], "filter": { - "name": "lyra-engine", + "name": "testbed", "kind": "bin" } }, "args": [], - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/examples/testbed" }, { "type": "lldb", diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..d88ac3f --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,6 @@ +steps: + build: + image: rust:1.73 + commands: + - cargo build --release + - cargo test diff --git a/Cargo.lock b/Cargo.lock index 8b637df..4e6e011 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -297,6 +297,18 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + [[package]] name = "bit-set" version = "0.5.3" @@ -890,6 +902,44 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gltf" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2dcfb6dd7a66f9eb3d181a29dcfb22d146b0bcdc2e1ed1713cbf03939a88ea" +dependencies = [ + "base64 0.13.1", + "byteorder", + "gltf-json", + "image", + "lazy_static", + "urlencoding", +] + +[[package]] +name = "gltf-derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cbcea5dd47e7ad4e9ee6f040384fcd7204bbf671aa4f9e7ca7dfc9bfa1de20" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "gltf-json" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5b810806b78dde4b71a95cc0e6fdcab34c4c617da3574df166f9987be97d03" +dependencies = [ + "gltf-derive", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "gpu-alloc" version = "0.5.4" @@ -1041,6 +1091,18 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "infer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + [[package]] name = "instant" version = "0.1.12" @@ -1232,6 +1294,7 @@ dependencies = [ "tracing-appender", "tracing-log", "tracing-subscriber", + "uuid", "wgpu", "winit", ] @@ -1241,8 +1304,16 @@ name = "lyra-resource" version = "0.0.1" dependencies = [ "anyhow", + "base64 0.21.4", + "edict", + "glam", + "gltf", "image", + "infer", + "mime", + "percent-encoding", "thiserror", + "tracing", "uuid", ] @@ -1311,6 +1382,12 @@ dependencies = [ "objc", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1476,7 +1553,7 @@ checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" dependencies = [ "num-integer", "num-traits", - "rand", + "rand 0.4.6", "rustc-serialize", ] @@ -1791,6 +1868,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-easy" version = "0.3.0" @@ -1858,6 +1941,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1873,6 +1977,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "range-alloc" version = "0.1.3" @@ -1963,6 +2076,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1994,6 +2113,28 @@ version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +[[package]] +name = "serde_derive" +version = "1.0.179" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -2372,12 +2513,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "uuid" -version = "1.4.1" +name = "urlencoding" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom", + "rand 0.8.5", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4f755b4..1aaf3ca 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,4 @@ aligned-vec = "0.5.0" tracing-appender = "0.2.2" stopwatch = "0.0.7" petgraph = "0.6.4" +uuid = { version = "1.5.0", features = ["v4", "fast-rng"] } \ No newline at end of file diff --git a/examples/testbed/Cargo.lock b/examples/testbed/Cargo.lock new file mode 100644 index 0000000..f7dfad3 --- /dev/null +++ b/examples/testbed/Cargo.lock @@ -0,0 +1,3121 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1061f3ff92c2f65800df1f12fc7b4ff44ee14783104187dd04dfee6f11b0fd2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-activity" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" +dependencies = [ + "android-properties", + "bitflags 1.3.2", + "cc", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum 0.6.1", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.24", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-process" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf012553ce51eb7aa6dc2143804cc8252bd1cb681a1c5cb7fa94ca88682dee1d" +dependencies = [ + "async-io", + "async-lock", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.0.0", + "futures-lite", + "rustix 0.38.14", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-signal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c99f3cb3f9ff89f7d718fbb942c9eb91bedff12e396adf09a622dfe7ffec2bc2" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "concurrent-queue", + "futures-core", + "futures-io", + "libc", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atomicell" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157342dd84c64f16899b4b16c1fb2cce54b887990362aac3c590b3d13810890f" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + +[[package]] +name = "blocking" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "calloop" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +dependencies = [ + "bitflags 1.3.2", + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "com-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" + +[[package]] +name = "concurrent-queue" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "d3d12" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da" +dependencies = [ + "bitflags 1.3.2", + "libloading 0.7.4", + "winapi", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.0", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "edict" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85bf7cde5687ce04b093bfe183453fa12996b6bdfd2567a0262ebd621761d77" +dependencies = [ + "atomicell", + "edict-proc", + "hashbrown 0.13.2", + "parking_lot", + "smallvec", + "tiny-fn", +] + +[[package]] +name = "edict-proc" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94d80dc0f05250a9082bb9455bbf3d6c6c51db388b060df914aebcfb4a9b9f1" +dependencies = [ + "edict-proc-lib", + "syn 2.0.37", +] + +[[package]] +name = "edict-proc-lib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d98f9931a4f71c7eb7d85cf4ef1271b27014625c85a65376a52c10ac4ffaea" +dependencies = [ + "proc-easy", + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29e56284f00d94c1bc7fd3c77027b4623c88c1f53d8d2394c6199f2921dea325" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fps_counter" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aaba7ff514ee9d802b562927f80b1e94e93d8e74c31b134c9c3762dabf1a36b" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gilrs-core" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ccc99e9b8d63ffcaa334c4babfa31f46e156618a11f63efb6e8e6bcb37b830d" +dependencies = [ + "core-foundation", + "io-kit-sys", + "js-sys", + "libc", + "libudev-sys", + "log", + "nix 0.26.4", + "uuid", + "vec_map", + "wasm-bindgen", + "web-sys", + "windows 0.51.1", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "glow" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0fe580e4b60a8ab24a868bc08e2f03cbcb20d3d676601fa909386713333728" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gltf" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2dcfb6dd7a66f9eb3d181a29dcfb22d146b0bcdc2e1ed1713cbf03939a88ea" +dependencies = [ + "base64 0.13.1", + "byteorder", + "gltf-json", + "image", + "lazy_static", + "urlencoding", +] + +[[package]] +name = "gltf-derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cbcea5dd47e7ad4e9ee6f040384fcd7204bbf671aa4f9e7ca7dfc9bfa1de20" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "gltf-json" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5b810806b78dde4b71a95cc0e6fdcab34c4c617da3574df166f9987be97d03" +dependencies = [ + "gltf-derive", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "gpu-alloc" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22beaafc29b38204457ea030f6fb7a84c9e4dd1b86e311ba0542533453d87f62" +dependencies = [ + "bitflags 1.3.2", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "gpu-allocator" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8" +dependencies = [ + "backtrace", + "log", + "thiserror", + "winapi", + "windows 0.44.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +dependencies = [ + "bitflags 2.4.0", + "gpu-descriptor-types", + "hashbrown 0.14.1", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +dependencies = [ + "bitflags 2.4.0", +] + +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hassle-rs" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85" +dependencies = [ + "bitflags 1.3.2", + "com-rs", + "libc", + "libloading 0.7.4", + "thiserror", + "widestring", + "winapi", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational 0.4.1", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown 0.14.1", +] + +[[package]] +name = "infer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "io-kit-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2d4429acc1deff0fbdece0325b4997bdb02b2c245ab7023fd5deca0f6348de" +dependencies = [ + "core-foundation-sys", + "mach2", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" +dependencies = [ + "libc", + "libloading 0.7.4", + "pkg-config", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lyra-engine" +version = "0.0.1" +dependencies = [ + "aligned-vec", + "anyhow", + "async-std", + "async-trait", + "atomicell", + "bytemuck", + "cfg-if", + "edict", + "gilrs-core", + "glam", + "image", + "instant", + "lyra-resource", + "petgraph", + "quote", + "stopwatch", + "syn 2.0.37", + "tobj", + "tracing", + "tracing-appender", + "tracing-log", + "tracing-subscriber", + "uuid", + "wgpu", + "winit", +] + +[[package]] +name = "lyra-resource" +version = "0.0.1" +dependencies = [ + "anyhow", + "base64 0.21.4", + "edict", + "glam", + "gltf", + "image", + "infer", + "mime", + "percent-encoding", + "thiserror", + "tracing", + "uuid", +] + +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "naga" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3d4269bcb7d50121097702fde1afb75f4ea8083aeb7a55688dcf289a853271" +dependencies = [ + "bit-set", + "bitflags 1.3.2", + "codespan-reporting", + "hexf-parse", + "indexmap 1.9.3", + "log", + "num-traits", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum 0.5.11", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.1.42", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" +dependencies = [ + "num-integer", + "num-traits", + "rand 0.4.6", + "rustc-serialize", +] + +[[package]] +name = "num-complex" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" +dependencies = [ + "num-traits", + "rustc-serialize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "rustc-serialize", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "orbclient" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8378ac0dfbd4e7895f2d2c1f1345cab3836910baf3a300b000d04250f0c8428f" +dependencies = [ + "redox_syscall", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owned_ttf_parser" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.0.2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-easy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea59c637cd0e6b71ae18e589854e9de9b7cb17fefdbf2047e42bd38e24285b19" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89dff0959d98c9758c88826cc002e2c3d0b9dfac4139711d1f30de442f1139b" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "renderdoc-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustix" +version = "0.37.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "smithay-client-toolkit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.24.3", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spirv" +version = "0.2.0+1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags 1.3.2", + "num-traits", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stopwatch" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d04b5ebc78da44d3a456319d8bc2783e7d8cc7ccbb5cb4dc3f54afbd93bf728" +dependencies = [ + "num", +] + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "testbed" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "fps_counter", + "lyra-engine", + "tracing", +] + +[[package]] +name = "thiserror" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiff" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-fn" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7b2c33e09916c65a15c92c1e583946052527e06102689ed11c6125f64fa8ba" + +[[package]] +name = "tiny-skia" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tobj" +version = "3.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57381207291289bad19de63acd3fbf5948ff99b2868116c367b7224c37d55f90" +dependencies = [ + "ahash", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.0.2", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" +dependencies = [ + "crossbeam-channel", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "ttf-parser" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +dependencies = [ + "getrandom", + "rand 0.8.5", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.37", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "wgpu" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d745a1b6d91d85c33defbb29f0eee0450e1d2614d987e14bf6baf26009d132d7" +dependencies = [ + "arrayvec", + "cfg-if", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7131408d940e335792645a98f03639573b0480e9e2e7cddbbab74f7c6d9f3fff" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 1.3.2", + "codespan-reporting", + "fxhash", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdcf61a283adc744bb5453dd88ea91f3f86d5ca6b027661c6c73c7734ae0288b" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 1.3.2", + "block", + "core-graphics-types", + "d3d12", + "foreign-types", + "fxhash", + "glow", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.7.4", + "log", + "metal", + "naga", + "objc", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32444e121b0bd00cb02c0de32fde457a9491bd44e03e7a5db6df9b1da2f6f110" +dependencies = [ + "bitflags 1.3.2", + "js-sys", + "web-sys", +] + +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winit" +version = "0.28.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +dependencies = [ + "android-activity", + "bitflags 1.3.2", + "cfg_aliases", + "core-foundation", + "core-graphics", + "dispatch", + "instant", + "libc", + "log", + "mio", + "ndk", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle", + "redox_syscall", + "sctk-adwaita", + "smithay-client-toolkit", + "wasm-bindgen", + "wayland-client", + "wayland-commons", + "wayland-protocols", + "wayland-scanner", + "web-sys", + "windows-sys 0.45.0", + "x11-dl", +] + +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/examples/testbed/Cargo.toml b/examples/testbed/Cargo.toml new file mode 100644 index 0000000..25efcb5 --- /dev/null +++ b/examples/testbed/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "testbed" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lyra-engine = { path = "../../", version = "0.0.1" } +anyhow = "1.0.75" +async-std = "1.12.0" +tracing = "0.1.37" +fps_counter = "2.0.0" + +[workspace] \ No newline at end of file diff --git a/examples/testbed/assets/AntiqueCamera.glb b/examples/testbed/assets/AntiqueCamera.glb new file mode 100644 index 0000000..a45f134 Binary files /dev/null and b/examples/testbed/assets/AntiqueCamera.glb differ diff --git a/examples/testbed/assets/cube-embedded.gltf b/examples/testbed/assets/cube-embedded.gltf new file mode 100644 index 0000000..5d42356 --- /dev/null +++ b/examples/testbed/assets/cube-embedded.gltf @@ -0,0 +1,121 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.5", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQA" + } + ] +} diff --git a/examples/testbed/assets/cube-texture-bin.glb b/examples/testbed/assets/cube-texture-bin.glb new file mode 100644 index 0000000..9a3965b Binary files /dev/null and b/examples/testbed/assets/cube-texture-bin.glb differ diff --git a/examples/testbed/assets/cube-texture-embedded.gltf b/examples/testbed/assets/cube-texture-embedded.gltf new file mode 100644 index 0000000..5309758 --- /dev/null +++ b/examples/testbed/assets/cube-texture-embedded.gltf @@ -0,0 +1,142 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.6", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "bufferView":4, + "mimeType":"image/png", + "name":"uvgrid" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + }, + { + "buffer":0, + "byteLength":40222, + "byteOffset":840 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":41064, + "uri":"data:application/octet-stream;base64,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xn/zf/P/8f/oF5M6zvfN/9fN//0EID5ZXhMgQIAAAQIECBAgQIAAgW8qYAHgmzas2yJAgAABAgQIECBAgAABAlFg9QJA3A4cK856nb39v7+v7O0/ffzs7X99/Oztv338uB2o/yyjHLcDZsTrY8TtwP1nGWX5P/43f7faUf5Pt3iGfyb/x//Nn1uNaPwf/zd/bvkb/3N//tO3hfG/+PlP+JtffZvHsvF/ihzpr7/6+L96ASBdXEACBAgQIECAAAECBAgQIEBgtoAFgNlkTiBAgAABAgQIECBAgAABAl9PwALA12szV0yAAAECBAgQIECAAAECBGYLWACYTeYEAgQIECBAgAABAgQIECDw9QQsAHy9NnPFBAgQIECAAAECBAgQIEBgtoAFgNlkTiBAgAABAgQIECBAgAABAl9PwALA12szV0yAAAECBAgQIECAAAECBGYLWACYTeYEAgQIECBAgAABAgQIECDw9QQsAHy9NnPFBAgQIECAAAECBAgQIEBgtoAFgNlkTiBAgAABAgQIECBAgAABAl9PwALA12szV0yAAAECBAgQIECAAAECBGYLWACYTeYEAgQIECBAgAABAgQIECDw9QR+PT4+ll71y8tLaXz3r/0rH0DPv/yvfP70f/q/yudP/6f/q3z+9H/6v8rnT/+n/6t8/uwAqNQXmwABAgQIECBAgAABAgQIJAlYAEiCFoYAAQIECBAgQIAAAQIECFQKWACo1BebAAECBAgQIECAAAECBAgkCVgASIIWhgABAgQIECBAgAABAgQIVApYAKjUF5sAAQIECBAgQIAAAQIECCQJWABIghaGAAECBAgQIECAAAECBAhUClgAqNQXmwABAgQIECBAgAABAgQIJAlYAEiCFoYAAQIECBAgQIAAAQIECFQKWACo1BebAAECBAgQIECAAAECBAgkCVgASIIWhgABAgQIECBAgAABAgQIVApYAKjUF5sAAQIECBAgQIAAAQIECCQJrF4AeJ32SZd6Pcz57nj9g6R3p33x/R/vku70epjX/XT9g6R3j0/npEjXw+yn6foHSe8ez09Jka6Hkf/y//qTkfOu/Jf/OU/a9SjG/+k6TNK78l/+Jz1qV8PI/+mqS9abPz7/V37/Wb0AkNXQ4hAgQIAAAQIECBAgQIAAAQLLBSwALLdzJgECBAgQIECAAAECBAgQ+DICv5Zc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xn/j//gv/TH/4nhv/Df/N/+fYnoMfx37u4rvP7G9K77/xe+7a7//+gnA8MdVAAIECBAgQIAAAQIECBAgUC9gAaC+DVwBAQIECBAgQIAAAQIECBAYLrB6ASBuBx5+tVcCxO0QVz4e/lbcDjI82JUAcTvMlY+Hv5W9/be/oezt/338uB2w/yyjHLcDZ8TrY8j/qSdJLcv/3O2/fePK/9yfP/T+xv/xf/OnN49l43/x85/wN39ie/evjf9TT5Ja/vHjf8LfPLvVoNXf/9aOf6sXAG7h+IwAAQIECBAgQIAAAQIECBDYhoAFgG20g6sgQIAAAQIECBAgQIAAAQJDBSwADOVVOQECBAgQIECAAAECBAgQ2IaABYBttIOrIECAAAECBAgQIECAAAECQwUsAAzlVTkBAgQIECBAgAABAgQIENiGgAWAbbSDqyBAgAABAgQIECBAgAABAkMFLAAM5VU5AQIECBAgQIAAAQIECBDYhoAFgG20g6sgQIAAAQIECBAgQIAAAQJDBSwADOVVOQECBAgQIECAAAECBAgQ2IaABYBttIOrIECAAAECBAgQIECAAAECQwUsAAzlVTkBAgQIECBAgAABAgQIENiGgAWAbbSDqyBAgAABAgQIECBAgAABAkMF/np4ePh7aIR3Kn98fHzniLEfv7y8jA3wTu3uX/u/84gM/djzL/+HPmDvVK7/0/+984gM/Vj/p/8b+oC9U7n+T//3ziMy9GP938/u/+wAGJpeKidAgAABAgQIECBAgAABAtsQsACwjXZwFQQIECBAgAABAgQIECBAYKiABYChvConQIAAAQIECBAgQIAAAQLbELAAsI12cBUECBAgQIAAAQIECBAgQGCogAWAobwqJ0CAAAECBAgQIECAAAEC2xCwALCNdnAVBAgQIECAAAECBAgQIEBgqIAFgKG8KidAgAABAgQIECBAgAABAtsQsACwjXZwFQQIECBAgAABAgQIECBAYKiABYChvConQIAAAQIECBAgQIAAAQLbELAAsI12cBUECBAgQIAAAQIECBAgQGCogAWAobwqJ0CAAAECBAgQIECAAAEC2xBYvQBwPt6V3snrfiqNf3w6l8bfT8X3f34qvf/XaV8a/3x3LI0/7YvvX/6Xtr/8L37+5X/p82/8n0r9j8b/Un/jf/Hz/9Pn/8X3X93/V3//XZv/qxcASns/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AAECBAjkClgAyPUWjQABAgQIECBAgAABAgQIlAhYAChhF5QAAQIECBAgQIAAAQIECOQKWADI9RaNAAECBAgQIECAAAECBAiUCFgAKGEXlAABAgQIECBAgAABAgQI5ApYAMj1Fo0AAQIECBAgQIAAAQIECJQIWAAoYReUAAECBAgQIECAAAECBAjkClgAyPUWjQABAgQIECBAgAABAgQIlAhYAChhF5QAAQIECBAgQIAAAQIECOQKWADI9RaNAAECBAgQIECAAAECBAiUCPz18PDwd0nk/wV9fHysDP+fl5eX0vjuX/tXPoCef/lf+fzp//R/lc+f/k//V/n86f/0f5XPn/7vZ/d/dgBUZp/YBAgQIECAAAECBAgQIEAgScACQBK0MAQIECBAgAABAgQIECBAoFLAAkClvtgECBAgQIAAAQIECBAgQCBJwAJAErQwBAgQIECAAAECBAgQIECgUsACQKW+2AQIECBAgAABAgQIECBAIEnAAkAStDAECBAgQIAAAQIECBAgQKBSwAJApb7YBAgQIECAAAECBAgQIEAgScACQBK0MAQIECBAgAABAgQIECBAoFLAAkClvtgECBAgQIAAAQIECBAgQCBJwAJAErQwBAgQIECAAAECBAgQIECgUsACQKW+2AQIECBAgAABAgQIECBAIElg9QLA8emcdKnXw+yn6foHSe8ez09Jka6HeZ321z9Ievd8d0yKdD3MtC++/+Pd9QtLevd1PyVFuh5G/sv/609Gzrvyf8qB/kMU+S////BopLwt/6cU5z8F+fH5X/z9p3r+dy6e/5bnf/H3n7Xf/1YvAPypY/A+AQIECBAgQIAAAQIECBAgsB0BCwDbaQtXQoAAAQIECBAgQIAAAQIEhgn8WlJzv+0nlg/PuyVVzjqn3/Yfy6eEnwT02/5j+bB7nnUvSw7ut33E8v10WlLlrHP6bf+xvHs7zKprycH9tp9Ynk4J999te4rboHaHtyW3NOucfttXLN+fpll1LTk45ns7P5blv/xf8kzNOSfmezsvluX/NIdy0bEx31sFsSz/5f+ih2rGSTHf22mxLP+nGZLLDo353mqI5R+R/922/+z7j/O95h/LGfO/ON9t8WM5Y/4b873Fj+WU/O+2/Wd//4nf99r9x/Lc73+LFgBikreHP5bbBY3+L37Jb1/+Y3l07FZ//JLfvvzHckb8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fN+2dWcECBAgQIAAAQIECBAgQOAisHoBIG4Hu9Sa+CJ7+39/a3E7UP9ZRjluh8qI18eI28H6zzLK2dv/+3uK24H6zzLKcTtkRrw+hvyfepLUsvyf95u3z24c+V/8/IftsJ/dth+pr3z8L77/6v6/evwrz//wc4CPPK+ffUz1/K96/JP/02c/UrPqk//r5j+rFwBmtZaDCRAgQIAAAQIECBAgQIAAgRIBCwAl7IISIECAAAECBAgQIECAAIFcAQsAud6iESBAgAABAgQIECBAgACBEgELACXsghIgQIAAAQIECBAgQIAAgVwBCwC53qIRIECAAAECBAgQIECAAIESAQsAJeyCEiBAgAABAgQIECBAgACBXAELALneohEgQIAAAQIECBAgQIAAgRIBCwAl7IISIECAAAECBAgQIECAAIFcAQsAud6iESBAgAABAgQIECBAgACBEgELACXsghIgQIAAAQIECBAgQIAAgVwBCwC53qIRIECAAAECBAgQIECAAIESAQsAJeyCEiBAgAABAgQIECBAgACBXIG/Hh4e/s4N+e9oj4+P/34jufTy8pIc8d/h3L/2//cTkVvy/Mv/3Cfu39H0f/q/fz8RuSX9n/4v94n7dzT9n/7v309Ebkn/97P7PzsAcvNNNAIECBAgQIAAAQIECBAgUCJgAaCEXVACBAgQIECAAAECBAgQIJArYAEg11s0AgQIECBAgAABAgQIECBQImABoIRdUAIECBAgQIAAAQIECBAgkCtgASDXWzQCBAgQIECAAAECBAgQIFAiYAGghF1QAgQIECBAgAABAgQIECCQK2ABINdbNAIECBAgQIAAAQIECBAgUCJgAaCEXVACBAgQIECAAAECBAgQIJArYAEg11s0AgQIECBAgAABAgQIECBQImABoIRdUAIECBAgQIAAAQIECBAgkCtgASDXWzQCBAgQIECAAAECBAgQIFAisHoB4Ph0Lrnwf4Lup+mflyX/P56fSuL+E/R12v/zsuT/57tjSdx/gk774vs/3v1zKSX/f91PJXH/CSr/5f8/z0LF/+X/VMF+ifnj8794/lPd/5+Lx7/y/C+e/1TP/6rnv+Xzf/l/GQsqXsj/dd9/Vi8AVDS6mAQIECBAgAABAgQIECBAgMA8AQsA87wcTYAAAQIECBAgQIAAAQIEvqTAryVX3W/7i+XD825JlbPO6bf9xPIp4ScB/banWD7snmfdy5KD+21fsXw/nZZUOeucftt/LO/eDrPqWnJwv+0nlqdTwv132x7jNsjd4W3JLc06p9/2Gcv3p2lWXUsOjvnezo9l+S//lzxTc86J+d7Oi2X5P82hXHRszPdWQSz/iPzvtv1m33/s75t/LGf0/3G8a/FjOWP8i/ne4sdySv532/6z5z9xvtfuP5Yz5n9xvtvix3LG/DfO91v8WE6Z/8v/xn75T/7//hl0xvefmO+tEWJ5bv4vWgCIg3wb/GL58lQMfBGTvCV/LA8Me6k6dnKt84vly0EDX8RGbo0fywPDXqqOD3kb/GL5ctDAF3GQb4N/LA8Me6k6TnJa5xfLl4MGvoiTvDb5i+WBYS9Vx3yX//Jf/o9f9Lsk339fxHyX/wXjf/hHjor+r7r943hXMf7F8b5k/A//yFEx/4nzvYr5X5zvVsx/43y/ZP4v/y/DkfzP//7zmfnvJwCXR9kLAgQIECBAgAABAgQIECDwfQUsAHzftnVnBAgQIECAAAECBAgQIEDgIrB6ASBuB77UmvgibgdKDHsJFbdDXd5MfBG3gySGvYTK3v57Cfy/F3E7YP9ZRjluh8yI18eI20H7zzLK8n/8b/5vtaP8H/83P275y//xf/Pnln/5+B+2A9+6zlGfVff/1c9/+fgffg4wqo1v1Vvd/1fPf+X/dOvxGP6Z/B//N89uNeLa/F+9AHDr4nxGgAABAgQIECBAgAABAgQIbEPAAsA22sFVECBAgAABAgQIECBAgACBoQIWAIbyqpwAAQIECBAgQIAAAQIECGxDwALANtrBVRAgQIAAAQIECBAgQIAAgaECFgCG8qqcAAECBAgQIECAAAECBAhsQ8ACwDbawVUQIECAAAECBAgQIECAAIGhAhYAhvKqnAABAgQIECBAgAABAgQIbEPAAsA22sFVECBAgAABAgQIECBAgACBoQIWAIbyqpwAAQIECBAgQIAAAQIECGxDwALANtrBVRAgQIAAAQIECBAgQIAAgaECFgCG8qqcAAECBAgQIECAAAECBAhsQ8ACwDbawVUQIECAAAECBAgQIECAAIGhAr8eHx+HBniv8peXl/cOGfq5+9f+Qx+wdyr3/Mv/dx6RoR/r//R/Qx+wdyrX/+n/3nlEhn6s/9P/DX3A3qlc/6f/e+cRGfqxHQBDeVVOgAABAgQIECBAgAABAgS2IWABYBvt4CoIECBAgAABAgQIECBAgMBQAQsAQ3lVToAAAQIECBAgQIAAAQIEtiFgAWAb7eAqCBAgQIAAAQIECBAgQIDAUAELAEN5VU6AAAECBAgQIECAAAECBLYhYAFgG+3gKggQIECAAAECBAgQIECAwFABCwBDeVVOgAABAgQIECBAgAABAgS2IWABYBvt4CoIECBAgAABAgQIECBAgMBQAQsAQ3lVToAAAQIECBAgQIAAAQIEtiFgAWAb7eAqCBAgQIAAAQIECBAgQIDAUAELAEN5VU6AAAECBAgQIECAAAECBLYhsHoBYD9NpXdyPD+Vxn+d9qXxz3fH0vjTvvj+j3el9/+6n0rjH5/OpfHlf/HzL/9Ln3/5/7P7v+r2PxePf+Xjf3H/Vz3/q57/lo//xfMf+f+z5z9fPf9XLwCUzr4EJ0CAAAECBAgQIECAAAECBD4kYAHgQ0wOIkCAAAECBAgQIECAAAECX1vg15LL77f9xPIp4ScB/banWD7snpfc0qxz+m0fsXw/nWbVteTgftt/LO/eDkuqnHVOv+0vlqdTwv132x7jNsjd4W3WvSw5uN/2Fcv3p2lJlbPO6bf9x/LheTerriUHx3xv58ey/E94/rttr/L/98+A5P8PyP9u2292/xf7+9b/xXJG/x/HuxY/ljOe/zjet/ixnDL+F/d/cb7X7j+WM+Z/cb7b4sdyxvw3jvctfiynjP/yv7Ff/pP/v38GnfH9J+Z7a4RY/mr5v2gBICZ5S/5YvjyVA1/ETq51frE8MOyl6tjIrfFj+XLQwBfxIW+T/1geGPZSdRzk2+Afy5eDBr6Ik5zW+cXywLCXquMkr03+Yvly0MAX8Ut+m/zG8sCwl6pjvst/+S//p0tuZLyI+f4j8z8sclbcf+zvK/r/ON5VjH8x30vG//CPHBXznzjfq5j/xfluxfy3fPyX/5dhRv7nf//5TvnvJwCXVPKCAAECBAgQIECAAAECBAh8XwELAN+3bd0ZAQIECBAgQIAAAQIECBC4CKxeAIjbgS61Jr6I26ESw15Cxe0glzcTX2Rv/+9vLW4H7D/LKMftkBnx+hhxO2j/WUY5bgfOiNfHkP/jf/Pfm8ey/B//Nz+id/9a/k89SWq5uv+rbv/q8a98/A8/B0h98P4XrHr+Vz3/LR//w88BSto/4W8+3bov+T/+b57d8v/q+b96AeAWjs8IECBAgAABAgQIECBAgACBbQhYANhGO7gKAgQIECBAgAABAgQIECAwVMACwFBelRMgQIAAAQIECBAgQIAAgW0IWADYRju4CgIECBAgQIAAAQIECBAgMFTAAsBQXpUTIECAAAECBAgQIECAAIFtCFgA2EY7uAoCBAgQIECAAAECBAgQIDBUwALAUF6VEyBAgAABAgQIECBAgACBbQhYANhGO7gKAgQIECBAgAABAgQIECAwVMACwFBelRMgQIAAAQIECBAgQIAAgW0IWADYRju4CgIECBAgQIAAAQIECBAgMFTAAsBQXpUTIECAAAECBAgQIECAAIFtCFgA2EY7uAoCBAgQIECAAAECBAgQIDBU4K+Hh4e/h0Z4p/LHx8d3jhj78cvLy9gA79Tu/rX/O4/I0I89//J/6AP2TuX6P/3fO4/I0I/1f/q/oQ/YO5Xr//R/7zwiQz/W//3s/s8OgKHppXICBAgQIECAAAECBAgQILANAQsA22gHV0GAAAECBAgQIECAAAECBIYKWAAYyqtyAgQIECBAgAABAgQIECCwDQELANtoB1dBgAABAgQIECBAgAABAgSGClgAGMqrcgIECBAgQIAAAQIECBAgsA0BCwDbaAdXQYAAAQIECBAgQIAAAQIEhgpYABjKq3ICBAgQIECAAAECBAgQILANAQsA22gHV0GAAAECBAgQIECAAAECBIYKWAAYyqtyAgQIECBAgAABAgQIECCwDQELANtoB1dBgAABAgQIECBAgAABAgSGClgAGMqrcgIECBAgQIAAAQIECBAgsA2B1QsAx/NT6Z28TvvS+Oe7Y2n8aV98/8e70vt/3U+l8Y9P59L4+6n4/uV/afvL/+Ln/6fnf/H9V/f/5+Lxrzz/i+c/1fO/6vlv+fgv/3/2+C//S9t/bf6vXgAovXvBCRAgQIAAAQIECBAgQIAAgQ8JWAD4EJODCBAgQIAAAQIECBAgQIDA1xb4teTy+21PsXzYPS+pctY5/bavWL6fTrPqWnJwv+0/lndvhyVVzjqn3/YXy9Mp4f67bY9xG+Tu8DbrXpYc3G/7jOX707Skylnn9Nv+Y/nwvJtV15KD+20/sXxK+ElAzPd2/bEs/+X/kmd6zjkx39t5sSz/pzmUi46N/V2rIJYz+r/Y3i1+LGe0fxzvWvxYzhj/4njf4sdyyvjfbfvNnv/E+V67/1jOmP/F8a7Fj+WM8S+O9y1+LKeM/922f/n/+2ew8n/8/Cfme3v+Y1n+T43kw/8tWgCInVzr/GL5w5FXHBgbuTV+LK+o9sOnxi/5bfCL5Q9XsuLAOMi3wT+WV1T74VNjJ9cmP7H84UpWHBgneW3yF8srqv3wqXGS2wa/WP5wJSsOjIN8G/xjeUW1Hz415rv8l//yf/yiX0zOmO8l+R8WOSv6v9jfV/T/8XmvGP/ieF8y/od/5KiY/8T5XsX8r3r8k//TpTuU/wXzf/l/ef4q5r+fmf9+AnBpSi8IECBAgAABAgQIECBAgMD3FbAA8H3b1p0RIECAAAECBAgQIECAAIGLwOoFgLgd6lJr4ou4HSwx7CVU9vb/S+D/vYjbAfvPMspxO2RGvD5G3A7af5ZRzt7+399T3A7Uf5ZRlv/jf/N2qx3l/3SLZ/hnPz7/w88BhmNfCVDd/1ePf+X5H7YDX2me4W9Vz/+qx7/y8V/+D3/GbwWQ/+P/5tkt/6+e/6sXAG7h+IwAAQIECBAgQIAAAQIECBDYhoAFgG20g6sgQIAAAQIECBAgQIAAAQJDBSwADOVVOQECBAgQIECAAAECBAgQ2IaABYBttIOrIECAAAECBAgQIECAAAECQwUsAAzlVTkBAgQIECBAgAABAgQIENiGgAWAbbSDqyBAgAABAgQIECBAgAABAkMFLAAM5VU5AQIECBAgQIAAAQIECBDYhoAFgG20g6sgQIAAAQIECBAgQIAAAQJDBSwADOVVOQECBAgQIECAAAECBAgQ2IaABYBttIOrIECAAAECBAgQIECAAAECQwUsAAzlVTkBAgQIECBAgAABAgQIENiGgAWAbbSDqyBAgAABAgQIECBAgAABAkMFfj0+Pg4N8F7lLy8v7x0y9HP3r/2HPmDvVO75l//vPCJDP9b/6f+GPmDvVK7/0/+984gM/Vj/p/8b+oC9U7n+T//3ziMy9GM7AIbyqpwAAQIECBAgQIAAAQIECGxDwALANtrBVRAgQIAAAQIECBAgQIAAgaECFgCG8qqcAAECBAgQIECAAAECBAhsQ8ACwDbawVUQIECAAAECBAgQIECAAIGhAhYAhvKqnAABAgQIECBAgAABAgQIbEPAAsA22sFVECBAgAABAgQIECBAgACBoQIWAIbyqpwAAQIECBAgQIAAAQIECGxDwALANtrBVRAgQIAAAQIECBAgQIAAgaECFgCG8qqcAAECBAgQIECAAAECBAhsQ8ACwDbawVUQIECAAAECBAgQIECAAIGhAhYAhvKqnAABAgQIECBAgAABAgQIbENg9QLA67QvvZPz3bE0/rQvvv/jXen9v+6n0vjHp3Np/P1UfP/np9L7l//yv/IB/PH5X9z/Vff/5+Lxr3z8L57/VPf/x+Lxr3z8l/+Vw89/5H/t/Ef+r/v+s3oBoDT7BCdAgAABAgQIECBAgAABAgQ+JGAB4ENMDiJAgAABAgQIECBAgAABAl9b4NeSy++3fcXy/XRaUuWsc/pt/7G8ezvMqmvJwf22n1ieTgn33217jNsgd4e3Jbc065x+22cs35+mWXUtObjf9hvLh+fdkipnndNv+4vlU8JPAvptT7F82D3PupclB8d8b+fHsvyX/0ueqTnnxHxv58Xyj8j/bttv9v3H/r75x3JG/x/HuxY/ljPGvzjet/ixnDL+d9v+s+c/sb9v9x/LGf1/HO9a/FjOGP/ieN/ix3LK+C//G/vlP/n/+2fQGd9/Yr63Rohl+T9dnstRL+J432LE8tz5z6IFgNjIrfFjedRNx3rjQ94Gv1iOx416HQf5NvjH8qiYsd44yWmdXyzH40a9jpO8NvmL5VExY73xIW8PfyzH40a9joN8G/xjeVTMWG+c5LTJTyzH40a9jvku/+W//J9GpdrVemN/V9H/xfau6P/jeFcx/sXxvmT8D//IUTH/qe7/43hXMf7F8b5k/A//yCH/8+e/8v/3P3JUzP/k/+9/5Fyb/34CcHWK5U0CBAgQIECAAAECBAgQIPC9BCwAfK/2dDcECBAgQIAAAQIECBAgQOCqwOoFgLgd7GqEwW9mb//vbyduB+o/yyjH7ZAZ8foYcTto/1lGOW6HzYjXx4jbAfvPMspxO1RGvD6G/P+9Ha63ySjL/9/b4TK8+xjl+R+2A/fXllGu7v+rn//y8T/8HCCjvfsY1f1/9fgn/6f+kUgty//xf/PsVoPK/9rnf+33n9ULALceDp8RIECAAAECBAgQIECAAAEC2xCwALCNdnAVBAgQIECAAAECBAgQIEBgqIAFgKG8KidAgAABAgQIECBAgAABAtsQsACwjXZwFQQIECBAgAABAgQIECBAYKiABYChvConQIAAAQIECBAgQIAAAQLbELAAsI12cBUECBAgQIAAAQIECBAgQGCogAWAobwqJ0CAAAECBAgQIECAAAEC2xCwALCNdnAVBAgQIECAAAECBAgQIEBgqIAFgKG8KidAgAABAgQIECBAgAABAtsQsACwjXZwFQQIECBAgAABAgQIECBAYKiABYChvConQIAAAQIECBAgQIAAAQLbELAAsI12cBUECBAgQIAAAQIECBAgQGCowF8PDw9/D43wTuWPj4/vHDH245eXl7EB3qnd/Wv/dx6RoR97/uX/0Afsncr1f/q/dx6RoR/r//R/Qx+wdyrX/+n/3nlEhn6s//vZ/Z8dAEPTS+UECBAgQIAAAQIECBAgQGAbAhYAttEOroIAAQIECBAgQIAAAQIECAwVsAAwlFflBAgQIECAAAECBAgQIEBgGwIWALbRDq6CAAECBAgQIECAAAECBAgMFbAAMJRX5QQIECBAgAABAgQIECBAYBsCFgC20Q6uggABAgQIECBAgAABAgQIDBWwADCUV+UECBAgQIAAAQIECBAgQGAbAhYAttEOroIAAQIECBAgQIAAAQIECAwVsAAwlFflBAgQIECAAAECBAgQIEBgGwIWALbRDq6CAAECBAgQIECAAAECBAgMFbAAMJRX5QQIECBAgAABAgQIECBAYBsCqxcAznfH0juZ9vvS+OfjXWn81/1UGv/4dC6Nv5+K7//8VHr/r1Px8y//a9tf/pf6V/d/1f1/9fhXPv4X93/V/f+xePwrH/+L5z/y/2fPf+R/8fz/i+f/6gWA0tmX4AQIECBAgAABAgQIECBAgMCHBCwAfIjJQQQIECBAgAABAgQIECBA4GsL/Fpy+f22/1jevR2WVDnrnH7bXyxPp9OsupYc3G97jOXd4W1JlbPO6bd9xfL9aZpV15KD+22vsXx43i2pctY5/ba/WD4l/CSg3/YYy4fd86x7WXJwv+0rlu+nhOe/2/Yq/3//DEj+/4D877b9Zfd/sb9v/UcsZ/T/cbxr8WM54/mP432LH8sp439x/xf7+3b/sZzR/8fxrsWP5YzxL473LX4sp4z/8r+xX/6T/79/Bp3x/Sfme2uEWJb/0+W5HPUijvctRixnfP+J432LH8tzx/9FCwDxIW+T/1huFzT6vzjIt8E/lkfHbvXHSU7r/GI5I35s5Nb4sZwRPz7k7eGP5Yz4cZBvg38sZ8SPk5w2+YnljPixk2+dfyxnxI/5Lv/l/4/L/7DIWdH/xf6+ov+P413F+BfH+5LxP/wjR0X/F/v7iv4/jncV418c70vGf/l/mWbI//zvP/J/ujx/8n/d9z8/Abg8Sl4QIECAAAECBAgQIECAAIHvK2AB4Pu2rTsjQIAAAQIECBAgQIAAAQIXgdULAHE78KXWxBdxO2Bi2EuouB3y8mbii7gdNDHsJVT29t9L4P+9iNsB+88yynE7ZEa8PkbcDtZ/llGW/+P/5setdpT/0y2e4Z9V93/V7V89/pWP/+HnAMMftisBqvv/6vGvfPwPPwe40jzD35L/4//m0a1GrJ7/yP/pVvMM/+yrj/+rFwCGCwtAgAABAgQIECBAgAABAgQIrBawALCaUAUECBAgQIAAAQIECBAgQGD7AhYAtt9GrpAAAQIECBAgQIAAAQIECKwWsACwmlAFBAgQIECAAAECBAgQIEBg+wIWALbfRq6QAAECBAgQIECAAAECBAisFrAAsJpQBQQIECBAgAABAgQIECBAYPsCFgC230aukAABAgQIECBAgAABAgQIrBawALCaUAUECBAgQIAAAQIECBAgQGD7AhYAtt9GrpAAAQIECBAgQIAAAQIECKwWsACwmlAFBAgQIECAAAECBAgQIEBg+wIWALbfRq6QAAECBAgQIECAAAECBAisFrAAsJpQBQQIECBAgAABAgQIECBAYPsCvx4fH0uv8uXlpTS++9f+lQ+g51/+Vz5/+j/9X+Xzp//T/1U+f/o//V/l86f/0/9VPn92AFTqi02AAAECBAgQIECAAAECBJIELAAkQQtDgAABAgQIECBAgAABAgQqBSwAVOqLTYAAAQIECBAgQIAAAQIEkgQsACRBC0OAAAECBAgQIECAAAECBCoFLABU6otNgAABAgQIECBAgAABAgSSBCwAJEELQ4AAAQIECBAgQIAAAQIEKgUsAFTqi02AAAECBAgQIECAAAECBJIELAAkQQtDgAABAgQIECBAgAABAgQqBSwAVOqLTYAAAQIECBAgQIAAAQIEkgQsACRBC0OAAAECBAgQIECAAAECBCoFLABU6otNgAABAgQIECBAgAABAgSSBFYvAEz7fdKlXg9zPt5d/yDp3df9lBTpepjj0/n6B0nv7qcpKdL1MMfz0/UPkt59nYqf/7tj0p1eDyP/p+swSe/++Pwv7v+q+//q8a88/4v7v+r+v3r8Kx//5X/SSHM9jPyvnf/J/9rvP199/F+9AHC9W/AuAQIECBAgQIAAAQIECBAgsCUBCwBbag3XQoAAAQIECBAgQIAAAQIEBgn8WlJvv+0nlqfTaUmVs87ptz3G8u7wNquuJQf32z5i+f40Laly1jn9tt9YPjzvZtW15OB+218snxJ+EtBve4rlw+55yS3NOqff9hnL91PC899tez2H8u7tMOtelhwc872dH8vyf1pCOuucmO/txFj+EfnfbfvNvv/Y3zf/WM7o/+N41+LHcsb4F/O9xY/llPwP/d3/3X8oZ/R/sb9v8WM5o/+P412LH8sZ418c71v8WE4Z/+V/Y7/8J/9//wxS/o+f/8Z8bw9hLMv/6ZKXo17EfG8xYnnu+L9sASB8yW+Df8agHzHjTbabj+V43KjXcZLXJn+xPCpmrDdO8tvkN5bjcaNexyRvyR/Lo2LGeuMkp01+YjkeN+p1nOS1yV8sj4oZ642DXPvyH8vxuFGvY77Lf/mfnv9J6GfLAAAgAElEQVRhkbOi/4v9fUX/H8e7ivGvPP/DImdF/xf7+4r+P453FeNfzPeS8V/+X4Z2+Z8//5H/0+X5k//587/PHP/9BODyKHtBgAABAgQIECBAgAABAgS+r4AFgO/btu6MAAECBAgQIECAAAECBAhcBFYvAMTtgJdaE1/E7RCJYS+h4nbQy5uJL7K3//e3FrcD9p9llON2yIx4fYy4Haz/LKOcvf2/vyf5P/UkqeUfn/9hO3Aq/P+CVff/1eNfef6HnwOUtH/C33y5dV/V41/5+C//bz0ewz+T/+P/5tOtRpT/4//m2S3/rz7+r14AuIXjMwIECBAgQIAAAQIECBAgQGAbAhYAttEOroIAAQIECBAgQIAAAQIECAwVsAAwlFflBAgQIECAAAECBAgQIEBgGwIWALbRDq6CAAECBAgQIECAAAECBAgMFbAAMJRX5QQIECBAgAABAgQIECBAYBsCFgC20Q6uggABAgQIECBAgAABAgQIDBWwADCUV+UECBAgQIAAAQIECBAgQGAbAhYAttEOroIAAQIECBAgQIAAAQIECAwVsAAwlFflBAgQIECAAAECBAgQIEBgGwIWALbRDq6CAAECBAgQIECAAAECBAgMFbAAMJRX5QQIECBAgAABAgQIECBAYBsCFgC20Q6uggABAgQIECBAgAABAgQIDBX46+Hh4e+hEd6p/PHx8Z0jxn788vIyNsA7tbt/7f/OIzL0Y8+//B/6gL1Tuf5P//fOIzL0Y/2f/m/oA/ZO5fo//d87j8jQj/V/P7v/swNgaHqpnAABAgQIECBAgAABAgQIbEPAAsA22sFVECBAgAABAgQIECBAgACBoQIWAIbyqpwAAQIECBAgQIAAAQIECGxDwALANtrBVRAgQIAAAQIECBAgQIAAgaECFgCG8qqcAAECBAgQIECAAAECBAhsQ8ACwDbawVUQIECAAAECBAgQIECAAIGhAhYAhvKqnAABAgQIECBAgAABAgQIbEPAAsA22sFVECBAgAABAgQIECBAgACBoQIWAIbyqpwAAQIECBAgQIAAAQIECGxDwALANtrBVRAgQIAAAQIECBAgQIAAgaECFgCG8qqcAAECBAgQIECAAAECBAhsQ2D1AsD5eFd6J6/7qTT+8elcGn8/Fd//+an0/l+nfWn8892xNP60L75/+V/a/uX5X9z/Vff/1eNfef4X93/V/f+xePyT/1Np/yv/a+c/8r/2+a/+/mP8Xzf/X70AUNr7Ck6AAAECBAgQIECAAAECBAh8SMACwIeYHESAAAECBAgQIECAAAECBL62wK8ll99ve4rl3eFtSZWzzum3fcTy/WmaVdeSg/ttL7F8eN4tqXLWOf22v1g+JfwkoN/2GMuH3fOse1lycL/tK5bvp9OSKmed02/7j+Xd22FWXUsO7rf9xvJ0Srj/btu//P/9M6Afkf/dtv/s/i/29y1/Yjmj/4/Pe4sfyxnjX8z3Fj+WU/K/2/af3f/F/r7dfyxn9P9xvGvxYzlj/IvjfYsfyynjv/xv7Jf/5P/vnwFkzH9ivrdGiGX5P12ey1Ev4njfYsRyxvwnjvctfiwb/+fN/xctAMRJTuv8Yrk1yOj/YiO3xo/l0bFb/fEhbw9/LGfEj4N8G/xjOSN+nOS0yU8sZ8SPnXzr/GM5I34c5NrkN5Yz4sdJfpv8x3JG/Jjv8v8H5n9Y5Kzo/2J/X9H/Vz//Md9L8j8sclb0f7G/r+j/43hXMf7F8b5k/Jf/l2G2YvyT/7+/5Mj/gvm//P82+e8nAJem9IIAAQIECBAgQIAAAQIECHxfAQsA37dt3RkBAgQIECBAgAABAgQIELgIrF4AiNshL7UmvojbQRPDXkJlb/+/BP7fi7gdsP8soxy3Q2bE62PE7aD9Zxnl7O3//T3F7YD9Zxll+T/+b37casfy/A/bAW9d56jPqvv/6ue/PP/DzwFGtfGteqv7/+rxT/5Ptx6P4Z/J//F/8+hWI8r/2ue/+vuP8f/3z2Fu5cmfPlu9APCnir1PgAABAgQIECBAgAABAgQIbEfAAsB22sKVECBAgAABAgQIECBAgACBYQIWAIbRqpgAAQIECBAgQIAAAQIECGxHwALAdtrClRAgQIAAAQIECBAgQIAAgWECFgCG0aqYAAECBAgQIECAAAECBAhsR8ACwHbawpUQIECAAAECBAgQIECAAIFhAhYAhtGqmAABAgQIECBAgAABAgQIbEfAAsB22sKVECBAgAABAgQIECBAgACBYQIWAIbRqpgAAQIECBAgQIAAAQIECGxHwALAdtrClRAgQIAAAQIECBAgQIAAgWECFgCG0aqYAAECBAgQIECAAAECBAhsR8ACwHbawpUQIECAAAECBAgQIECAAIFhAr8eHx+HVf6Ril9eXj5y2LBj3L/2H/ZwfaBiz7/8/8BjMuwQ/Z/+b9jD9YGK9X/6vw88JsMO0f/p/4Y9XB+oWP+n//vAYzLsEDsAhtGqmAABAgQIECBAgAABAgQIbEfAAsB22sKVECBAgAABAgQIECBAgACBYQIWAIbRqpgAAQIECBAgQIAAAQIECGxHwALAdtrClRAgQIAAAQIECBAgQIAAgWECFgCG0aqYAAECBAgQIECAAAECBAhsR8ACwHbawpUQIECAAAECBAgQIECAAIFhAhYAhtGqmAABAgQIECBAgAABAgQIbEfAAsB22sKVECBAgAABAgQIECBAgACBYQIWAIbRqpgAAQIECBAgQIAAAQIECGxHwALAdtri/7dzN8eN5NgaQGciaiULZAOtKA+404ZWyIP0oKzgRjs6QxtkgbbzBi+6qW8Q1VJlMvGjqqPNEEUSQB7gApl3wNYTAgQIECBAgAABAgQIECDQTEACoBmtigkQIECAAAECBAgQIECAwDwCdycAXo/L0Ks5P1+Htn9cBl//9Xno9b8ux6HtXx/OQ9tfjoOv//ww9PrF/zLUf/T6N3r8r4Pn//D4H7z+jV7/z4P3v+H7/+D7H/E/eP8X/0P3X/E/9v7H/n/f88/dCYCh0adxAgQIECBAgAABAgQIECBA4JcEJAB+icmHCBAgQIAAAQIECBAgQIDA1xb4tqX79bGvLD9eli1VrvpOfew1y6cfh1V1bflwfewny5cOPwmojz1m+XT4seWSVn2nPvaZ5cflsqquLR+uj/1n+fB22lLlqu/Ux36zvFw6XH917DmPQR1Ob6uuZcuHM97L97Ms/pctpKu+k+td+WKWe6x/Od6l/Sz3GP+c76X9LPeY/xnvpf0sd4n/6thv7/Uv1/ty/Vnusf7nflfaz3KP/S/3+9J+lrvs/9Wxf/H//jM48d/+/ifjvcz/LIv/pZA0/ct4Lw1l2f7f/v439/vin+Wvtv9vSgDkTV65+ctyAWn9l5O8TP4st2671J+bfNn8s9yj/bzJKTc/We7Rfi7yZfHPco/28yG/3PxmuUf7GeQl+LPco/28ySkPP1nu0X7Gu/gfEP+R5Byx/o0e/5zvI+Z/xvuQ+I8k54j1L9f7Eet/7ncj9r/c74fs/+L/ts2J//73P+J/uc0/8d//+c/+/57ku3f/9xOAWyh7QYAAAQIECBAgQIAAAQIEfl8BCYDfd2xdGQECBAgQIECAAAECBAgQuAncnQDI46C3Wju+6H38v760PA5Yv9ejnMche7RXt5HHwer3epR7H/+vrymPA9fv9Sjncage7dVtiP+lJulaHr3+jR7/0fN/ePzHzwG6Try/Ghu9/o/e/4bv//FzgCHj3+G/+fTRdYn/9+PAHzm1ek/8L61of6le+3/73/x/NBBfff+/OwHwEY73CBAgQIAAAQIECBAgQIAAgTkEJADmGAe9IECAAAECBAgQIECAAAECTQUkAJryqpwAAQIECBAgQIAAAQIECMwhIAEwxzjoBQECBAgQIECAAAECBAgQaCogAdCUV+UECBAgQIAAAQIECBAgQGAOAQmAOcZBLwgQIECAAAECBAgQIECAQFMBCYCmvConQIAAAQIECBAgQIAAAQJzCEgAzDEOekGAAAECBAgQIECAAAECBJoKSAA05VU5AQIECBAgQIAAAQIECBCYQ0ACYI5x0AsCBAgQIECAAAECBAgQINBUQAKgKa/KCRAgQIAAAQIECBAgQIDAHAISAHOMg14QIECAAAECBAgQIECAAIGmAv/+/v37f5q28EnlT09Pn3yi7dsvLy9tG/ikdtdv/D+ZIk3fNv/Ff9MJ9knl1j/r3ydTpOnb1j/rX9MJ9knl1j/r3ydTpOnb1r8/e/1zAqBpeKmcAAECBAgQIECAAAECBAjMISABMMc46AUBAgQIECBAgAABAgQIEGgqIAHQlFflBAgQIECAAAECBAgQIEBgDgEJgDnGQS8IECBAgAABAgQIECBAgEBTAQmAprwqJ0CAAAECBAgQIECAAAECcwhIAMwxDnpBgAABAgQIECBAgAABAgSaCkgANOVVOQECBAgQIECAAAECBAgQmENAAmCOcdALAgQIECBAgAABAgQIECDQVEACoCmvygkQIECAAAECBAgQIECAwBwCEgBzjINeECBAgAABAgQIECBAgACBpgISAE15VU6AAAECBAgQIECAAAECBOYQuDsBcH6+Dr2S47IMbf98fR7a/utyHNr+9eE8tP3lOPj6zw9Dr//1uAxt/4+P/8Hr3+jxvw6e/8Pjf/D6N3r9H73/Dd//xf/Q/Uf8j73/Ef9jn3/s/2Pn/+jnn3v3/7sTAENXf40TIECAAAECBAgQIECAAAECvyQgAfBLTD5EgAABAgQIECBAgAABAgS+tsC3Ld2vj/1m+fTjsKXKVd+pj/1l+dLhJwH1sacsnw4/Vl3Llg/Xxz6y/LhctlS56jv1sZcsH95Oq+ra8uH62F+Wl0uH66+OPecx6MPpbcslrfpOfewry4+XZVVdWz6c8V6+n+U/Iv6rY7+9rz/Hu/hnucf453wv7We5x/zPeC/tZ7lL/FfH/nuvf7nel+vPco/1P/e70n6We+x/ud+X9rPcZf8X/4X99if+338G2eP+J+O9DEKWxf9ym5etXuR+X9rIco/7n9zvS/tZtv93uP//jfb/TQmAnORl8me5TMjWf7nJl80/y63bLvXnTU65+clyj/ZzkS+Lf5Z7tJ+bXLn5zXKP9vMmv9z8Z7lH+/mQU25+styj/Vzky+Kf5R7tZ7z/kfEfSc4R15/jPWL8c76PmP8Z70PiP5KcI9a/XO9HrP+5343Y/3K/H7L/i//bNiP++9//iP/lNv/Ef//7P/v/+//J+dX3fz8BuC0lXhAgQIAAAQIECBAgQIAAgd9XQALg9x1bV0aAAAECBAgQIECAAAECBG4CdycA8jjwrdaOL/I4YMdmb03lccjbP3Z8kcfBOjZ7a6r38f9bw3+9yOPA9Xs9ynkcqkd7dRt5HLx+r0f5j4//OA7cw7tuY/T4j57/w+M/fg5Qj02P8uj1f/T+N3z/F/89pvk/tiH+2//m+R/x//uG+G//3zz7yN/+P3b+j37+uXf/vzsB8NHk9B4BAgQIECBAgAABAgQIECAwh4AEwBzjoBcECBAgQIAAAQIECBAgQKCpgARAU16VEyBAgAABAgQIECBAgACBOQQkAOYYB70gQIAAAQIECBAgQIAAAQJNBSQAmvKqnAABAgQIECBAgAABAgQIzCEgATDHOOgFAQIECBAgQIAAAQIECBBoKiAB0JRX5QQIECBAgAABAgQIECBAYA4BCYA5xkEvCBAgQIAAAQIECBAgQIBAUwEJgKa8KidAgAABAgQIECBAgAABAnMISADMMQ56QYAAAQIECBAgQIAAAQIEmgpIADTlVTkBAgQIECBAgAABAgQIEJhDQAJgjnHQCwIECBAgQIAAAQIECBAg0FTg29PTU9MGPqv85eXls480fd/1G/+mE+yTys1/8f/JFGn6tvXP+td0gn1SufXP+vfJFGn6tvXP+td0gn1SufXP+vfJFGn6thMATXlVToAAAQIECBAgQIAAAQIE5hCQAJhjHPSCAAECBAgQIECAAAECBAg0FZAAaMqrcgIECBAgQIAAAQIECBAgMIeABMAc46AXBAgQIECAAAECBAgQIECgqYAEQFNelRMgQIAAAQIECBAgQIAAgTkEJADmGAe9IECAAAECBAgQIECAAAECTQUkAJryqpwAAQIECBAgQIAAAQIECMwhIAEwxzjoBQECBAgQIECAAAECBAgQaCogAdCUV+UECBAgQIAAAQIECBAgQGAOAQmAOcZBLwgQIECAAAECBAgQIECAQFMBCYCmvConQIAAAQIECBAgQIAAAQJzCNydADguy9ArOV+fh7b/uhyHtn99OA9tfzkOvv7zw9Drfz0uQ9s/P1+Htj88/gdf/+jxvw6e/8Pjf/D6N3r9H73/if+x67/4H3v/I/7Hzv/R9z/2/8H3//b/u+7/704A3NW6LxMgQIAAAQIECBAgQIAAAQJdBCQAujBrhAABAgQIECBAgAABAgQIjBX4tqX5+thfli8dfhJQH3vM8unwY8slrfpOfewry4/LZVVdWz5cH/vP8uHttKXKVd+pj/1mebl0uP7q2HMegzyc3lZdy5YP18e+svx4WbZUueo79bG3LJ9+HFbVteXDGe/l+1nuEv/Vsf/e15/jXa4/yz3GP+d7aT/LPeZ/xntpP8td4r869td7/cv1vlx/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XO9HrP+5yZebgSz3iL+Md/Hff/0X/+8P+eJf/Of9QI/1z/7//pBr/+///Jf7vf3/vucfPwHosWJqgwABAgQIECBAgAABAgQIDBaQABg8AJonQIAAAQIECBAgQIAAAQI9BO5OAORx0B4drtvoffy/bj+PA9fv9Sjnccge7dVt5HGw+r0e5d7H3+pryuPA9Xs9yqOvf/T4j57/w+M/fg7QY77VbYxe//M4YN23HmXxv/Rg/sc2xP/7zwH+EanhG+J/aaj7edX2//a/+f9oFOz/7z+H+Mip1Xv2/3W/+a/H4e4EQF2hMgECBAgQIECAAAECBAgQIDCfgATAfGOiRwQIECBAgAABAgQIECBAYHcBCYDdSVVIgAABAgQIECBAgAABAgTmE5AAmG9M9IgAAQIECBAgQIAAAQIECOwuIAGwO6kKCRAgQIAAAQIECBAgQIDAfAISAPONiR4RIECAAAECBAgQIECAAIHdBSQAdidVIQECBAgQIECAAAECBAgQmE9AAmC+MdEjAgQIECBAgAABAgQIECCwu4AEwO6kKiRAgAABAgQIECBAgAABAvMJSADMNyZ6RIAAAQIECBAgQIAAAQIEdheQANidVIUECBAgQIAAAQIECBAgQGA+AQmA+cZEjwgQIECAAAECBAgQICBcDOoAABxJSURBVECAwO4C//7+/ft/dq91RYVPT08rPr3/R19eXvavdEWNrt/4r5guu3/U/Bf/u0+qFRVa/6x/K6bL7h+1/ln/dp9UKyq0/ln/VkyX3T9q/fuz1z8nAHYPKRUSIECAAAECBAgQIECAAIH5BCQA5hsTPSJAgAABAgQIECBAgAABArsLSADsTqpCAgQIECBAgAABAgQIECAwn4AEwHxjokcECBAgQIAAAQIECBAgQGB3AQmA3UlVSIAAAQIECBAgQIAAAQIE5hOQAJhvTPSIAAECBAgQIECAAAECBAjsLiABsDupCgkQIECAAAECBAgQIECAwHwCEgDzjYkeESBAgAABAgQIECBAgACB3QUkAHYnVSEBAgQIECBAgAABAgQIEJhPQAJgvjHRIwIECBAgQIAAAQIECBAgsLuABMDupCokQIAAAQIECBAgQIAAAQLzCdydALg+nIde1XI8Dm3/en4Y2v7rcRna/vn5OrT94/JnX//o8R89/4fH/+D173UZu/6dr8/if6CA+B87/0ff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/+vufzclAPImpyx+WW49+KX+HOQy+Fnu0X4+5JaH3yz3aD83+bL5Z7lH+3m9I64/x3vE+Od8HzH/8yG/PPxnucf45yZfHv6z3KP9fMgvD/9Z7tF+PuSXh/8s92g/413891//xf/7Q774F/95P9Bj/bP/v9/k2//t/3k/0CP+Mt7d//d//s37/Xvj308AekSMNggQIECAAAECBAgQIECAwGABCYDBA6B5AgQIECBAgAABAgQIECDQQ+DuBEAeh+zR4bqNPA5Wv9ejnMdherRXt9H7+E/d/ujrHz3+o+d/Hgeqx6ZHuffx//qaeh//r9vvffy/bl/8LzVJ17L4f/85QFf4vxoT/8sI9lub9v/2/82rG/ZPXtj/338O8ROe5v9k/2//3zz7aBDd/983/+9OAHw0ON4jQIAAAQIECBAgQIAAAQIE5hCQAJhjHPSCAAECBAgQIECAAAECBAg0FZAAaMqrcgIECBAgQIAAAQIECBAgMIeABMAc46AXBAgQIECAAAECBAgQIECgqYAEQFNelRMgQIAAAQIECBAgQIAAgTkEJADmGAe9IECAAAECBAgQIECAAAECTQUkAJryqpwAAQIECBAgQIAAAQIECMwhIAEwxzjoBQECBAgQIECAAAECBAgQaCogAdCUV+UECBAgQIAAAQIECBAgQGAOAQmAOcZBLwgQIECAAAECBAgQIECAQFMBCYCmvConQIAAAQIECBAgQIAAAQJzCEgAzDEOekGAAAECBAgQIECAAAECBJoKfHt6emrawGeVv7y8fPaRpu+7fuPfdIJ9Urn5L/4/mSJN37b+Wf+aTrBPKrf+Wf8+mSJN37b+Wf+aTrBPKrf+Wf8+mSJN33YCoCmvygkQIECAAAECBAgQIECAwBwCEgBzjINeECBAgAABAgQIECBAgACBpgISAE15VU6AAAECBAgQIECAAAECBOYQkACYYxz0ggABAgQIECBAgAABAgQINBWQAGjKq3ICBAgQIECAAAECBAgQIDCHgATAHOOgFwQIECBAgAABAgQIECBAoKmABEBTXpUTIECAAAECBAgQIECAAIE5BCQA5hgHvSBAgAABAgQIECBAgAABAk0FJACa8qqcAAECBAgQIECAAAECBAjMISABMMc46AUBAgQIECBAgAABAgQIEGgqIAHQlFflBAgQIECAAAECBAgQIEBgDoG7EwCv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" + } + ] +} diff --git a/examples/testbed/assets/happy-tree.png b/examples/testbed/assets/happy-tree.png new file mode 100755 index 0000000..fc86db3 Binary files /dev/null and b/examples/testbed/assets/happy-tree.png differ diff --git a/examples/testbed/assets/texture-sep/texture-sep.bin b/examples/testbed/assets/texture-sep/texture-sep.bin new file mode 100644 index 0000000..9322438 Binary files /dev/null and b/examples/testbed/assets/texture-sep/texture-sep.bin differ diff --git a/examples/testbed/assets/texture-sep/texture-sep.gltf b/examples/testbed/assets/texture-sep/texture-sep.gltf new file mode 100644 index 0000000..8e9d366 --- /dev/null +++ b/examples/testbed/assets/texture-sep/texture-sep.gltf @@ -0,0 +1,137 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.6", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"uvgrid", + "uri":"uvgrid.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"texture-sep.bin" + } + ] +} diff --git a/examples/testbed/assets/texture-sep/uvgrid.png b/examples/testbed/assets/texture-sep/uvgrid.png new file mode 100644 index 0000000..06aad7c Binary files /dev/null and b/examples/testbed/assets/texture-sep/uvgrid.png differ diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs new file mode 100644 index 0000000..4a74aa7 --- /dev/null +++ b/examples/testbed/src/main.rs @@ -0,0 +1,214 @@ +use std::ops::DerefMut; + +use lyra_engine::{math, ecs::{World, components::{mesh::MeshComponent, transform::TransformComponent, camera::CameraComponent, model::ModelComponent}, atomicell::{Ref, RefMut}, EventQueue}, render::{mesh::Mesh, material::Material, vertex::Vertex, window::{CursorGrabMode, WindowOptions}}, math::Transform, input::{KeyCode, InputButtons, MouseMotion}, game::Game, change_tracker::Ct}; + +use lyra_engine::assets::{ResourceManager, Texture, Model}; + +use tracing::debug; + +/* pub 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 +]; */ + +pub const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + +#[derive(Debug, Default)] +struct Point2d { + x: i32, + y: i32, +} + +impl std::fmt::Display for Point2d { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(x={}, y={})", self.x, self.y) + } +} + +impl Point2d { + pub fn new(x: i32, y: i32) -> Self { + Self { + x, + y, + } + } +} + +#[derive(Debug, Default)] +struct Point3d { + x: i32, + y: i32, + z: i32, +} + +impl std::fmt::Display for Point3d { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(x={}, y={}, z={})", self.x, self.y, self.z) + } +} + +impl Point3d { + pub fn new(x: i32, y: i32, z: i32) -> Self { + Self { + x, + y, + z, + } + } +} + +#[async_std::main] +async fn main() { + let setup_sys = |world: &mut World| -> anyhow::Result<()> { + /* { + let mut window_options = world.get_resource_mut::>().unwrap(); + window_options.cursor_grab = CursorGrabMode::Confined; + window_options.cursor_visible = false; + } */ + + let mut resman = world.get_resource_mut::().unwrap(); + let diffuse_texture = resman.request::("assets/happy-tree.png").unwrap(); + let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); + let cube_model = resman.request::("assets/texture-sep/texture-sep.gltf").unwrap(); + drop(resman); + + /* world.spawn(( + ModelComponent(cube_model.clone()), + TransformComponent::from(Transform::from_xyz(3.0, 0.5, -2.2)), + )); */ + + world.spawn(( + ModelComponent(antique_camera_model), + TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)), + )); + + let mut camera = CameraComponent::new_3d(); + camera.transform.translation += math::Vec3::new(0.0, 0.0, 7.5); + //camera.transform.rotate_y(Angle::Degrees(-25.0)); + camera.transform.rotate_z(math::Angle::Degrees(-90.0)); + world.spawn((camera,)); + + Ok(()) + }; + + let fps_system = |world: &mut World| -> anyhow::Result<()> { + let mut counter: RefMut = world.get_resource_mut().unwrap(); + + let fps = counter.tick(); + + debug!("FPS: {fps}"); + + Ok(()) + }; + let fps_plugin = move |game: &mut Game| { + let world = game.world(); + world.insert_resource(fps_counter::FPSCounter::new()); + + game.with_system("fps", fps_system, &["input"]); + }; + + let jiggle_system = |world: &mut World| -> anyhow::Result<()> { + let keys = world.get_resource::>(); + if keys.is_none() { + return Ok(()); + } + let keys = keys.unwrap(); + + let speed = 0.01; + let rot_speed = 1.0; + + let mut dir_x = 0.0; + let mut dir_y = 0.0; + let mut dir_z = 0.0; + + let mut rot_x = 0.0; + let mut rot_y = 0.0; + + if keys.is_pressed(KeyCode::A) { + dir_x += speed; + } + + if keys.is_pressed(KeyCode::D) { + dir_x -= speed; + } + + if keys.is_pressed(KeyCode::S) { + dir_y += speed; + } + + if keys.is_pressed(KeyCode::W) { + dir_y -= speed; + } + + if keys.is_pressed(KeyCode::E) { + dir_z += speed; + } + + if keys.is_pressed(KeyCode::Q) { + dir_z -= speed; + } + + if keys.is_pressed(KeyCode::Left) { + rot_y -= rot_speed; + } + + if keys.is_pressed(KeyCode::Right) { + rot_y += rot_speed; + } + + if keys.is_pressed(KeyCode::Up) { + rot_x -= rot_speed; + } + + if keys.is_pressed(KeyCode::Down) { + rot_x += rot_speed; + } + + drop(keys); + + if dir_x == 0.0 && dir_y == 0.0 && dir_z == 0.0 && rot_x == 0.0 && rot_y == 0.0 { + return Ok(()); + } + + //debug!("moving by ({}, {})", dir_x, dir_y); + + for transform in world.query_mut::<(&mut TransformComponent,)>().iter_mut() { + let t = &mut transform.transform; + //debug!("Translation: {}", t.translation); + //debug!("Rotation: {}", t.rotation); + + /* t.translation += glam::Vec3::new(0.0, 0.001, 0.0); + t.translation.x *= -1.0; */ + t.translation.x += dir_x; + t.translation.y += dir_y; + t.translation.z += dir_z; + t.rotate_x(math::Angle::Degrees(rot_x)); + t.rotate_y(math::Angle::Degrees(rot_y)); + } + + let events = world.get_resource_mut::().unwrap(); + if let Some(mm) = events.read_events::() { + debug!("Mouse motion: {:?}", mm); + } + + Ok(()) + }; + + let jiggle_plugin = move |game: &mut Game| { + game.with_system("jiggle", jiggle_system, &["input"]); + }; + + Game::initialize().await + .with_plugin(lyra_engine::DefaultPlugins) + .with_startup_system(setup_sys) + //.with_plugin(fps_plugin) + .with_plugin(jiggle_plugin) + .run().await; +} diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml index 0207327..ae214bf 100644 --- a/lyra-resource/Cargo.toml +++ b/lyra-resource/Cargo.toml @@ -7,6 +7,15 @@ edition = "2021" [dependencies] anyhow = "1.0.75" +base64 = "0.21.4" +edict = "0.5.0" +glam = "0.24.1" +gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness"] } image = "0.24.7" +# not using custom matcher, or file type from file path +infer = { version = "0.15.0", default-features = false } +mime = "0.3.17" +percent-encoding = "2.3.0" thiserror = "1.0.48" +tracing = "0.1.37" uuid = { version = "1.4.1", features = ["v4"] } diff --git a/lyra-resource/src/lib.rs b/lyra-resource/src/lib.rs index 347aa03..f78b8ab 100644 --- a/lyra-resource/src/lib.rs +++ b/lyra-resource/src/lib.rs @@ -9,3 +9,11 @@ pub use texture::*; pub mod loader; pub use loader::*; + +pub mod model; +pub use model::*; + +pub mod material; +pub use material::*; + +pub(crate) mod util; \ No newline at end of file diff --git a/lyra-resource/src/loader/image.rs b/lyra-resource/src/loader/image.rs new file mode 100644 index 0000000..8a1940b --- /dev/null +++ b/lyra-resource/src/loader/image.rs @@ -0,0 +1,115 @@ +use std::{fs::File, sync::Arc, io::Read}; + +use image::ImageError; + +use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource, ResourceManager}; + +use super::{LoaderError, ResourceLoader}; + +impl From for LoaderError { + fn from(value: ImageError) -> Self { + LoaderError::DecodingError(value.into()) + } +} + +/// A struct that implements the `ResourceLoader` trait used for loading textures. +#[derive(Default)] +pub struct ImageLoader; + +impl ResourceLoader for ImageLoader { + fn extensions(&self) -> &[&str] { + &[ + // the extensions of these are the names of the formats + "bmp", "dds", "gif", "ico", "jpeg", "jpg", "png", "qoi", "tga", "tiff", "webp", + + // farbfeld + "ff", + + // pnm + "pnm", "pbm", "pgm", "ppm", + ] + } + + fn mime_types(&self) -> &[&str] { + &[ + "image/bmp", "image/vnd.ms-dds", "image/gif", "image/x-icon", "image/jpeg", + "image/png", "image/qoi", "image/tga", "image/tiff", "image/webp", + + // no known mime for farbfeld + + // pnm, pbm, pgm, ppm + "image/x-portable-anymap", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", + ] + } + + fn load(&self, _resource_manager: &mut ResourceManager, path: &str) -> Result, LoaderError> { + // check if the file is supported by this loader + if !self.does_support_file(path) { + return Err(LoaderError::UnsupportedExtension(path.to_string())); + } + + // read file bytes + let mut file = File::open(path)?; + let mut buf = vec![]; + file.read_to_end(&mut buf)?; + + // load the image and construct Resource + let image = image::load_from_memory(&buf) + .map_err(|e| match e { + ImageError::IoError(e) => LoaderError::IoError(e), + _ => LoaderError::DecodingError(e.into()), + })?; + let texture = Texture { + image, + }; + let res = Resource::with_data(path, texture); + + Ok(Arc::new(res)) + } + + fn load_bytes(&self, _resource_manager: &mut ResourceManager, bytes: Vec, offset: usize, length: usize) -> Result, LoaderError> { + let image = image::load_from_memory(&bytes[offset..(length-offset)]) + .map_err(|e| match e { + ImageError::IoError(e) => LoaderError::IoError(e), + _ => LoaderError::DecodingError(e.into()), + })?; + let texture = Texture { + image, + }; + let res = Resource::with_data(&uuid::Uuid::new_v4().to_string(), texture); + + Ok(Arc::new(res)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn get_image(path: &str) -> String { + let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + + format!("{manifest}/test_files/img/{path}") + } + + #[test] + fn check_unsupport() { + let loader = ImageLoader::default(); + assert_eq!(loader.does_support_file("test.gltf"), false); + } + + /// Tests loading an image + #[test] + fn image_load() { + let mut manager = ResourceManager::new(); + let loader = ImageLoader::default(); + loader.load(&mut manager, &get_image("squiggles.png")).unwrap(); + } + + #[test] + fn image_load_unsupported() { + let mut manager = ResourceManager::new(); + let loader = ImageLoader::default(); + assert!(loader.load(&mut manager, &get_image("squiggles.gltf")).is_err()); + } +} \ No newline at end of file diff --git a/lyra-resource/src/loader/mod.rs b/lyra-resource/src/loader/mod.rs index 78e5e23..06d13df 100644 --- a/lyra-resource/src/loader/mod.rs +++ b/lyra-resource/src/loader/mod.rs @@ -1,10 +1,11 @@ -pub mod texture; +pub mod image; +pub mod model; -use std::{io, sync::Arc, fs::File}; +use std::{io, sync::Arc, path::Path, ffi::OsStr}; use thiserror::Error; -use crate::resource_manager::ResourceStorage; +use crate::{resource_manager::ResourceStorage, ResourceManager}; #[derive(Error, Debug)] pub enum LoaderError { @@ -15,7 +16,7 @@ pub enum LoaderError { UnsupportedExtension(String), #[error("IOError: '{0}'")] - IOError(io::Error), + IoError(io::Error), // From is implemented for this field in each loader module #[error("Decoding error: '{0}'")] @@ -24,12 +25,50 @@ pub enum LoaderError { impl From for LoaderError { fn from(value: io::Error) -> Self { - LoaderError::IOError(value) + LoaderError::IoError(value) } } pub trait ResourceLoader: Send + Sync { + /// Returns the extensions that this loader supports. fn extensions(&self) -> &[&str]; - fn does_support_file(&self, path: &str) -> bool; - fn load(&self, path: &str) -> Result, LoaderError>; + /// Returns the mime types that this loader supports. + fn mime_types(&self) -> &[&str]; + + /// Returns true if this loader supports the file. + fn does_support_file(&self, path: &str) -> bool { + match Path::new(path).extension().and_then(OsStr::to_str) { + Some(ext) => { + self.extensions().contains(&ext) + }, + _ => false, + } + } + + /// Returns true if this loader supports the mime type. + fn does_support_mime(&self, mime: &str) -> bool { + self.mime_types().contains(&mime) + } + + /// Load a resource from a path. + fn load(&self, resource_manager: &mut ResourceManager, path: &str) -> Result, LoaderError>; + /// Load a resource from bytes. + fn load_bytes(&self, resource_manager: &mut ResourceManager, bytes: Vec, offset: usize, length: usize) -> Result, LoaderError>; +} + + +#[cfg(test)] +mod tests { + use super::{*, image::ImageLoader}; + + /// Ensure that `does_support_file` works + #[test] + fn check_support() { + let loader = ImageLoader::default(); + let extensions = loader.extensions(); + let fake_paths: Vec = extensions.iter().map(|e| format!("a.{}", e)).collect(); + for path in fake_paths.iter() { + assert!(loader.does_support_file(&path)); + } + } } \ No newline at end of file diff --git a/lyra-resource/src/loader/model.rs b/lyra-resource/src/loader/model.rs new file mode 100644 index 0000000..daae8e4 --- /dev/null +++ b/lyra-resource/src/loader/model.rs @@ -0,0 +1,230 @@ +use std::{sync::Arc, path::{Path, PathBuf}}; + +use base64::Engine; +use thiserror::Error; + +use crate::{ResourceLoader, LoaderError, Mesh, Model, MeshVertexAttribute, VertexAttributeData, Resource, Material, MeshIndices, ResourceManager, util}; + +use tracing::debug; + +impl From for LoaderError { + fn from(value: gltf::Error) -> Self { + LoaderError::DecodingError(value.into()) + } +} + +#[derive(Error, Debug)] +enum ModelLoaderError { + #[error("The model ({0}) is missing the BIN section in the gltf file")] + MissingBin(String), + #[error("There was an error with decoding a uri defined in the model: '{0}'")] + UriDecodingError(util::UriReadError), +} + +impl From for LoaderError { + fn from(value: ModelLoaderError) -> Self { + LoaderError::DecodingError(value.into()) + } +} + +pub(crate) struct GltfLoadContext<'a> { + pub resource_manager: &'a mut ResourceManager, + pub gltf: &'a gltf::Gltf, + /// Path to the gltf + pub gltf_path: &'a str, + /// The path to the directory that the gltf is contained in. + pub gltf_parent_path: &'a str, + /// List of buffers in the gltf + pub buffers: &'a Vec>, +} + +#[derive(Default)] +pub struct ModelLoader; + +impl ModelLoader { + /* fn parse_uri(containing_path: &str, uri: &str) -> Option> { + let uri = uri.strip_prefix("data")?; + let (mime, data) = uri.split_once(",")?; + + let (_mime, is_base64) = match mime.strip_suffix(";base64") { + Some(mime) => (mime, true), + None => (mime, false), + }; + + if is_base64 { + Some(base64::engine::general_purpose::STANDARD.decode(data).unwrap()) + } else { + let full_path = format!("{containing_path}/{data}"); + let buf = std::fs::read(&full_path).unwrap(); + Some(buf) + } + } */ + + fn process_node(&self, buffers: &Vec>, materials: &Vec, node: gltf::Node<'_>) -> Vec { + let mut meshes = vec![]; + if let Some(mesh) = node.mesh() { + for prim in mesh.primitives() { + let reader = prim.reader(|buf| Some(buffers[buf.index()].as_slice())); + + let mut new_mesh = Mesh::default(); + + // read the positions + if let Some(pos) = reader.read_positions() { + if prim.mode() != gltf::mesh::Mode::Triangles { + todo!("Load position primitives that aren't triangles"); // TODO + } + + let pos: Vec = pos.map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos)); + } + + // read the normals + if let Some(norms) = reader.read_normals() { + let norms: Vec = norms.map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::Normals, VertexAttributeData::Vec3(norms)); + } + + // read the tangents + if let Some(tangents) = reader.read_tangents() { + let tangents: Vec = tangents.map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::Tangents, VertexAttributeData::Vec4(tangents)); + } + + // read tex coords + if let Some(tex_coords) = reader.read_tex_coords(0) { + let tex_coords: Vec = tex_coords.into_f32().map(|t| t.into()).collect(); + new_mesh.add_attribute(MeshVertexAttribute::TexCoords, VertexAttributeData::Vec2(tex_coords)); + } + + // read the indices + if let Some(indices) = reader.read_indices() { + let indices: MeshIndices = match indices { + // wpgu doesn't support u8 indices, so those must be converted to u16 + gltf::mesh::util::ReadIndices::U8(i) => MeshIndices::U16(i.map(|i| i as u16).collect()), + gltf::mesh::util::ReadIndices::U16(i) => MeshIndices::U16(i.collect()), + gltf::mesh::util::ReadIndices::U32(i) => MeshIndices::U32(i.collect()), + }; + + new_mesh.indices = Some(indices); + } + + let mat = materials.get(prim.material().index().unwrap()).unwrap(); + new_mesh.set_material(mat.clone()); + //prim.material(). + + meshes.push(new_mesh); + } + } + + for child in node.children() { + let mut child_meshes = self.process_node(buffers, materials, child); + meshes.append(&mut child_meshes); + } + + meshes + } +} + +impl ResourceLoader for ModelLoader { + fn extensions(&self) -> &[&str] { + &[ + "gltf", "glb" + ] + } + + fn mime_types(&self) -> &[&str] { + &[] + } + + fn load(&self, resource_manager: &mut ResourceManager, path: &str) -> Result, crate::LoaderError> { + // check if the file is supported by this loader + if !self.does_support_file(path) { + return Err(LoaderError::UnsupportedExtension(path.to_string())); + } + + let mut parent_path = PathBuf::from(path); + parent_path.pop(); + let parent_path = parent_path.display().to_string(); + + /* let (document, buffers, images) = gltf::import(&path)?; + let buffers: Vec> = buffers.into_iter().map(|b| b.0).collect(); + + let scene = document.scenes().next().unwrap(); + + let materials: Vec = document.materials() + .map(|mat| Material::from_gltf(resource_manager, &parent_path, mat)).collect(); + + let meshes: Vec = scene.nodes() + .map(|node| self.process_node(&buffers, &materials, node)) + .flatten().collect(); */ + + let gltf = gltf::Gltf::open(path)?; + + let mut use_bin = false; + let buffers: Vec> = gltf.buffers().map(|b| match b.source() { + gltf::buffer::Source::Bin => { + use_bin = true; + gltf.blob.as_deref().map(|v| v.to_vec()) + .ok_or(ModelLoaderError::MissingBin(path.to_string())) + }, + gltf::buffer::Source::Uri(uri) => util::gltf_read_buffer_uri(&parent_path, uri) + .map_err(|e| ModelLoaderError::UriDecodingError(e)), + }).flatten().collect(); + + // TODO: Read in multiple scenes + let scene = gltf.scenes().next().unwrap(); + + let mut context = GltfLoadContext { + resource_manager, + gltf: &gltf, + gltf_path: path, + gltf_parent_path: &parent_path, + buffers: &buffers, + }; + + let materials: Vec = gltf.materials() + .map(|mat| Material::from_gltf(&mut context, mat)).collect(); + + let meshes: Vec = scene.nodes() + .map(|node| self.process_node(&buffers, &materials, node)) + .flatten().collect(); + debug!("Loaded {} meshes, and {} materials from '{}'", meshes.len(), materials.len(), path); + + Ok(Arc::new(Resource::with_data(path, Model::new(meshes)))) + } + + fn load_bytes(&self, resource_manager: &mut ResourceManager, bytes: Vec, offset: usize, length: usize) -> Result, LoaderError> { + todo!() + } +} + +#[cfg(test)] +mod tests { + use crate::ResourceLoader; + use super::*; + + fn test_file_path(path: &str) -> String { + let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + + format!("{manifest}/test_files/gltf/{path}") + } + + #[test] + fn test_loading() { + let path = test_file_path("texture-embedded.gltf"); + + let mut manager = ResourceManager::new(); + let loader = ModelLoader::default(); + let model = loader.load(&mut manager, &path).unwrap(); + let model = Arc::downcast::>(model.as_arc_any()).unwrap(); + let model = model.data.as_ref().unwrap(); + assert_eq!(model.meshes.len(), 1); // There should only be 1 mesh + let mesh = &model.meshes[0]; + assert!(mesh.position().unwrap().len() > 0); + assert!(mesh.normals().unwrap().len() > 0); + assert!(mesh.tex_coords().unwrap().len() > 0); + assert!(mesh.indices.clone().unwrap().len() > 0); + assert!(mesh.material().base_color_texture.is_some()); + let _mesh_mat = mesh.material(); // inner panic if material was not loaded + } +} \ No newline at end of file diff --git a/lyra-resource/src/loader/texture.rs b/lyra-resource/src/loader/texture.rs deleted file mode 100644 index c781ffa..0000000 --- a/lyra-resource/src/loader/texture.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::{fs::File, sync::Arc, path::Path, ffi::OsStr, io::Read}; - -use image::ImageError; - -use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource}; - -use super::{LoaderError, ResourceLoader}; - -impl From for LoaderError { - fn from(value: ImageError) -> Self { - LoaderError::DecodingError(anyhow::Error::from(value)) - } -} - -/// A struct that implements the `ResourceLoader` trait used for loading textures. -#[derive(Default)] -pub struct TextureLoader; - -impl ResourceLoader for TextureLoader { - fn extensions(&self) -> &[&str] { - &[ - // the extensions of these are the names of the formats - "bmp", "dds", "gif", "ico", "jpeg", "jpg", "png", "qoi", "tga", "tiff", "webp", - - // farbfeld - "ff", - - // pnm - "pnm", "pbm", "pgm", "ppm", "pam", - ] - } - - fn does_support_file(&self, path: &str) -> bool { - match Path::new(path).extension().and_then(OsStr::to_str) { - Some(ext) => { - self.extensions().contains(&ext) - }, - _ => false, - } - } - - fn load(&self, path: &str) -> Result, LoaderError> { - // check if the file is supported by this loader - if !self.does_support_file(path) { - return Err(LoaderError::UnsupportedExtension(path.to_string())); - } - - // read file bytes - let mut file = File::open(path)?; - let mut buf = vec![]; - file.read_to_end(&mut buf)?; - - // load the image and construct Resource - let image = image::load_from_memory(&buf) - .map_err(|e| match e { - ImageError::IoError(e) => LoaderError::IOError(e), - _ => LoaderError::DecodingError(e.into()), - })?; - let texture = Texture { - image, - }; - let res = Resource::with_data(path, texture); - - Ok(Arc::new(res)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn get_image(path: &str) -> String { - let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - - format!("{manifest}/test_files/img/{path}") - } - - /// Ensure that `does_support_file` works - #[test] - fn check_support() { - let loader = TextureLoader::default(); - let extensions = loader.extensions(); - let fake_paths: Vec = extensions.iter().map(|e| format!("a.{}", e)).collect(); - for path in fake_paths.iter() { - assert!(loader.does_support_file(&path)); - } - } - - #[test] - fn check_unsupport() { - let loader = TextureLoader::default(); - assert_eq!(loader.does_support_file("test.gltf"), false); - } - - /// Tests loading an image - #[test] - fn image_load() { - let loader = TextureLoader::default(); - loader.load(&get_image("squiggles.png")).unwrap(); - } - - #[test] - fn image_load_unsupported() { - let loader = TextureLoader::default(); - assert!(loader.load(&get_image("squiggles.gltf")).is_err()); - } -} \ No newline at end of file diff --git a/lyra-resource/src/material.rs b/lyra-resource/src/material.rs new file mode 100644 index 0000000..7294486 --- /dev/null +++ b/lyra-resource/src/material.rs @@ -0,0 +1,244 @@ +use std::{fs::File, io::{BufReader, Read}, collections::hash_map::DefaultHasher, hash::{Hash, Hasher}}; + +use crate::{Texture, ResHandle, ResourceManager, util, loader::model::GltfLoadContext}; + +/// PBR metallic roughness +#[derive(Clone, Debug, Default)] +pub struct PbrRoughness { + /// The rgba base color of the PBR material + pub base_color: [f32; 4], + /// The metalness of the material + /// From 0.0 (non-metal) to 1.0 (metal) + pub metallic: f32, + /// The roughness of the material + /// From 0.0 (smooth) to 1.0 (rough) + pub roughness: f32, + // TODO: base_color_texture and metallic_roughness_texture +} + +impl From> for PbrRoughness { + fn from(value: gltf::material::PbrMetallicRoughness) -> Self { + PbrRoughness { + base_color: value.base_color_factor(), + metallic: value.metallic_factor(), + roughness: value.roughness_factor(), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct PbrGlossiness { + /// The rgba diffuse color of the material + pub diffuse_color: glam::Vec4, + // The base color texture + // pub diffuse_texture // TODO + pub specular: glam::Vec3, + /// The glossiness factor of the material. + /// From 0.0 (no glossiness) to 1.0 (full glossiness) + pub glossiness: f32, + // pub glossiness_texture // TODO +} + +impl From> for PbrGlossiness { + fn from(value: gltf::material::PbrSpecularGlossiness) -> Self { + PbrGlossiness { + diffuse_color: value.diffuse_factor().into(), + specular: value.specular_factor().into(), + glossiness: value.glossiness_factor() + } + } +} + +/// The alpha rendering mode of a material. +/// This is essentially a re-export of gltf::material::AlphaMode +#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] +pub enum AlphaMode { + /// The alpha value is ignored and the rendered output is fully opaque. + #[default] + Opaque = 1, + + /// The rendered output is either fully opaque or fully transparent depending on + /// the alpha value and the specified alpha cutoff value. + Mask, + + /// The alpha value is used, to determine the transparency of the rendered output. + /// The alpha cutoff value is ignored. + Blend, +} + +impl From for AlphaMode { + fn from(value: gltf::material::AlphaMode) -> Self { + match value { + gltf::material::AlphaMode::Opaque => AlphaMode::Opaque, + gltf::material::AlphaMode::Mask => AlphaMode::Mask, + gltf::material::AlphaMode::Blend => AlphaMode::Blend, + } + } +} + +#[derive(Clone, Default)] +pub struct Material { + pub shader_uuid: Option, + pub name: Option, + pub double_sided: bool, + + //pub pbr_roughness: PbrRoughness, + /// The RGBA base color of the model. If a texture is supplied with `base_color_texture`, this value + /// will tint the texture. If a texture is not provided, this value would be the color of the Material. + pub base_color: glam::Vec4, + /// The metalness of the material + /// From 0.0 (non-metal) to 1.0 (metal) + pub metallic: f32, + /// The roughness of the material + /// From 0.0 (smooth) to 1.0 (rough) + pub roughness: f32, + /// The base color texture of the model. + pub base_color_texture: Option>, + + /// The metallic-roughness texture. + /// + /// The metalness values are sampled from the B channel. The roughness values are sampled from + /// the G channel. These values are linear. If other channels are present (R or A), they are + /// ignored for metallic-roughness calculations. + pub metallic_roughness_texture: Option>, + + /// A set of parameter values that are used to define the specular-glossiness material model + /// from Physically-Based Rendering (PBR) methodology. + /// GLTF extension: [KHR_materials_pbrSpecularGlossiness](https://kcoley.github.io/glTF/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness) + pub pbr_glossiness: Option, + + /// The optional alpha cutoff value of the material. + pub alpha_cutoff: Option, + + /// The alpha rendering mode of the material. The material's alpha rendering + /// mode enumeration specifying the interpretation of the alpha value of the main + /// factor and texture. + /// + /// * In `Opaque` mode (default) the alpha value is ignored + /// and the rendered output is fully opaque. + /// * In `Mask` mode, the rendered + /// output is either fully opaque or fully transparent depending on the alpha + /// value and the specified alpha cutoff value. + /// * In `Blend` mode, the alpha value is used to composite the source and + /// destination areas and the rendered output is combined with the background + /// using the normal painting operation (i.e. the Porter and Duff over + /// operator). + pub alpha_mode: AlphaMode, + + //pub texture: Option>, +} + +impl Material { + /// Get a uri's identifier + /// + /// I'm not actually sure how identifiable this would be + fn uri_ident(gltf_rel_path: &str, uri: &str) -> String { + let mut hasher = DefaultHasher::new(); + uri.hash(&mut hasher); + let hash = hasher.finish(); + + format!("{gltf_rel_path};{hash}") + } + + fn source_ident(gltf_rel_path: &str, src: &gltf::image::Source) -> Option { + match src { + gltf::image::Source::View { view, mime_type } => { + let buf = view.buffer(); + let src = buf.source(); + + match src { + gltf::buffer::Source::Bin => None, + gltf::buffer::Source::Uri(uri) => { + Some(Material::uri_ident(gltf_rel_path, uri)) + } + } + }, + gltf::image::Source::Uri { uri, mime_type } => { + Some(Material::uri_ident(gltf_rel_path, uri)) + }, + } + } + + fn read_source(context: &mut GltfLoadContext, src: gltf::image::Source) -> Result, util::UriReadError> { + let gltf_rel_path = context.gltf_parent_path; + // TODO: Don't copy sources + match src { + gltf::image::Source::View { view, mime_type: _ } => { + let buf = view.buffer(); + let src = buf.source(); + + let offset = view.offset(); + let len = view.length(); + + match src { + gltf::buffer::Source::Bin => { + let mut b = context.gltf.blob.clone().unwrap(); + b.drain(0..offset); + b.truncate(len); + Ok(b) + }, + gltf::buffer::Source::Uri(uri) => { + util::gltf_read_buffer_uri(gltf_rel_path, uri) + .map(|mut buf| { + buf.drain(0..offset); + buf.truncate(len); + buf + }) + } + } + }, + gltf::image::Source::Uri { uri, mime_type: _ } => { + util::gltf_read_buffer_uri(gltf_rel_path, uri) + }, + } + } + + fn load_texture(context: &mut GltfLoadContext, texture_info: gltf::texture::Info<'_>) -> ResHandle { + // TODO: texture_info.tex_coord() + let tex = texture_info.texture(); + let img = tex.source(); + let src = img.source(); + + let buf = Material::read_source(context, src).unwrap(); + let buflen = buf.len(); + let mime_type = infer::get(&buf).expect("Failed to get file type").mime_type(); + + context.resource_manager.load_bytes::(&uuid::Uuid::new_v4().to_string(), mime_type, + buf, 0, buflen).unwrap() + } + + /// Load the Material from a gltf::Material. + /// + /// `gltf_rel_path`: The relative path of the gltf file, + /// e.g. gltf model path is "resource/models/player.gltf", the relative path would be "resource/models/" + pub(crate) fn from_gltf(context: &mut GltfLoadContext, gltf_mat: gltf::Material) -> Self { + let pbr_rough = gltf_mat.pbr_metallic_roughness(); + let base_color = pbr_rough.base_color_factor().into(); + let metallic = pbr_rough.metallic_factor(); + let roughness = pbr_rough.roughness_factor(); + + let base_color_texture = pbr_rough.base_color_texture() + .map(|info| Material::load_texture(context, info)); + + let metallic_roughness_texture = pbr_rough.metallic_roughness_texture() + .map(|info| Material::load_texture(context, info)); + + Material { + name: gltf_mat.name() + .map(|s| s.to_string()), + double_sided: gltf_mat.double_sided(), + base_color, + metallic, + roughness, + pbr_glossiness: gltf_mat.pbr_specular_glossiness() + .map(|o| o.into()), + alpha_cutoff: gltf_mat.alpha_cutoff(), + alpha_mode: gltf_mat.alpha_mode().into(), + shader_uuid: None, + + // TODO + base_color_texture, + metallic_roughness_texture, + } + } +} \ No newline at end of file diff --git a/lyra-resource/src/model.rs b/lyra-resource/src/model.rs new file mode 100644 index 0000000..76ccfc6 --- /dev/null +++ b/lyra-resource/src/model.rs @@ -0,0 +1,147 @@ +use std::collections::HashMap; + +use crate::Material; + +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum MeshIndices { + //U8(Vec), + U16(Vec), + U32(Vec), +} + +impl MeshIndices { + pub fn len(&self) -> usize { + match self { + MeshIndices::U16(v) => v.len(), + MeshIndices::U32(v) => v.len(), + } + } +} + +/* impl From> for MeshIndices { + fn from(value: Vec) -> Self { + MeshIndices::U8(value) + } +} */ + +impl From> for MeshIndices { + fn from(value: Vec) -> Self { + MeshIndices::U16(value) + } +} + +impl From> for MeshIndices { + fn from(value: Vec) -> Self { + MeshIndices::U32(value) + } +} + +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum VertexAttributeData { + Vec2(Vec), + Vec3(Vec), + Vec4(Vec), +} + +impl VertexAttributeData { + /// Take self as a list of Vec2's + pub fn as_vec2(&self) -> &Vec { + match self { + VertexAttributeData::Vec2(v) => v, + _ => panic!("Attempt to get {self:?} as `Vec2`"), + } + } + + pub fn as_vec3(&self) -> &Vec { + match self { + VertexAttributeData::Vec3(v) => v, + _ => panic!("Attempt to get {self:?} as `Vec3`"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum MeshVertexAttribute { + Position, + Normals, + Tangents, + Colors, // TODO: Figure out best way to store color data + Joints, // TODO: Animation + TexCoords, + Weights, // TODO: Animation + MorphTargets, // TODO: Animation + /// Used during loading of the Mesh to process the materials taht it + MaterialRef, + Other(String), +} + +#[derive(Clone, edict::Component)] +pub struct Mesh { + pub uuid: uuid::Uuid, + pub attributes: HashMap, + pub indices: Option, + material: Option, +} + +impl Default for Mesh { + fn default() -> Self { + Self { + uuid: uuid::Uuid::new_v4(), + attributes: Default::default(), + indices: Default::default(), + material: Default::default() + } + } +} + +impl Mesh { + pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) { + self.attributes.insert(attribute, data); + } + + /// Try to get the position attribute of the Mesh + pub fn position(&self) -> Option<&Vec> { + self.attributes.get(&MeshVertexAttribute::Position) + .map(|p| p.as_vec3()) + } + + pub fn add_position(&mut self, pos: Vec) { + self.attributes.insert(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos)); + } + + /// Try to get the normals attribute of the Mesh + pub fn normals(&self) -> Option<&Vec> { + self.attributes.get(&MeshVertexAttribute::Normals) + .map(|p| p.as_vec3()) + } + + /// Try to get the texture coordinates attribute of the Mesh + pub fn tex_coords(&self) -> Option<&Vec> { + self.attributes.get(&MeshVertexAttribute::TexCoords) + .map(|p| p.as_vec2()) + } + + pub fn material(&self) -> Material { + self.material.clone().expect("This mesh is missing a material!") + } + + pub fn set_material(&mut self, val: Material) { + self.material = Some(val); + } +} + +#[derive(Clone, Default)] +pub struct Model { + pub meshes: Vec, + //pub material +} + +impl Model { + pub fn new(meshes: Vec) -> Self { + Self { + meshes, + } + } +} \ No newline at end of file diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs index dd70bac..331d5f8 100644 --- a/lyra-resource/src/resource.rs +++ b/lyra-resource/src/resource.rs @@ -16,6 +16,9 @@ pub struct Resource { pub state: ResourceState, } +/// A helper type to make it easier to use resources +pub type ResHandle = Arc>; + impl Resource { /// Create the resource with data, its assumed the state is `Ready` pub fn with_data(path: &str, data: T) -> Self { diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs index 09b699e..b334429 100644 --- a/lyra-resource/src/resource_manager.rs +++ b/lyra-resource/src/resource_manager.rs @@ -2,7 +2,7 @@ use std::{sync::Arc, collections::{HashMap, hash_map::DefaultHasher}, hash::{Has use thiserror::Error; -use crate::{resource::Resource, loader::{ResourceLoader, LoaderError, texture::TextureLoader}}; +use crate::{resource::Resource, loader::{ResourceLoader, LoaderError, image::ImageLoader, model::ModelLoader}}; pub trait ResourceStorage: Send + Sync + Any + 'static { fn as_any(&self) -> &dyn Any; @@ -31,6 +31,10 @@ pub enum RequestError { Loader(LoaderError), #[error("The file extension is unsupported: '{0}'")] UnsupportedFileExtension(String), + #[error("The mimetype is unsupported: '{0}'")] + UnsupportedMime(String), + #[error("The identifier is not found: '{0}'")] + IdentNotFound(String), } impl From for RequestError { @@ -39,16 +43,19 @@ impl From for RequestError { } } +/// A struct that stores all Manager data. This is requried for sending +//struct ManagerStorage + pub struct ResourceManager { resources: HashMap>, - loaders: Vec>, + loaders: Vec>, } impl ResourceManager { pub fn new() -> Self { Self { resources: HashMap::new(), - loaders: vec![ Box::new(TextureLoader::default()) ], + loaders: vec![ Arc::new(ImageLoader::default()), Arc::new(ModelLoader::default()) ], } } @@ -65,12 +72,14 @@ impl ResourceManager { .find(|l| l.does_support_file(path)) { // Load the resource and store it - let res = loader.load(path)?; + let loader = Arc::clone(&loader); // stop borrowing from self + let res = loader.load(self, path)?; self.resources.insert(path.to_string(), res.clone()); - // convert Arc to Arc + // cast Arc to Arc let res = res.as_arc_any(); - let res = res.downcast::>().expect("Failure to downcast resource"); + let res = res.downcast::>() + .expect("Failure to downcast resource! Does the loader return an `Arc>`?"); Ok(res) } else { @@ -79,6 +88,49 @@ impl ResourceManager { } } } + + /// Store bytes in the manager. If there is already an entry with the same identifier it will be updated. + /// + /// Panics: If there is already an entry with the same `ident`, and the entry is not bytes, this function will panic. + /// + /// Parameters: + /// * `ident` - The identifier to store along with these bytes. Make sure its unique to avoid overriding something. + /// * `bytes` - The bytes to store. + /// + /// Returns: The `Arc` to the now stored resource + pub fn load_bytes(&mut self, ident: &str, mime_type: &str, bytes: Vec, offset: usize, length: usize) -> Result>, RequestError> { + if let Some(loader) = self.loaders.iter() + .find(|l| l.does_support_mime(mime_type)) { + let loader = loader.clone(); + let res = loader.load_bytes(self, bytes, offset, length)?; + self.resources.insert(ident.to_string(), res.clone()); + // code here... + + // cast Arc to Arc + let res = res.as_arc_any(); + let res = res.downcast::>() + .expect("Failure to downcast resource! Does the loader return an `Arc>`?"); + + Ok(res) + } else { + Err(RequestError::UnsupportedMime(mime_type.to_string())) + } + } + + /// Requests bytes from the manager. + pub fn request_loaded_bytes(&mut self, ident: &str) -> Result>, RequestError> { + match self.resources.get(&ident.to_string()) { + Some(res) => { + let res = res.clone().as_arc_any(); + let res = res.downcast::>().expect("Failure to downcast resource"); + + Ok(res) + }, + None => { + Err(RequestError::IdentNotFound(ident.to_string())) + } + } + } } #[cfg(test)] @@ -125,7 +177,7 @@ mod tests { assert!( match err { // make sure the error is NotFound - RequestError::Loader(LoaderError::IOError(e)) if e.kind() == io::ErrorKind::NotFound => true, + RequestError::Loader(LoaderError::IoError(e)) if e.kind() == io::ErrorKind::NotFound => true, _ => false } ); diff --git a/lyra-resource/src/util.rs b/lyra-resource/src/util.rs new file mode 100644 index 0000000..1061d31 --- /dev/null +++ b/lyra-resource/src/util.rs @@ -0,0 +1,41 @@ +use base64::Engine; +use thiserror::Error; +use std::io; + +#[derive(Error, Debug)] +pub enum UriReadError { + #[error("IOError: '{0}'")] + IoError(io::Error), + + // From is implemented for this field in each loader module + #[error("Base64 decoding error: '{0}'")] + Base64Decode(base64::DecodeError), + + #[error("Some data was missing from the uri")] + None +} + +/// Read a buffer's uri string into a byte buffer. +/// +/// * `containing_path`: The path of the containing folder of the buffers "parent", +/// the parent being where this buffer is defined in, +/// i.e. parent="resources/models/player.gltf", containing="resource/models" +pub(crate) fn gltf_read_buffer_uri(containing_path: &str, uri: &str) -> Result, UriReadError> { + if let Some((mime, data)) = uri.strip_prefix("data") + .and_then(|uri| uri.split_once(",")) { + let (_mime, is_base64) = match mime.strip_suffix(";base64") { + Some(mime) => (mime, true), + None => (mime, false), + }; + + if is_base64 { + base64::engine::general_purpose::STANDARD.decode(data) + .map_err(|e| UriReadError::Base64Decode(e)) + } else { + Ok(data.as_bytes().to_vec()) + } + } else { + let full_path = format!("{containing_path}/{uri}"); + std::fs::read(&full_path).map_err(|e| UriReadError::IoError(e)) + } +} \ No newline at end of file diff --git a/lyra-resource/test_files/gltf/test-blobs.glb b/lyra-resource/test_files/gltf/test-blobs.glb new file mode 100644 index 0000000..8f2d6fb Binary files /dev/null and b/lyra-resource/test_files/gltf/test-blobs.glb differ diff --git a/lyra-resource/test_files/gltf/test-embedded.gltf b/lyra-resource/test_files/gltf/test-embedded.gltf new file mode 100644 index 0000000..5d42356 --- /dev/null +++ b/lyra-resource/test_files/gltf/test-embedded.gltf @@ -0,0 +1,121 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.5", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQA" + } + ] +} diff --git a/lyra-resource/test_files/gltf/test-sep.bin b/lyra-resource/test_files/gltf/test-sep.bin new file mode 100644 index 0000000..9322438 Binary files /dev/null and b/lyra-resource/test_files/gltf/test-sep.bin differ diff --git a/lyra-resource/test_files/gltf/test-sep.gltf b/lyra-resource/test_files/gltf/test-sep.gltf new file mode 100644 index 0000000..a100177 --- /dev/null +++ b/lyra-resource/test_files/gltf/test-sep.gltf @@ -0,0 +1,121 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.5", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"test-sep.bin" + } + ] +} diff --git a/lyra-resource/test_files/gltf/texture-bin.glb b/lyra-resource/test_files/gltf/texture-bin.glb new file mode 100644 index 0000000..9a3965b Binary files /dev/null and b/lyra-resource/test_files/gltf/texture-bin.glb differ diff --git a/lyra-resource/test_files/gltf/texture-embedded.gltf b/lyra-resource/test_files/gltf/texture-embedded.gltf new file mode 100644 index 0000000..5309758 --- /dev/null +++ b/lyra-resource/test_files/gltf/texture-embedded.gltf @@ -0,0 +1,142 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.6", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "bufferView":4, + "mimeType":"image/png", + "name":"uvgrid" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + }, + { + "buffer":0, + "byteLength":40222, + "byteOffset":840 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":41064, + "uri":"data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQAiVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAAANmVYSWZNTSoAAAAAGAAAAEgAAAABAAAASAAAAAEAAgEaAAUAAAABAAAACAEbAAUAAAABAAAAEAAAAABsNyUqAAAACW9GRnMAAAAAAAAAAADaKrbOAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4AezcwXXjuNIw0Df/6ZUjcAyKwhlo542icAbMwFFo452SUQyOwNv5H77zRl2Do5ZN0qii7TubESQSRV6wAAgN+a+Hh4e//1P43+PjY2H0//zn5eWlNL771/6VD6DnX/5XPn/6P/1f5fOn/9P/VT5/+j/9X+Xzp//72f3f/6t8+MQmQIAAAQIECBAgQIAAAQIEcgQsAOQ4i0KAAAECBAgQIECAAAECBEoFLACU8gtOgAABAgQIECBAgAABAgRyBCwA5DiLQoAAAQIECBAgQIAAAQIESgUsAJTyC06AAAECBAgQIECAAAECBHIELADkOItCgAABAgQIECBAgAABAgRKBSwAlPILToAAAQIECBAgQIAAAQIEcgQsAOQ4i0KAAAECBAgQIECAAAECBEoFLACU8gtOgAABAgQIECBAgAABAgRyBCwA5DiLQoAAAQIECBAgQIAAAQIESgUsAJTyC06AAAECBAgQIECAAAECBHIEVi8AHM9POVf6hyiv0/4Pn+S8fb475gT6Q5RpX3z/x7s/XFnO26/7KSfQH6Icn85/+CTn7f005QT6QxT5L///8GikvC3/pxTnPwWR//L/T89Gxvvyf8pg/mMM+S////hwJHwg/6dVyqsXAFZFdzIBAgQIECBAgAABAgQIECCQImABIIVZEAIECBAgQIAAAQIECBAgUCvwa0n4fttPLB92z0uqnHVOv+0/lu+n06y6lhzcb/uP5d3bYUmVs87pt/3H8nRKuP9u2/85lHeHt1n3suTgfttPLN+fpiVVzjqn3/Yfy4fn3ay6lhzcb/uP5VPCTwJivrfrj2X5L/+XPNNzzon53s6LZfk/zaFcdGzM91ZBLMt/+b/ooZpxUsz3dlosy/9phuSyQ2O+txpiWf7L/2VP1cfPivnezopl+T99HPK/Ry5aAIhJ3pI/lmdFX3hw/JLfvvzH8sIqZ50Wv+S3L/+xPKuihQfHL/nty38sL6xy1mnxS3778h/LsypaeHBM8pb8sbywylmnxS/57ct/LM+qaOHB8Ut++/IfywurnHVazHf5L//l//hFv5igMd/lv/Hf+D/F9Bj+Oo73xn/zf/P/8f/oF5M6zvfN/9fN//0EID5ZXhMgQIAAAQIECBAgQIAAgW8qYAHgmzas2yJAgAABAgQIECBAgAABAlFg9QJA3A4cK856nb39v7+v7O0/ffzs7X99/Oztv338uB2o/yyjHLcDZsTrY8TtwP1nGWX5P/43f7faUf5Pt3iGfyb/x//Nn1uNaPwf/zd/bvkb/3N//tO3hfG/+PlP+JtffZvHsvF/ihzpr7/6+L96ASBdXEACBAgQIECAAAECBAgQIEBgtoAFgNlkTiBAgAABAgQIECBAgAABAl9PwALA12szV0yAAAECBAgQIECAAAECBGYLWACYTeYEAgQIECBAgAABAgQIECDw9QQsAHy9NnPFBAgQIECAAAECBAgQIEBgtoAFgNlkTiBAgAABAgQIECBAgAABAl9PwALA12szV0yAAAECBAgQIECAAAECBGYLWACYTeYEAgQIECBAgAABAgQIECDw9QQsAHy9NnPFBAgQIECAAAECBAgQIEBgtoAFgNlkTiBAgAABAgQIECBAgAABAl9PwALA12szV0yAAAECBAgQIECAAAECBGYLWACYTeYEAgQIECBAgAABAgQIECDw9QR+PT4+ll71y8tLaXz3r/0rH0DPv/yvfP70f/q/yudP/6f/q3z+9H/6v8rnT/+n/6t8/uwAqNQXmwABAgQIECBAgAABAgQIJAlYAEiCFoYAAQIECBAgQIAAAQIECFQKWACo1BebAAECBAgQIECAAAECBAgkCVgASIIWhgABAgQIECBAgAABAgQIVApYAKjUF5sAAQIECBAgQIAAAQIECCQJWABIghaGAAECBAgQIECAAAECBAhUClgAqNQXmwABAgQIECBAgAABAgQIJAlYAEiCFoYAAQIECBAgQIAAAQIECFQKWACo1BebAAECBAgQIECAAAECBAgkCVgASIIWhgABAgQIECBAgAABAgQIVApYAKjUF5sAAQIECBAgQIAAAQIECCQJrF4AeJ32SZd6Pcz57nj9g6R3p33x/R/vku70epjX/XT9g6R3j0/npEjXw+yn6foHSe8ez09Jka6Hkf/y//qTkfOu/Jf/OU/a9SjG/+k6TNK78l/+Jz1qV8PI/+mqS9abPz7/V37/Wb0AkNXQ4hAgQIAAAQIECBAgQIAAAQLLBSwALLdzJgECBAgQIECAAAECBAgQ+DICv5Zc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xn/j//gv/TH/4nhv/Df/N/+fYnoMfx37u4rvP7G9K77/xe+7a7//+gnA8MdVAAIECBAgQIAAAQIECBAgUC9gAaC+DVwBAQIECBAgQIAAAQIECBAYLrB6ASBuBx5+tVcCxO0QVz4e/lbcDjI82JUAcTvMlY+Hv5W9/be/oezt/338uB2w/yyjHLcDZ8TrY8j/qSdJLcv/3O2/fePK/9yfP/T+xv/xf/OnN49l43/x85/wN39ie/evjf9TT5Ja/vHjf8LfPLvVoNXf/9aOf6sXAG7h+IwAAQIECBAgQIAAAQIECBDYhoAFgG20g6sgQIAAAQIECBAgQIAAAQJDBSwADOVVOQECBAgQIECAAAECBAgQ2IaABYBttIOrIECAAAECBAgQIECAAAECQwUsAAzlVTkBAgQIECBAgAABAgQIENiGgAWAbbSDqyBAgAABAgQIECBAgAABAkMFLAAM5VU5AQIECBAgQIAAAQIECBDYhoAFgG20g6sgQIAAAQIECBAgQIAAAQJDBSwADOVVOQECBAgQIECAAAECBAgQ2IaABYBttIOrIECAAAECBAgQIECAAAECQwUsAAzlVTkBAgQIECBAgAABAgQIENiGgAWAbbSDqyBAgAABAgQIECBAgAABAkMF/np4ePh7aIR3Kn98fHzniLEfv7y8jA3wTu3uX/u/84gM/djzL/+HPmDvVK7/0/+984gM/Vj/p/8b+oC9U7n+T//3ziMy9GP938/u/+wAGJpeKidAgAABAgQIECBAgAABAtsQsACwjXZwFQQIECBAgAABAgQIECBAYKiABYChvConQIAAAQIECBAgQIAAAQLbELAAsI12cBUECBAgQIAAAQIECBAgQGCogAWAobwqJ0CAAAECBAgQIECAAAEC2xCwALCNdnAVBAgQIECAAAECBAgQIEBgqIAFgKG8KidAgAABAgQIECBAgAABAtsQsACwjXZwFQQIECBAgAABAgQIECBAYKiABYChvConQIAAAQIECBAgQIAAAQLbELAAsI12cBUECBAgQIAAAQIECBAgQGCogAWAobwqJ0CAAAECBAgQIECAAAEC2xBYvQBwPt6V3snrfiqNf3w6l8bfT8X3f34qvf/XaV8a/3x3LI0/7YvvX/6Xtr/8L37+5X/p82/8n0r9j8b/Un/jf/Hz/9Pn/8X3X93/V3//XZv/qxcASns/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AAECBAjkClgAyPUWjQABAgQIECBAgAABAgQIlAhYAChhF5QAAQIECBAgQIAAAQIECOQKWADI9RaNAAECBAgQIECAAAECBAiUCFgAKGEXlAABAgQIECBAgAABAgQI5ApYAMj1Fo0AAQIECBAgQIAAAQIECJQIWAAoYReUAAECBAgQIECAAAECBAjkClgAyPUWjQABAgQIECBAgAABAgQIlAhYAChhF5QAAQIECBAgQIAAAQIECOQKWADI9RaNAAECBAgQIECAAAECBAiUCPz18PDwd0nk/wV9fHysDP+fl5eX0vjuX/tXPoCef/lf+fzp//R/lc+f/k//V/n86f/0f5XPn/7vZ/d/dgBUZp/YBAgQIECAAAECBAgQIEAgScACQBK0MAQIECBAgAABAgQIECBAoFLAAkClvtgECBAgQIAAAQIECBAgQCBJwAJAErQwBAgQIECAAAECBAgQIECgUsACQKW+2AQIECBAgAABAgQIECBAIEnAAkAStDAECBAgQIAAAQIECBAgQKBSwAJApb7YBAgQIECAAAECBAgQIEAgScACQBK0MAQIECBAgAABAgQIECBAoFLAAkClvtgECBAgQIAAAQIECBAgQCBJwAJAErQwBAgQIECAAAECBAgQIECgUsACQKW+2AQIECBAgAABAgQIECBAIElg9QLA8emcdKnXw+yn6foHSe8ez09Jka6HeZ321z9Ievd8d0yKdD3MtC++/+Pd9QtLevd1PyVFuh5G/sv/609Gzrvyf8qB/kMU+S////BopLwt/6cU5z8F+fH5X/z9p3r+dy6e/5bnf/H3n7Xf/1YvAPypY/A+AQIECBAgQIAAAQIECBAgsB0BCwDbaQtXQoAAAQIECBAgQIAAAQIEhgn8WlJzv+0nlg/PuyVVzjqn3/Yfy6eEnwT02/5j+bB7nnUvSw7ut33E8v10WlLlrHP6bf+xvHs7zKprycH9tp9Ynk4J999te4rboHaHtyW3NOucfttXLN+fpll1LTk45ns7P5blv/xf8kzNOSfmezsvluX/NIdy0bEx31sFsSz/5f+ih2rGSTHf22mxLP+nGZLLDo353mqI5R+R/922/+z7j/O95h/LGfO/ON9t8WM5Y/4b873Fj+WU/O+2/Wd//4nf99r9x/Lc73+LFgBikreHP5bbBY3+L37Jb1/+Y3l07FZ//JLfvvzHckb8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fN+2dWcECBAgQIAAAQIECBAgQOAisHoBIG4Hu9Sa+CJ7+39/a3E7UP9ZRjluh8qI18eI28H6zzLK2dv/+3uK24H6zzLKcTtkRrw+hvyfepLUsvyf95u3z24c+V/8/IftsJ/dth+pr3z8L77/6v6/evwrz//wc4CPPK+ffUz1/K96/JP/02c/UrPqk//r5j+rFwBmtZaDCRAgQIAAAQIECBAgQIAAgRIBCwAl7IISIECAAAECBAgQIECAAIFcAQsAud6iESBAgAABAgQIECBAgACBEgELACXsghIgQIAAAQIECBAgQIAAgVwBCwC53qIRIECAAAECBAgQIECAAIESAQsAJeyCEiBAgAABAgQIECBAgACBXAELALneohEgQIAAAQIECBAgQIAAgRIBCwAl7IISIECAAAECBAgQIECAAIFcAQsAud6iESBAgAABAgQIECBAgACBEgELACXsghIgQIAAAQIECBAgQIAAgVwBCwC53qIRIECAAAECBAgQIECAAIESAQsAJeyCEiBAgAABAgQIECBAgACBXIG/Hh4e/s4N+e9oj4+P/34jufTy8pIc8d/h3L/2//cTkVvy/Mv/3Cfu39H0f/q/fz8RuSX9n/4v94n7dzT9n/7v309Ebkn/97P7PzsAcvNNNAIECBAgQIAAAQIECBAgUCJgAaCEXVACBAgQIECAAAECBAgQIJArYAEg11s0AgQIECBAgAABAgQIECBQImABoIRdUAIECBAgQIAAAQIECBAgkCtgASDXWzQCBAgQIECAAAECBAgQIFAiYAGghF1QAgQIECBAgAABAgQIECCQK2ABINdbNAIECBAgQIAAAQIECBAgUCJgAaCEXVACBAgQIECAAAECBAgQIJArYAEg11s0AgQIECBAgAABAgQIECBQImABoIRdUAIECBAgQIAAAQIECBAgkCtgASDXWzQCBAgQIECAAAECBAgQIFAisHoB4Ph0Lrnwf4Lup+mflyX/P56fSuL+E/R12v/zsuT/57tjSdx/gk774vs/3v1zKSX/f91PJXH/CSr/5f8/z0LF/+X/VMF+ifnj8794/lPd/5+Lx7/y/C+e/1TP/6rnv+Xzf/l/GQsqXsj/dd9/Vi8AVDS6mAQIECBAgAABAgQIECBAgMA8AQsA87wcTYAAAQIECBAgQIAAAQIEvqTAryVX3W/7i+XD825JlbPO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+R/x//uG+G//3zz7yN/+P3b+j37+uXf/vzsB8NHk9B4BAgQIECBAgAABAgQIECAwh4AEwBzjoBcECBAgQIAAAQIECBAgQKCpgARAU16VEyBAgAABAgQIECBAgACBOQQkAOYYB70gQIAAAQIECBAgQIAAAQJNBSQAmvKqnAABAgQIECBAgAABAgQIzCEgATDHOOgFAQIECBAgQIAAAQIECBBoKiAB0JRX5QQIECBAgAABAgQIECBAYA4BCYA5xkEvCBAgQIAAAQIECBAgQIBAUwEJgKa8KidAgAABAgQIECBAgAABAnMISADMMQ56QYAAAQIECBAgQIAAAQIEmgpIADTlVTkBAgQIECBAgAABAgQIEJhDQAJgjnHQCwIECBAgQIAAAQIECBAg0FTg29PTU9MGPqv85eXls480fd/1G/+mE+yTys1/8f/JFGn6tvXP+td0gn1SufXP+vfJFGn6tvXP+td0gn1SufXP+vfJFGn6thMATXlVToAAAQIECBAgQIAAAQIE5hCQAJhjHPSCAAECBAgQIECAAAECBAg0FZAAaMqrcgIECBAgQIAAAQIECBAgMIeABMAc46AXBAgQIECAAAECBAgQIECgqYAEQFNelRMgQIAAAQIECBAgQIAAgTkEJADmGAe9IECAAAECBAgQIECAAAECTQUkAJryqpwAAQIECBAgQIAAAQIECMwhIAEwxzjoBQECBAgQIECAAAECBAgQaCogAdCUV+UECBAgQIAAAQIECBAgQGAOAQmAOcZBLwgQIECAAAECBAgQIECAQFMBCYCmvConQIAAAQIECBAgQIAAAQJzCNydADguy9ArOV+fh7b/uhyHtn99OA9tfzkOvv7zw9Drfz0uQ9s/P1+Htj88/gdf/+jxvw6e/8Pjf/D6N3r9H73/if+x67/4H3v/I/7Hzv/R9z/2/8H3//b/u+7/704A3NW6LxMgQIAAAQIECBAgQIAAAQJdBCQAujBrhAABAgQIECBAgAABAgQIjBX4tqX5+thfli8dfhJQH3vM8unwY8slrfpOfewry4/LZVVdWz5cH/vP8uHttKXKVd+pj/1mebl0uP7q2HMegzyc3lZdy5YP18e+svx4WbZUueo79bG3LJ9+HFbVteXDGe/l+1nuEv/Vsf/e15/jXa4/yz3GP+d7aT/LPeZ/xntpP8td4r869td7/cv1vlx/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XO9HrP+5yZebgSz3iL+Md/Hff/0X/+8P+eJf/Of9QI/1z/7//pBr/+///Jf7vf3/vucfPwHosWJqgwABAgQIECBAgAABAgQIDBaQABg8AJonQIAAAQIECBAgQIAAAQI9BO5OAORx0B4drtvoffy/bj+PA9fv9Sjnccge7dVt5HGw+r0e5d7H3+pryuPA9Xs9yqOvf/T4j57/w+M/fg7QY77VbYxe//M4YN23HmXxv/Rg/sc2xP/7zwH+EanhG+J/aaj7edX2//a/+f9oFOz/7z+H+Mip1Xv2/3W/+a/H4e4EQF2hMgECBAgQIECAAAECBAgQIDCfgATAfGOiRwQIECBAgAABAgQIECBAYHcBCYDdSVVIgAABAgQIECBAgAABAgTmE5AAmG9M9IgAAQIECBAgQIAAAQIECOwuIAGwO6kKCRAgQIAAAQIECBAgQIDAfAISAPONiR4RIECAAAECBAgQIECAAIHdBSQAdidVIQECBAgQIECAAAECBAgQmE9AAmC+MdEjAgQIECBAgAABAgQIECCwu4AEwO6kKiRAgAABAgQIECBAgAABAvMJSADMNyZ6RIAAAQIECBAgQIAAAQIEdheQANidVIUECBAgQIAAAQIECBAgQGA+AQmA+cZEjwgQIECAAAECBAgQICBcDOoAABxJSURBVECAwO4C//7+/ft/dq91RYVPT08rPr3/R19eXvavdEWNrt/4r5guu3/U/Bf/u0+qFRVa/6x/K6bL7h+1/ln/dp9UKyq0/ln/VkyX3T9q/fuz1z8nAHYPKRUSIECAAAECBAgQIECAAIH5BCQA5hsTPSJAgAABAgQIECBAgAABArsLSADsTqpCAgQIECBAgAABAgQIECAwn4AEwHxjokcECBAgQIAAAQIECBAgQGB3AQmA3UlVSIAAAQIECBAgQIAAAQIE5hOQAJhvTPSIAAECBAgQIECAAAECBAjsLiABsDupCgkQIECAAAECBAgQIECAwHwCEgDzjYkeESBAgAABAgQIECBAgACB3QUkAHYnVSEBAgQIECBAgAABAgQIEJhPQAJgvjHRIwIECBAgQIAAAQIECBAgsLuABMDupCokQIAAAQIECBAgQIAAAQLzCdydALg+nIde1XI8Dm3/en4Y2v7rcRna/vn5OrT94/JnX//o8R89/4fH/+D173UZu/6dr8/if6CA+B87/0ff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/+vufzclAPImpyx+WW49+KX+HOQy+Fnu0X4+5JaH3yz3aD83+bL5Z7lH+3m9I64/x3vE+Od8HzH/8yG/PPxnucf45yZfHv6z3KP9fMgvD/9Z7tF+PuSXh/8s92g/413891//xf/7Q774F/95P9Bj/bP/v9/k2//t/3k/0CP+Mt7d//d//s37/Xvj308AekSMNggQIECAAAECBAgQIECAwGABCYDBA6B5AgQIECBAgAABAgQIECDQQ+DuBEAeh+zR4bqNPA5Wv9ejnMdherRXt9H7+E/d/ujrHz3+o+d/Hgeqx6ZHuffx//qaeh//r9vvffy/bl/8LzVJ17L4f/85QFf4vxoT/8sI9lub9v/2/82rG/ZPXtj/338O8ROe5v9k/2//3zz7aBDd/983/+9OAHw0ON4jQIAAAQIECBAgQIAAAQIE5hCQAJhjHPSCAAECBAgQIECAAAECBAg0FZAAaMqrcgIECBAgQIAAAQIECBAgMIeABMAc46AXBAgQIECAAAECBAgQIECgqYAEQFNelRMgQIAAAQIECBAgQIAAgTkEJADmGAe9IECAAAECBAgQIECAAAECTQUkAJryqpwAAQIECBAgQIAAAQIECMwhIAEwxzjoBQECBAgQIECAAAECBAgQaCogAdCUV+UECBAgQIAAAQIECBAgQGAOAQmAOcZBLwgQIECAAAECBAgQIECAQFMBCYCmvConQIAAAQIECBAgQIAAAQJzCEgAzDEOekGAAAECBAgQIECAAAECBJoKfHt6emrawGeVv7y8fPaRpu+7fuPfdIJ9Urn5L/4/mSJN37b+Wf+aTrBPKrf+Wf8+mSJN37b+Wf+aTrBPKrf+Wf8+mSJN33YCoCmvygkQIECAAAECBAgQIECAwBwCEgBzjINeECBAgAABAgQIECBAgACBpgISAE15VU6AAAECBAgQIECAAAECBOYQkACYYxz0ggABAgQIECBAgAABAgQINBWQAGjKq3ICBAgQIECAAAECBAgQIDCHgATAHOOgFwQIECBAgAABAgQIECBAoKmABEBTXpUTIECAAAECBAgQIECAAIE5BCQA5hgHvSBAgAABAgQIECBAgAABAk0FJACa8qqcAAECBAgQIECAAAECBAjMISABMMc46AUBAgQIECBAgAABAgQIEGgqIAHQlFflBAgQIECAAAECBAgQIEBgDoG7EwCv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" + } + ] +} diff --git a/lyra-resource/test_files/gltf/texture-sep/texture-sep.bin b/lyra-resource/test_files/gltf/texture-sep/texture-sep.bin new file mode 100644 index 0000000..9322438 Binary files /dev/null and b/lyra-resource/test_files/gltf/texture-sep/texture-sep.bin differ diff --git a/lyra-resource/test_files/gltf/texture-sep/texture-sep.gltf b/lyra-resource/test_files/gltf/texture-sep/texture-sep.gltf new file mode 100644 index 0000000..8e9d366 --- /dev/null +++ b/lyra-resource/test_files/gltf/texture-sep/texture-sep.gltf @@ -0,0 +1,137 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v3.6.6", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "TEXCOORD_0":1, + "NORMAL":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"uvgrid", + "uri":"uvgrid.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":480, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"texture-sep.bin" + } + ] +} diff --git a/lyra-resource/test_files/gltf/texture-sep/uvgrid.png b/lyra-resource/test_files/gltf/texture-sep/uvgrid.png new file mode 100644 index 0000000..06aad7c Binary files /dev/null and b/lyra-resource/test_files/gltf/texture-sep/uvgrid.png differ diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..07ade69 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/src/ecs/components/mesh.rs b/src/ecs/components/mesh.rs index b169c63..6612ea6 100755 --- a/src/ecs/components/mesh.rs +++ b/src/ecs/components/mesh.rs @@ -1,18 +1,16 @@ use edict::Component; -use crate::render::{vertex::Vertex, mesh::Mesh, material::Material}; +use lyra_resource::Mesh; #[derive(Clone, Component)] pub struct MeshComponent { pub mesh: Mesh, - pub material: Material, } impl MeshComponent { - pub fn new(mesh: Mesh, material: Material) -> Self { + pub fn new(mesh: Mesh) -> Self { Self { mesh, - material } } } \ No newline at end of file diff --git a/src/ecs/components/mod.rs b/src/ecs/components/mod.rs index 93b9a2b..4a474dd 100755 --- a/src/ecs/components/mod.rs +++ b/src/ecs/components/mod.rs @@ -1,3 +1,4 @@ pub mod mesh; +pub mod model; pub mod transform; pub mod camera; \ No newline at end of file diff --git a/src/ecs/components/model.rs b/src/ecs/components/model.rs new file mode 100644 index 0000000..b50b3de --- /dev/null +++ b/src/ecs/components/model.rs @@ -0,0 +1,39 @@ +use lyra_resource::ResHandle; + +use crate::assets::Model; + +#[derive(Clone, edict::Component)] +pub struct ModelComponent(pub ResHandle); + +impl From> for ModelComponent { + fn from(value: ResHandle) -> Self { + ModelComponent(value) + } +} + +/* impl From for ModelComponent { + +} */ + +impl std::ops::Deref for ModelComponent { + type Target = ResHandle; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for ModelComponent { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/* impl ModelComponent { + pub fn new(model, material: Material) -> Self { + Self { + mesh, + material + } + } +} */ \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index e719846..c76cfde 100755 --- a/src/game.rs +++ b/src/game.rs @@ -297,7 +297,7 @@ impl Game { .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() .with_target("lyra_engine", Level::TRACE) - .with_target("wgpu_core", Level::INFO) + .with_target("wgpu_core", Level::WARN) .with_default(Level::DEBUG)) .init(); diff --git a/src/lib.rs b/src/lib.rs index 4c01741..e45bd7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(hash_extract_if)] + pub mod game; pub mod render; pub mod input_event; diff --git a/src/render/default_texture.png b/src/render/default_texture.png new file mode 100644 index 0000000..ec02f5b Binary files /dev/null and b/src/render/default_texture.png differ diff --git a/src/render/render_job.rs b/src/render/render_job.rs index c331039..319329f 100755 --- a/src/render/render_job.rs +++ b/src/render/render_job.rs @@ -2,53 +2,23 @@ use edict::EntityId; use crate::math::Transform; -use super::{mesh::Mesh, material::Material}; - pub struct RenderJob { - mesh: Mesh, - material: Material, - entity: EntityId, + pub entity: EntityId, + pub shader_id: u64, + pub mesh_buffer_id: uuid::Uuid, - transform: Transform, - last_transform: Option, // TODO: render interpolation + pub transform: Transform, + pub last_transform: Option, // TODO: render interpolation } impl RenderJob { - pub fn new(mesh: Mesh, material: Material, entity: EntityId, transform: Transform, last_transform: Option) -> Self { + pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform, last_transform: Option) -> Self { Self { - mesh, - material, entity, + shader_id, + mesh_buffer_id, transform, last_transform, } } - - pub fn mesh(&self)-> &Mesh { - &self.mesh - } - - pub fn material(&self)-> &Material { - &self.material - } - - pub fn entity(&self)-> EntityId { - self.entity - } - - pub fn transform(&self)-> &Transform { - &self.transform - } - - pub fn set_transform(&mut self, transform: Transform){ - self.transform = transform; - } - - pub fn last_transform(&self)-> Option<&Transform> { - self.last_transform.as_ref() - } - - pub fn set_last_transform(&mut self, last_transform: Transform){ - self.last_transform = Some(last_transform); - } } \ No newline at end of file diff --git a/src/render/render_pipeline.rs b/src/render/render_pipeline.rs index d1a7f20..87f876f 100755 --- a/src/render/render_pipeline.rs +++ b/src/render/render_pipeline.rs @@ -1,8 +1,8 @@ -use std::{ops::Range, cell::Ref}; +use std::ops::Range; use wgpu::{PipelineLayout, RenderPipeline, RenderPass, VertexBufferLayout, BindGroupLayout}; -use super::{render_job::RenderJob, vertex::Vertex, desc_buf_lay::DescVertexBufferLayout, texture::RenderTexture}; +use super::{render_job::RenderJob, texture::RenderTexture}; pub struct FullRenderPipeline { layout: PipelineLayout, diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 0d1aec1..05844a8 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -5,30 +5,27 @@ use std::num::NonZeroU64; use std::sync::Arc; use std::borrow::Cow; -use aligned_vec::AVec; -use async_std::sync::Mutex; -use async_trait::async_trait; - -use atomicell::{AtomicCell, RefMut}; use edict::query::EpochOf; use edict::{EntityId, Entities}; -use glam::Mat4; +use glam::Vec3; use tracing::{debug, warn}; -use wgpu::{BindGroup, BindGroupLayout, Limits, BufferBinding}; +use wgpu::{BindGroup, BindGroupLayout, Limits}; use wgpu::util::DeviceExt; use winit::window::Window; use crate::ecs::components::camera::CameraComponent; use crate::ecs::components::mesh::MeshComponent; +use crate::ecs::components::model::ModelComponent; use crate::ecs::components::transform::TransformComponent; -use crate::math::{Transform, Angle}; -use crate::resources; +use crate::math::Transform; use super::camera::RenderCamera; use super::desc_buf_lay::DescVertexBufferLayout; use super::texture::RenderTexture; use super::vertex::Vertex; -use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh}; +use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, render_job::RenderJob}; + +use lyra_resource::Mesh; pub trait Renderer { fn prepare(&mut self, main_world: &mut edict::World); @@ -36,16 +33,15 @@ pub trait Renderer { fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize); fn surface_size(&self) -> winit::dpi::PhysicalSize; - fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc); + fn add_render_pipeline(&mut self, shader_id: u64, pipeline: Arc); } struct RenderBufferStorage { buffer_vertex: BufferStorage, - buffer_indices: Option, + buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>, render_texture: Option, texture_bindgroup: Option, - texture_layout: Option, /// The index of the transform for this entity. /// The tuple is structured like this: (transform index, index of transform inside the buffer) @@ -73,13 +69,15 @@ struct TransformBuffers { impl TransformBuffers { /// Update an entity's buffer with the new transform. Will panic if the entity isn't stored - fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) { + fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) -> TransformBufferIndices { let indices = self.not_updated.remove(&entity) + .or_else(|| self.just_updated.remove(&entity)) .expect("Use 'insert_entity' for new entities"); self.just_updated.insert(entity, indices); let (_, buffer, _) = self.buffer_bindgroups.get(indices.buffer_index).unwrap(); queue.write_buffer(buffer, indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64, bytemuck::bytes_of(&transform)); + indices } /// Insert a new entity into the buffer, returns where it was stored. @@ -109,6 +107,22 @@ impl TransformBuffers { indices } + /// Update or insert an entities transform + fn update_or_insert(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform_fn: TFn) -> TransformBufferIndices + where TFn: Fn() -> glam::Mat4 + { + if self.contains(entity) { + self.update_entity(queue, limits, entity, transform_fn()) + } else { + self.insert_entity(queue, limits, entity, transform_fn()) + } + } + + /// Returns true if the entity's transform is stored (does not mean its up-to-date). + fn contains(&self, entity: EntityId) -> bool { + self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity) + } + /// Collect the dead entities, mark entities and not updated for next updates. fn tick(&mut self) { // take the dead entities, these were ones that were not updated this tick @@ -116,7 +130,6 @@ impl TransformBuffers { .map(|t| t.clone()).collect(); self.dead_indices = dead; - // swap just_updated into not_updated self.not_updated = self.just_updated.clone(); self.just_updated.clear(); } @@ -145,10 +158,11 @@ pub struct BasicRenderer { pub clear_color: wgpu::Color, - pub render_pipelines: HashMap>, + pub render_pipelines: HashMap>, pub render_jobs: VecDeque, - buffer_storage: HashMap, // TODO: clean up left over buffers from deleted entities/components + mesh_buffers: HashMap, // TODO: clean up left over buffers from deleted entities/components + entity_meshes: HashMap, transform_buffers: TransformBuffers, transform_bind_group_layout: BindGroupLayout, @@ -160,6 +174,8 @@ pub struct BasicRenderer { camera_buffer: wgpu::Buffer, camera_bind_group: wgpu::BindGroup, + texture_bind_group_layout: BindGroupLayout, + default_texture_bind_group: BindGroup, depth_buffer_texture: RenderTexture, } @@ -208,7 +224,7 @@ impl BasicRenderer { false => surface_caps.present_modes[0] }; */ - println!("present mode: {:?}", present_mode); + debug!("present mode: {:?}", present_mode); let surface_format = surface_caps.formats.iter() .copied() @@ -348,12 +364,27 @@ impl BasicRenderer { let depth_texture = RenderTexture::create_depth_texture(&device, &config, "Depth Buffer"); - let mut pipelines = HashMap::new(); - pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader, - vec![super::vertex::Vertex::desc(),], - vec![&texture_bind_group_layout, &transform_bind_group_layout, &camera_bind_group_layout]))); + // load the default texture + let bytes = include_bytes!("default_texture.png"); + let tex = RenderTexture::from_bytes(&device, &queue, bytes, "default_texture").unwrap(); + let default_tex_bindgroup = device.create_bind_group( + &wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(tex.view()), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(tex.sampler()), + } + ], + label: Some("default_texture"), + } + ); - Self { + let mut s = Self { window, surface, device, @@ -366,9 +397,10 @@ impl BasicRenderer { b: 0.3, a: 1.0, }, - render_pipelines: pipelines, + render_pipelines: HashMap::new(), render_jobs: VecDeque::new(), - buffer_storage: HashMap::new(), + mesh_buffers: HashMap::new(), + entity_meshes: HashMap::new(), render_limits, transform_buffers, @@ -378,29 +410,31 @@ impl BasicRenderer { camera_buffer, camera_bind_group, + texture_bind_group_layout, + default_texture_bind_group: default_tex_bindgroup, depth_buffer_texture: depth_texture, - } + }; + + // create the default pipelines + let mut pipelines = HashMap::new(); + pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader, + vec![super::vertex::Vertex::desc(),], + vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout]))); + s.render_pipelines = pipelines; + + s } - fn find_next_multiple(n: u32, mul: u32) -> u32 { - if n % mul == 0 { - n - } else { - n + (mul - n % mul) - } - } - - // TODO: minimize how often model buffers are updated by checking if they changed - fn update_model_buffers(&mut self, entity: EntityId, model: &MeshComponent) { - if let Some(buffers) = self.buffer_storage.get_mut(&entity) { + fn update_mesh_buffers(&mut self, _entity: EntityId, mesh: &Mesh) { + if let Some(buffers) = self.mesh_buffers.get_mut(&mesh.uuid) { // check if the buffer sizes dont match. If they dont, completely remake the buffers - let vertices = &model.mesh.vertices; + let vertices = mesh.position().unwrap(); if buffers.buffer_vertex.count() != vertices.len() { - drop(buffers); - let (vert, idx) = self.create_vertex_index_buffers(&model.mesh); + debug!("Recreating buffers for mesh {}", mesh.uuid.to_string()); + let (vert, idx) = self.create_vertex_index_buffers(mesh); // have to re-get buffers because of borrow checker - let buffers = self.buffer_storage.get_mut(&entity).unwrap(); + let buffers = self.mesh_buffers.get_mut(&mesh.uuid).unwrap(); buffers.buffer_indices = idx; buffers.buffer_vertex = vert; @@ -411,109 +445,104 @@ impl BasicRenderer { let vertex_buffer = buffers.buffer_vertex.buffer(); let vertices = vertices.as_slice(); // align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT) - let (_, vertices, _) = bytemuck::pod_align_to::(vertices); + let (_, vertices, _) = bytemuck::pod_align_to::(vertices); self.queue.write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(&vertices)); // update the indices if they're given if let Some(index_buffer) = buffers.buffer_indices.as_ref() { - let index_buffer = index_buffer.buffer(); - let indices = model.mesh.indices.as_ref().unwrap().as_slice(); - let (_, indices, _) = bytemuck::pod_align_to::(indices); - - self.queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(&indices)); + let aligned_indices = match mesh.indices.as_ref().unwrap() { + // U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size. + lyra_resource::MeshIndices::U16(v) => bytemuck::pod_align_to::(v).1, + lyra_resource::MeshIndices::U32(v) => bytemuck::pod_align_to::(v).1, + }; + + let index_buffer = index_buffer.1.buffer(); + self.queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(&aligned_indices)); } } } - fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option) { + fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) { + let positions = mesh.position().unwrap(); + let tex_coords: Vec = mesh.tex_coords().cloned() + .unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]); + + assert!(positions.len() == tex_coords.len()); + + let vertex_inputs: Vec = std::iter::zip(positions, tex_coords.into_iter()) + .map(|(v, t)| Vertex::new(v.clone(), t)) + .collect(); + let vertex_buffer = self.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(mesh.vertices.as_slice()), + contents: bytemuck::cast_slice(vertex_inputs.as_slice()),//vertex_combined.as_slice(), usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages:: COPY_DST, } ); - let vertex_buffer = BufferStorage::new(vertex_buffer, 0, mesh.vertices.len()); + let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len()); - let buffer_indices = match mesh.indices.as_ref() { + let indices = match mesh.indices.as_ref() { Some(indices) => { + let (idx_type, len, contents) = match indices { + lyra_resource::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(&v)), + lyra_resource::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(&v)), + }; + let index_buffer = self.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Index Buffer"), - contents: bytemuck::cast_slice(&indices), + contents, usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages:: COPY_DST, } ); - let buffer_indices = BufferStorage::new(index_buffer, 0, indices.len()); + let buffer_indices = BufferStorage::new(index_buffer, 0, len); - Some(buffer_indices) + Some((idx_type, buffer_indices)) }, None => { None } }; - ( vertex_buffer, buffer_indices ) + ( vertex_buffer, indices ) } - fn create_model_buffers(&mut self, model: &MeshComponent, transform_indices: TransformBufferIndices) -> RenderBufferStorage { - let mesh = &model.mesh; - + fn create_mesh_buffers(&mut self, mesh: &Mesh, transform_indices: TransformBufferIndices) -> RenderBufferStorage { let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh); - let model_texture = &model.material.texture; - let image = &model_texture.data.as_ref().unwrap().image; - let diffuse_texture = RenderTexture::from_image(&self.device, &self.queue, image, None).unwrap(); + let diffuse_bindgroup = if let Some(model_texture) = &mesh.material().base_color_texture { + let image = &model_texture.data.as_ref().unwrap().image; + let diffuse_texture = RenderTexture::from_image(&self.device, &self.queue, image, None).unwrap(); - let texture_bind_group_layout = - self.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 }, + let diffuse_bind_group = self.device.create_bind_group( + &wgpu::BindGroupDescriptor { + layout: &self.texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(diffuse_texture.view()), }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - // This should match the filterable field of the - // corresponding Texture entry above. - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - label: Some("texture_bind_group_layout"), - }); + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(diffuse_texture.sampler()), + } + ], + label: Some("diffuse_bind_group"), + } + ); - let diffuse_bind_group = self.device.create_bind_group( - &wgpu::BindGroupDescriptor { - layout: &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("diffuse_bind_group"), - } - ); + Some(diffuse_bind_group) + } else { + None + }; RenderBufferStorage { buffer_vertex: vertex_buffer, buffer_indices, render_texture: None, - texture_layout: None, - texture_bindgroup: Some(diffuse_bind_group), + texture_bindgroup: diffuse_bindgroup, transform_index: transform_indices } } @@ -553,6 +582,28 @@ impl BasicRenderer { buffers.next_indices = indices; indices } + + /// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed. + fn process_mesh(&mut self, entity: EntityId, transform: Transform, mesh: &Mesh) -> bool { + let indices = self.transform_buffers.update_or_insert(&self.queue, &self.render_limits, + entity, || transform.calculate_mat4()); + + if self.mesh_buffers.contains_key(&mesh.uuid) { + false + } else { + // check if the transform buffers need to be expanded + if self.transform_buffers.should_expand() { + self.expand_transform_buffers(); + } + + // create the mesh's buffers + let buffers = self.create_mesh_buffers(mesh, indices); + self.mesh_buffers.insert(mesh.uuid, buffers); + self.entity_meshes.insert(entity, mesh.uuid); + + true + } + } } impl Renderer for BasicRenderer { @@ -561,45 +612,39 @@ impl Renderer for BasicRenderer { let mut alive_entities = HashSet::new(); - for (entity, model, model_epoch, transform) in main_world.query::<(Entities, &MeshComponent, EpochOf, &TransformComponent)>().iter() { - // Create the render job and push it to the queue - let job = RenderJob::new(model.mesh.clone(), model.material.clone(), entity, transform.transform, None); - self.render_jobs.push_back(job); - + for (entity, model, model_epoch, transform) in main_world.query::<(Entities, &ModelComponent, EpochOf, &TransformComponent)>().iter() { alive_entities.insert(entity); - if self.buffer_storage.get(&entity).is_none() { - // check if the transform buffers need to be expanded - if self.transform_buffers.should_expand() { - self.expand_transform_buffers(); + let model = model.data.as_ref().unwrap().as_ref(); + + for mesh in model.meshes.iter() { + if !self.process_mesh(entity, transform.transform, mesh) { + if model_epoch == last_epoch { + self.update_mesh_buffers(entity, mesh); + } } - // insert transform into buffers - let indices = self.transform_buffers.insert_entity(&self.queue, &self.render_limits, - entity, transform.transform.calculate_mat4()); - - // create the mesh's buffers - let buffers = self.create_model_buffers(model, indices); - self.buffer_storage.insert(entity, buffers); - } else { - // update entity transforms - self.transform_buffers.update_entity(&self.queue, &self.render_limits, - entity, transform.transform.calculate_mat4()); - - // if the model was updated, update its buffers - if model_epoch == last_epoch { - self.update_model_buffers(entity, model); - } + let shader = mesh.material().shader_uuid.unwrap_or(0); + let job = RenderJob::new(entity, shader, mesh.uuid, transform.transform, None); + self.render_jobs.push_back(job); } } + for (entity, mesh, mesh_epoch, transform) in main_world.query::<(Entities, &MeshComponent, EpochOf, &TransformComponent)>().iter() { + debug!("TODO: Process MeshComponents"); // TODO: Process MeshComponents + } + // collect dead entities self.transform_buffers.tick(); // when buffer storage length does not match the amount of iterated entities, // remove all dead entities, and their buffers, if they weren't iterated over - if self.buffer_storage.len() != alive_entities.len() { - self.buffer_storage.retain(|e, _| alive_entities.contains(e)); + if self.mesh_buffers.len() != alive_entities.len() { + let removed_entities: Vec = self.entity_meshes + .extract_if(|e, _| !alive_entities.contains(e)) + .map(|(_, v)| v) + .collect(); + self.mesh_buffers.retain(|u, _| !removed_entities.contains(u)); } if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() { @@ -644,17 +689,18 @@ impl Renderer for BasicRenderer { // Pop off jobs from the queue as they're being processed while let Some(job) = self.render_jobs.pop_front() { - if let Some(pipeline) = self.render_pipelines.get(&job.material().shader_id) { + if let Some(pipeline) = self.render_pipelines.get(&job.shader_id) { // specify to use this pipeline render_pass.set_pipeline(pipeline.get_wgpu_pipeline()); // get the mesh (containing vertices) and the buffers from storage - let mesh = job.mesh(); - let buffers = self.buffer_storage.get(&job.entity()).unwrap(); + let buffers = self.mesh_buffers.get(&job.mesh_buffer_id).unwrap(); // Bind the optional texture if let Some(tex) = buffers.texture_bindgroup.as_ref() { render_pass.set_bind_group(0, &tex, &[]); + } else { + render_pass.set_bind_group(0, &self.default_texture_bind_group, &[]); } // Get the bindgroup for job's transform and bind to it using an offset. @@ -667,18 +713,18 @@ impl Renderer for BasicRenderer { render_pass.set_bind_group(2, &self.camera_bind_group, &[]); // if this mesh uses indices, use them to draw the mesh - if let Some(indices) = buffers.buffer_indices.as_ref() { + if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() { let indices_len = indices.count() as u32; render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..)); - render_pass.set_index_buffer(indices.buffer().slice(..), wgpu::IndexFormat::Uint16); + render_pass.set_index_buffer(indices.buffer().slice(..), idx_type.clone()); render_pass.draw_indexed(0..indices_len, 0, 0..1); } else { + let vertex_count = buffers.buffer_vertex.count(); + render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..)); - render_pass.draw(0..mesh.vertices.len() as u32, 0..1); + render_pass.draw(0..vertex_count as u32, 0..1); } - } else { - warn!("Failure to find RenderPipeline with shader id of '{}'!", job.material().shader_id); } } } @@ -706,7 +752,7 @@ impl Renderer for BasicRenderer { self.size } - fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc) { + fn add_render_pipeline(&mut self, shader_id: u64, pipeline: Arc) { self.render_pipelines.insert(shader_id, pipeline); } } \ No newline at end of file diff --git a/src/render/vertex.rs b/src/render/vertex.rs index 12ac319..2b53e9d 100755 --- a/src/render/vertex.rs +++ b/src/render/vertex.rs @@ -3,9 +3,17 @@ use super::desc_buf_lay::DescVertexBufferLayout; #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct Vertex { - pub position: [f32; 3], + pub position: glam::Vec3, + pub tex_coords: glam::Vec2 //pub color: [f32; 3], // TODO: add color again - pub tex_coords: [f32; 2] +} + +impl Vertex { + pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self { + Self { + position, tex_coords + } + } } impl DescVertexBufferLayout for Vertex { @@ -17,12 +25,12 @@ impl DescVertexBufferLayout for Vertex { wgpu::VertexAttribute { offset: 0, shader_location: 0, - format: wgpu::VertexFormat::Float32x3, + format: wgpu::VertexFormat::Float32x3, // Vec3 }, wgpu::VertexAttribute { offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, shader_location: 1, - format: wgpu::VertexFormat::Float32x2, + format: wgpu::VertexFormat::Float32x2, // Vec2 } ] }