diff --git a/Cargo.lock b/Cargo.lock index 701308e..8b637df 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" @@ -312,6 +312,12 @@ 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" @@ -515,6 +521,30 @@ dependencies = [ "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" @@ -524,6 +554,12 @@ 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" @@ -598,6 +634,12 @@ dependencies = [ "syn 2.0.26", ] +[[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" @@ -631,6 +673,22 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1e481eb11a482815d3e9d618db8c42a93207134662873809335a92327440c18" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -665,6 +723,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -680,6 +751,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[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" @@ -716,6 +793,12 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "fxhash" version = "0.2.1" @@ -732,8 +815,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -847,6 +942,15 @@ dependencies = [ "bitflags", ] +[[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" @@ -900,17 +1004,21 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "image" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", "color_quant", + "exr", + "gif", "jpeg-decoder", - "num-rational", + "num-rational 0.4.1", "num-traits", "png", + "qoi", + "tiff", ] [[package]] @@ -992,6 +1100,9 @@ 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" @@ -1028,6 +1139,12 @@ 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.147" @@ -1105,8 +1222,10 @@ dependencies = [ "glam", "image", "instant", + "lyra-resource", "petgraph", "quote", + "stopwatch", "syn 2.0.26", "tobj", "tracing", @@ -1117,6 +1236,16 @@ dependencies = [ "winit", ] +[[package]] +name = "lyra-resource" +version = "0.0.1" +dependencies = [ + "anyhow", + "image", + "thiserror", + "uuid", +] + [[package]] name = "mach2" version = "0.4.1" @@ -1159,6 +1288,15 @@ 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" @@ -1221,6 +1359,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + [[package]] name = "ndk" version = "0.7.0" @@ -1259,7 +1406,7 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1272,7 +1419,7 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1307,6 +1454,42 @@ dependencies = [ "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", + "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" @@ -1317,6 +1500,29 @@ dependencies = [ "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" @@ -1337,6 +1543,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.11" @@ -1500,12 +1716,32 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 1.9.3", + "indexmap 2.0.0", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", ] [[package]] @@ -1591,6 +1827,15 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2" +[[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.29" @@ -1600,6 +1845,34 @@ 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_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 = "range-alloc" version = "0.1.3" @@ -1612,6 +1885,37 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[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" @@ -1639,6 +1943,12 @@ 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.23" @@ -1771,6 +2081,15 @@ dependencies = [ "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" @@ -1787,6 +2106,15 @@ 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" @@ -1826,18 +2154,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", @@ -1854,6 +2182,17 @@ dependencies = [ "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.28" @@ -2034,9 +2373,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", +] [[package]] name = "valuable" @@ -2223,6 +2565,12 @@ dependencies = [ "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" @@ -2579,3 +2927,12 @@ name = "xml-rs" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a56c84a8ccd4258aed21c92f70c0f6dea75356b6892ae27c24139da456f9336" + +[[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/Cargo.toml b/Cargo.toml index ba39438..4f755b4 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,14 @@ name = "lyra-engine" version = "0.0.1" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = [ + "lyra-resource" +] [dependencies] +lyra-resource = { path = "lyra-resource", version = "0.0.1" } + winit = "0.28.1" tracing = "0.1.37" tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] } @@ -22,7 +27,6 @@ tobj = { version = "3.2.1", features = [ instant = "0.1" async-trait = "0.1.65" glam = { version = "0.24.0", features = ["bytemuck"] } -petgraph = "0.6.3" gilrs-core = "0.5.6" syn = "2.0.26" quote = "1.0.29" @@ -31,3 +35,4 @@ atomicell = "0.1.9" aligned-vec = "0.5.0" tracing-appender = "0.2.2" stopwatch = "0.0.7" +petgraph = "0.6.4" diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml new file mode 100644 index 0000000..0207327 --- /dev/null +++ b/lyra-resource/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lyra-resource" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.75" +image = "0.24.7" +thiserror = "1.0.48" +uuid = { version = "1.4.1", features = ["v4"] } diff --git a/lyra-resource/src/lib.rs b/lyra-resource/src/lib.rs new file mode 100644 index 0000000..9d7a7e5 --- /dev/null +++ b/lyra-resource/src/lib.rs @@ -0,0 +1,26 @@ +pub mod resource_manager; +pub use resource_manager::*; + +pub mod resource; +pub use resource::*; + +pub mod texture; +pub use texture::*; + +pub mod loader; +pub use loader::*; + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/lyra-resource/src/loader/mod.rs b/lyra-resource/src/loader/mod.rs new file mode 100644 index 0000000..78e5e23 --- /dev/null +++ b/lyra-resource/src/loader/mod.rs @@ -0,0 +1,35 @@ +pub mod texture; + +use std::{io, sync::Arc, fs::File}; + +use thiserror::Error; + +use crate::resource_manager::ResourceStorage; + +#[derive(Error, Debug)] +pub enum LoaderError { + #[error("A malformed path was given: '{0}'")] + InvalidPath(String), + + #[error("The loader does not support the extension '{0}'")] + UnsupportedExtension(String), + + #[error("IOError: '{0}'")] + IOError(io::Error), + + // From is implemented for this field in each loader module + #[error("Decoding error: '{0}'")] + DecodingError(anyhow::Error), +} + +impl From for LoaderError { + fn from(value: io::Error) -> Self { + LoaderError::IOError(value) + } +} + +pub trait ResourceLoader: Send + Sync { + fn extensions(&self) -> &[&str]; + fn does_support_file(&self, path: &str) -> bool; + fn load(&self, path: &str) -> Result, LoaderError>; +} \ No newline at end of file diff --git a/lyra-resource/src/loader/texture.rs b/lyra-resource/src/loader/texture.rs new file mode 100644 index 0000000..31ac93f --- /dev/null +++ b/lyra-resource/src/loader/texture.rs @@ -0,0 +1,62 @@ +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)?; + let texture = Texture { + image, + }; + let res = Resource::with_data(path, texture); + + Ok(Arc::new(res)) + } +} \ No newline at end of file diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs new file mode 100644 index 0000000..46f7740 --- /dev/null +++ b/lyra-resource/src/resource.rs @@ -0,0 +1,29 @@ +use std::sync::Arc; + +use uuid::Uuid; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum ResourceState { + Loading, + Ready, +} + +#[derive(Clone)] +pub struct Resource { + pub path: String, + pub data: Option>, + pub uuid: Uuid, + pub state: ResourceState, +} + +impl Resource { + /// Create the resource with data, its assumed the state is `Ready` + pub fn with_data(path: &str, data: T) -> Self { + Self { + path: path.to_string(), + data: Some(Arc::new(data)), + uuid: Uuid::new_v4(), + state: ResourceState::Ready, + } + } +} \ No newline at end of file diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs new file mode 100644 index 0000000..1491242 --- /dev/null +++ b/lyra-resource/src/resource_manager.rs @@ -0,0 +1,83 @@ +use std::{sync::Arc, collections::{HashMap, hash_map::DefaultHasher}, hash::{Hash, Hasher}, any::Any}; + +use thiserror::Error; + +use crate::{resource::Resource, loader::{ResourceLoader, LoaderError, texture::TextureLoader}}; + +pub trait ResourceStorage: Send + Sync + Any + 'static { + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; + fn as_arc_any(self: Arc) -> Arc; +} + +/// Implements this trait for anything that fits the type bounds +impl ResourceStorage for T { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_arc_any(self: Arc) -> Arc { + self.clone() + } +} + +#[derive(Error, Debug)] +pub enum RequestError { + #[error("{0}")] + Loader(LoaderError), + #[error("The file extension is unsupported: '{0}'")] + UnsupportedFileExtension(String), +} + +impl From for RequestError { + fn from(value: LoaderError) -> Self { + RequestError::Loader(value) + } +} + +#[derive(Default)] +pub struct ResourceManager { + resources: HashMap>, + loaders: Vec>, +} + +impl ResourceManager { + pub fn new() -> Self { + Self { + resources: HashMap::new(), + loaders: vec![ Box::new(TextureLoader::default()) ], + } + } + + pub fn request(&mut self, path: &str) -> Result>, RequestError> { + match self.resources.get(&path.to_string()) { + Some(res) => { + let res = res.clone().as_arc_any(); + let res = res.downcast::>().expect("Failure to downcast resource"); + + Ok(res) + }, + None => { + if let Some(loader) = self.loaders.iter() + .find(|l| l.does_support_file(path)) { + + // Load the resource and store it + let res = loader.load(path)?; + self.resources.insert(path.to_string(), res.clone()); + + // convert Arc to Arc + let res = res.as_arc_any(); + let res = res.downcast::>().expect("Failure to downcast resource"); + + Ok(res) + } else { + Err(RequestError::UnsupportedFileExtension(path.to_string())) + } + } + } + } +} \ No newline at end of file diff --git a/lyra-resource/src/texture.rs b/lyra-resource/src/texture.rs new file mode 100644 index 0000000..2286df7 --- /dev/null +++ b/lyra-resource/src/texture.rs @@ -0,0 +1,10 @@ +use image::{DynamicImage, ImageResult, ImageBuffer, Rgba}; + +#[derive(Clone)] +pub struct Texture { + pub image: DynamicImage, +} + +impl Texture { + +} \ No newline at end of file diff --git a/src/ecs/components/camera.rs b/src/ecs/components/camera.rs index 07b7b8a..2998686 100755 --- a/src/ecs/components/camera.rs +++ b/src/ecs/components/camera.rs @@ -20,7 +20,13 @@ impl Default for CameraComponent { } impl CameraComponent { - pub fn new() -> Self { + pub fn new_3d() -> Self { Self::default() } + + pub fn new_2d() -> Self { + let mut s = Self::default(); + s.mode = CameraProjectionMode::Orthographic; + s + } } \ No newline at end of file diff --git a/src/ecs/mod.rs b/src/ecs/mod.rs index 7ea540e..4821b86 100755 --- a/src/ecs/mod.rs +++ b/src/ecs/mod.rs @@ -1,31 +1,29 @@ -use std::collections::HashMap; - -use petgraph::{prelude::StableDiGraph, stable_graph::NodeIndex, visit::Topo}; -use tracing::warn; - -use crate::game::Controls; - pub use edict::*; pub mod components; pub mod events; pub use events::*; +use std::collections::HashMap; + +use petgraph::{stable_graph::{StableDiGraph, NodeIndex}, visit::Topo}; +use tracing::warn; + /// A trait that represents a simple system pub trait SimpleSystem { - fn setup(&mut self, controls: &mut Controls) -> anyhow::Result<()> { + fn setup(&mut self, world: &mut World) -> anyhow::Result<()> { Ok(()) } // todo: make async? - fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()>; + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>; } impl SimpleSystem for S where S: FnMut(&mut edict::World) -> anyhow::Result<()> { - fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()> { - self(controls.world) + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { + self(world) } } @@ -44,9 +42,9 @@ impl BatchedSystem { } impl SimpleSystem for BatchedSystem { - fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()> { + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { for system in self.systems.iter_mut() { - system.execute_mut(controls)?; + system.execute_mut(world)?; } Ok(()) @@ -122,13 +120,13 @@ impl SystemDispatcher { true } - pub(crate) fn execute_systems(&mut self, controls: &mut Controls) { + pub(crate) fn execute_systems(&mut self, world: &mut World) { let mut topo = Topo::new(&self.graph); while let Some(nx) = topo.next(&self.graph) { let node = self.graph.node_weight_mut(nx).unwrap(); - match node.system.execute_mut(controls) { + match node.system.execute_mut(world) { Ok(()) => {}, Err(e) => { warn!("System execution of {} resulted in an error! '{}'.", node.name, e); @@ -144,8 +142,8 @@ impl SystemDispatcher { } impl SimpleSystem for SystemDispatcher { - fn execute_mut(&mut self, controls: &mut Controls) -> anyhow::Result<()> { - self.execute_systems(controls); + fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { + self.execute_systems(world); Ok(()) } diff --git a/src/game.rs b/src/game.rs index ab157b2..e670246 100755 --- a/src/game.rs +++ b/src/game.rs @@ -68,15 +68,11 @@ impl GameLoop { } async fn update(&mut self) { - let mut controls = Controls { - world: &mut self.world, - }; - - if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut controls) { + if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut self.world) { error!("Error when executing engine ecs systems: '{}'", e); } - if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut controls) { + if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut self.world) { error!("Error when executing user ecs systems: '{}'", e); } } @@ -224,9 +220,10 @@ impl GameLoop { } pub struct Game { - pub world: Option, + world: Option, plugins: VecDeque>, system_dispatcher: Option, + startup_systems: VecDeque>, } impl Default for Game { @@ -235,6 +232,7 @@ impl Default for Game { world: Some(edict::World::new()), plugins: VecDeque::new(), system_dispatcher: Some(SystemDispatcher::new()), + startup_systems: VecDeque::new(), } } } @@ -244,6 +242,13 @@ impl Game { Self::default() } + /// Get the world of this game + pub fn world(&mut self) -> &mut edict::World { + // world is always `Some`, so unwrapping is safe + self.world.as_mut().unwrap() + } + + /// Add a system to the ecs world pub fn with_system(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self where S: SimpleSystem + 'static @@ -254,7 +259,18 @@ impl Game { self } - /// Add a plugin to the game + /// Add a startup system that will be ran right after plugins are setup. + /// They will only be ran once + pub fn with_startup_system(&mut self, system: S) -> &mut Self + where + S: SimpleSystem + 'static + { + self.startup_systems.push_back(Box::new(system)); + + self + } + + /// Add a plugin to the game. These will be executed before the window is initiated and opened pub fn with_plugin

(&mut self, plugin: P) -> &mut Self where P: Plugin + 'static @@ -264,12 +280,16 @@ impl Game { self } + /// Override the default (empty) world + /// + /// This isn't recommended, you should create a startup system and add it to `with_startup_system` pub fn with_world(&mut self, world: edict::World) -> &mut Self { self.world = Some(world); self } + /// Start the game pub async fn run(&mut self) { // init logging let (stdout_layer, _stdout_nb) = non_blocking(std::io::stdout()); @@ -286,11 +306,19 @@ impl Game { plugin.as_ref().setup(self); } + let mut world = self.world.take().unwrap_or_else(|| edict::World::new()); + + // run startup systems + while let Some(mut startup) = self.startup_systems.pop_front() { + let startup = startup.as_mut(); + startup.setup(&mut world).expect("World returned an error!"); + startup.execute_mut(&mut world).expect("World returned an error!"); + } + // start winit event loops let event_loop = EventLoop::new(); let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); - - let world = self.world.take().unwrap_or_else(|| edict::World::new()); + let system_dispatcher = self.system_dispatcher.take().unwrap(); let mut g_loop = GameLoop::new(Arc::clone(&window), world, system_dispatcher).await; g_loop.on_init().await; diff --git a/src/input.rs b/src/input.rs index fabdef8..ce0e67a 100755 --- a/src/input.rs +++ b/src/input.rs @@ -313,9 +313,7 @@ impl InputSystem { } impl SimpleSystem for InputSystem { - fn execute_mut(&mut self, controls: &mut crate::game::Controls) -> anyhow::Result<()> { - let world = &mut controls.world; - + fn execute_mut(&mut self, world: &mut edict::World) -> anyhow::Result<()> { let queue = world.get_resource_mut::() .map(|q| q.read_events::()).flatten(); diff --git a/src/lib.rs b/src/lib.rs index eefaaca..d599eed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,17 +8,6 @@ pub mod input; pub mod castable_any; pub mod plugin; -use plugin::Plugin; +pub use lyra_resource as assets; -use crate::input::InputPlugin; - -/// Default plugins of Lyra. Make sure to have these added to the Game first -#[derive(Default)] -pub struct DefaultPlugins; - -impl Plugin for DefaultPlugins { - fn setup(&self, game: &mut game::Game) { - // setup input - InputPlugin::default().setup(game); - } -} \ No newline at end of file +pub use plugin::DefaultPlugins; \ No newline at end of file diff --git a/src/plugin.rs b/src/plugin.rs index 1f28220..8be0e8e 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,4 +1,7 @@ +use lyra_resource::ResourceManager; + use crate::game::Game; +use crate::input::InputPlugin; /// A Plugin is something you can add to a `Game` that can be used to define systems, or spawn initial entities. pub trait Plugin { @@ -73,4 +76,25 @@ impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) } impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) } impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) (C13, 13) } -impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) (C13, 13) (C14, 14) } \ No newline at end of file +impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6) (C7, 7) (C8, 8) (C9, 9) (C10, 10) (C11, 11) (C12, 12) (C13, 13) (C14, 14) } + +#[derive(Default)] +pub struct ResourceManagerPlugin; + +impl Plugin for ResourceManagerPlugin { + fn setup(&self, game: &mut Game) { + game.world().insert_resource(ResourceManager::new()); + } +} + +/// Default plugins of Lyra. Make sure to have these added to the Game first +#[derive(Default)] +pub struct DefaultPlugins; + +impl Plugin for DefaultPlugins { + fn setup(&self, game: &mut Game) { + // setup input + InputPlugin::default().setup(game); + ResourceManagerPlugin::default().setup(game); + } +} \ No newline at end of file diff --git a/src/render/camera.rs b/src/render/camera.rs index a4542fd..17e9f8e 100755 --- a/src/render/camera.rs +++ b/src/render/camera.rs @@ -40,6 +40,7 @@ impl Projection { pub struct RenderCamera { view_proj: glam::Mat4, + size: PhysicalSize, aspect: f32, znear: f32, zfar: f32, @@ -50,6 +51,7 @@ impl RenderCamera { Self { view_proj: glam::Mat4::IDENTITY, + size, aspect: size.width as f32 / size.height as f32, znear: 0.1, zfar: 100.0, @@ -61,27 +63,39 @@ impl RenderCamera { } pub fn update_view_projection(&mut self, camera: &CameraComponent) -> &glam::Mat4 { - let position = camera.transform.translation; - let target = camera.transform.rotation * glam::Vec3::new(0.0, 0.0, -1.0); - let target = target.normalize(); - - /* debug!("Camera rotation: {:?}", rotation); - debug!("Camera position: {:?}", position); - debug!("Camera target: {:?}", target); */ + match camera.mode { + CameraProjectionMode::Perspective => { + let position = camera.transform.translation; + let target = camera.transform.rotation * glam::Vec3::new(0.0, 0.0, -1.0); + let target = target.normalize(); - let view = glam::Mat4::look_to_rh( - position, - target, - glam::Vec3::new(0.0, 1.0, 0.0) - ); - - let proj = glam::Mat4::perspective_rh_gl(camera.fov.to_radians(), self.aspect, self.znear, self.zfar); + let view = glam::Mat4::look_to_rh( + position, + target, + glam::Vec3::new(0.0, 1.0, 0.0) + ); + + let proj = glam::Mat4::perspective_rh_gl(camera.fov.to_radians(), self.aspect, self.znear, self.zfar); - self.view_proj = OPENGL_TO_WGPU_MATRIX * proj * view; - &self.view_proj - } + self.view_proj = OPENGL_TO_WGPU_MATRIX * proj * view; + &self.view_proj + }, + CameraProjectionMode::Orthographic => { + let position = camera.transform.translation; + let target = camera.transform.rotation * glam::Vec3::new(0.0, 0.0, -1.0); + let target = target.normalize(); - pub fn view_proj(&self) -> &glam::Mat4 { - &self.view_proj + let ratio_size_per_depth = ((camera.fov.to_radians() / 2.0) * 2.0).atan(); + let distance = (target - position).length(); + + let size_y = ratio_size_per_depth * distance; + let size_x = ratio_size_per_depth * distance * self.aspect; + + let proj = glam::Mat4::orthographic_rh_gl(-size_x, size_x, -size_y, size_y, self.znear, self.zfar); + + self.view_proj = OPENGL_TO_WGPU_MATRIX * proj; + &self.view_proj + }, + } } } \ No newline at end of file diff --git a/src/render/material.rs b/src/render/material.rs index d89ae1c..fa5de81 100755 --- a/src/render/material.rs +++ b/src/render/material.rs @@ -1,7 +1,9 @@ -use super::texture::Texture; +use std::sync::Arc; + +use lyra_resource::{Texture, Resource}; #[derive(Clone)] pub struct Material { pub shader_id: u32, - pub texture: Texture, + pub texture: Arc>, } \ No newline at end of file diff --git a/src/render/renderer.rs b/src/render/renderer.rs index bd2f117..0d1aec1 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -462,8 +462,9 @@ impl BasicRenderer { let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh); - let texture_img = &model.material.texture; - let diffuse_texture = RenderTexture::from_image(&self.device, &self.queue, &texture_img.img, 0, None).unwrap(); + 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 texture_bind_group_layout = self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { diff --git a/src/render/texture.rs b/src/render/texture.rs index 0d8d689..bf69703 100755 --- a/src/render/texture.rs +++ b/src/render/texture.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use image::GenericImageView; +use lyra_resource::{Resource, Texture}; -#[derive(Clone)] +/* #[derive(Clone)] pub struct Texture { texture_id: u32, pub img: image::DynamicImage, @@ -17,14 +18,13 @@ impl Texture { img, }) } -} +} */ #[derive(Clone)] #[allow(dead_code)] pub struct RenderTexture { - texture_id: u32, texture: Arc, view: Arc, sampler: Arc, @@ -33,12 +33,12 @@ pub struct RenderTexture { impl RenderTexture { pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], texture_id: u32, label: &str) -> anyhow::Result { + pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> anyhow::Result { let img = image::load_from_memory(bytes)?; - Self::from_image(device, queue, &img, texture_id, Some(label)) + Self::from_image(device, queue, &img, Some(label)) } - pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, texture_id: u32, label: Option<&str>) -> anyhow::Result { + pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result { let rgba = img.to_rgba8(); let dimensions = img.dimensions(); @@ -90,16 +90,16 @@ impl RenderTexture { ); Ok(Self { - texture_id, texture: Arc::new(texture), view: Arc::new(view), sampler: Arc::new(sampler), }) } - pub fn update_texture(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, texture: &Texture) { - let rgba = texture.img.to_rgba8(); - let dimensions = texture.img.dimensions(); + pub fn update_texture(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, texture: &Arc>) { + let texture = &texture.data.as_ref().unwrap().image; + let rgba = texture.to_rgba8(); + let dimensions = texture.dimensions(); let size = wgpu::Extent3d { width: dimensions.0, height: dimensions.1, @@ -162,7 +162,6 @@ impl RenderTexture { texture: Arc::new(texture), view: Arc::new(view), sampler: Arc::new(sampler), - texture_id: 0 } }