diff --git a/code/tutorials/platformer/Cargo.lock b/code/tutorials/platformer/Cargo.lock index 1e6d3e18..8cb34a6f 100644 --- a/code/tutorials/platformer/Cargo.lock +++ b/code/tutorials/platformer/Cargo.lock @@ -935,7 +935,7 @@ dependencies = [ [[package]] name = "fyrox" version = "0.32.1" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "bitflags 2.4.1", "clap", @@ -971,7 +971,7 @@ dependencies = [ [[package]] name = "fyrox-animation" version = "0.1.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "fxhash", "fyrox-core", @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "fyrox-core" version = "0.26.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "android-activity", "arrayvec", @@ -1013,7 +1013,7 @@ dependencies = [ [[package]] name = "fyrox-core-derive" version = "0.21.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "convert_case", "darling", @@ -1026,7 +1026,7 @@ dependencies = [ [[package]] name = "fyrox-resource" version = "0.10.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "fxhash", "fyrox-core", @@ -1039,7 +1039,7 @@ dependencies = [ [[package]] name = "fyrox-sound" version = "0.33.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "fyrox-core", "fyrox-resource", @@ -1056,7 +1056,7 @@ dependencies = [ [[package]] name = "fyrox-ui" version = "0.23.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "copypasta", "fontdue", @@ -1074,7 +1074,7 @@ dependencies = [ [[package]] name = "fyroxed_base" version = "0.19.0" -source = "git+https://github.com/FyroxEngine/Fyrox#57d70fe286901e11673716653751083792ad0ba1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" dependencies = [ "fyrox", "lazy_static", diff --git a/code/tutorials/platformer/editor/Cargo.toml b/code/tutorials/platformer/editor/Cargo.toml index bd26e382..6882f697 100644 --- a/code/tutorials/platformer/editor/Cargo.toml +++ b/code/tutorials/platformer/editor/Cargo.toml @@ -9,6 +9,8 @@ platformer = { path = "../game" } [dependencies.fyrox ] git = "https://github.com/FyroxEngine/Fyrox" +rev = "57d70fe286901e11673716653751083792ad0ba1" [dependencies.fyroxed_base ] git = "https://github.com/FyroxEngine/Fyrox" +rev = "57d70fe286901e11673716653751083792ad0ba1" \ No newline at end of file diff --git a/code/tutorials/platformer/executor/Cargo.toml b/code/tutorials/platformer/executor/Cargo.toml index 39daf2b2..5ef52263 100644 --- a/code/tutorials/platformer/executor/Cargo.toml +++ b/code/tutorials/platformer/executor/Cargo.toml @@ -9,3 +9,4 @@ platformer = { path = "../game" } [dependencies.fyrox ] git = "https://github.com/FyroxEngine/Fyrox" +rev = "57d70fe286901e11673716653751083792ad0ba1" \ No newline at end of file diff --git a/code/tutorials/platformer/game/Cargo.toml b/code/tutorials/platformer/game/Cargo.toml index 4983a135..ffe70bee 100644 --- a/code/tutorials/platformer/game/Cargo.toml +++ b/code/tutorials/platformer/game/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies.fyrox ] git = "https://github.com/FyroxEngine/Fyrox" +rev = "57d70fe286901e11673716653751083792ad0ba1" \ No newline at end of file diff --git a/code/tutorials/rpg/Cargo.lock b/code/tutorials/rpg/Cargo.lock new file mode 100644 index 00000000..5ed4d7e2 --- /dev/null +++ b/code/tutorials/rpg/Cargo.lock @@ -0,0 +1,3587 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aaudio" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "746e7ba6563301e3b2efeba053433c66969f343cd85b3167aa4e6ec7990688a8" +dependencies = [ + "aaudio-sys", + "libc", +] + +[[package]] +name = "aaudio-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c073abf866886bf14e3b76d736af5e96498c39ba2f9af21b68908e30463a4596" +dependencies = [ + "libc", +] + +[[package]] +name = "ab_glyph" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" +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 = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "android-activity" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b801912a977c3fd52d80511fe1c0c8480c6f957f21ae2ce1b92ffe970cf4b9" +dependencies = [ + "android-properties", + "bitflags 2.4.1", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[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 = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bindgen" +version = "0.69.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.44", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys", + "objc2", +] + +[[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" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "calloop" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" +dependencies = [ + "bitflags 2.4.1", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client 0.31.1", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[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 = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clipboard-win" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "copypasta" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133fc8675ee3a4ec9aa513584deda9aa0faeda3586b87f7f0f2ba082c66fb172" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3120ebb80a9de008e638ad833d4127d50ea3d3a960ea23ea69bc66d9358a028" +dependencies = [ + "bindgen", +] + +[[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" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +dependencies = [ + "serde", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ddsfile" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479dfe1e6737aa9e96c6ac7b69689dc4c32da8383f2c12744739d76afa8b66c4" +dependencies = [ + "bitflags 2.4.1", + "byteorder", + "enum-primitive-derive", + "num-traits", +] + +[[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", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "editor" +version = "0.1.0" +dependencies = [ + "fyrox", + "fyroxed_base", + "rpg", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enum-primitive-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e" +dependencies = [ + "num-traits", + "quote", + "syn 1.0.109", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "executor" +version = "0.1.0" +dependencies = [ + "fyrox", + "rpg", +] + +[[package]] +name = "executor-android" +version = "0.1.0" +dependencies = [ + "fyrox", + "rpg", +] + +[[package]] +name = "executor-wasm" +version = "0.1.0" +dependencies = [ + "fyrox", + "rpg", +] + +[[package]] +name = "fast_image_resize" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc789a40040e11bbe4ba31ca319406805a12fe3f8d71314bbc4bd076602ad55a" +dependencies = [ + "num-traits", + "thiserror", +] + +[[package]] +name = "fdeflate" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fontdue" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc" +dependencies = [ + "hashbrown 0.13.2", + "ttf-parser 0.15.2", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fyrox" +version = "0.32.1" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "bitflags 2.4.1", + "clap", + "ddsfile", + "fast_image_resize", + "fxhash", + "fyrox-animation", + "fyrox-core", + "fyrox-core-derive", + "fyrox-resource", + "fyrox-sound", + "fyrox-ui", + "glow", + "glutin", + "glutin-winit", + "half", + "image", + "inflate", + "lazy_static", + "rapier2d", + "rapier3d", + "raw-window-handle 0.5.2", + "rayon", + "ron", + "serde", + "strum", + "strum_macros", + "tbc", + "walkdir", + "winit", +] + +[[package]] +name = "fyrox-animation" +version = "0.1.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "fxhash", + "fyrox-core", + "spade", + "strum", + "strum_macros", +] + +[[package]] +name = "fyrox-core" +version = "0.26.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "android-activity", + "arrayvec", + "base64", + "bitflags 2.4.1", + "byteorder", + "futures", + "fxhash", + "fyrox-core-derive", + "instant", + "js-sys", + "lazy_static", + "memoffset 0.9.0", + "nalgebra", + "notify", + "num-traits", + "once_cell", + "parking_lot", + "rand", + "serde", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "fyrox-core-derive" +version = "0.21.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "convert_case", + "darling", + "fxhash", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "fyrox-resource" +version = "0.10.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "fxhash", + "fyrox-core", + "rayon", + "ron", + "serde", + "walkdir", +] + +[[package]] +name = "fyrox-sound" +version = "0.33.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "fyrox-core", + "fyrox-resource", + "hound", + "hrtf", + "lewton", + "ogg", + "serde", + "strum", + "strum_macros", + "tinyaudio", +] + +[[package]] +name = "fyrox-ui" +version = "0.23.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "copypasta", + "fontdue", + "fxhash", + "fyrox-core", + "fyrox-resource", + "lazy_static", + "notify", + "serde", + "strum", + "strum_macros", + "sysinfo", +] + +[[package]] +name = "fyroxed_base" +version = "0.19.0" +source = "git+https://github.com/FyroxEngine/Fyrox?rev=57d70fe286901e11673716653751083792ad0ba1#57d70fe286901e11673716653751083792ad0ba1" +dependencies = [ + "fyrox", + "lazy_static", + "open", + "ron", + "rust-fuzzy-search", + "serde", + "strum", + "strum_macros", + "toml", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +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]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[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 = "glutin" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005459a22af86adc706522d78d360101118e2638ec21df3852fcc626e0dbb212" +dependencies = [ + "bitflags 2.4.1", + "cfg_aliases", + "cgl", + "core-foundation", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "icrate", + "libloading", + "objc2", + "once_cell", + "raw-window-handle 0.5.2", + "wayland-sys 0.31.1", + "windows-sys 0.48.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle 0.5.2", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +dependencies = [ + "gl_generator", + "windows-sys 0.48.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "half" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "hrtf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4de47a84fd55fa33aa5ef337016814fdc869fdad23e7898b5322fa290248e6" +dependencies = [ + "byteorder", + "rubato", + "rustfft", +] + +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2", + "dispatch", + "objc2", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "tiff", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[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 = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder", + "ogg", + "tinyvec", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +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 = "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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nalgebra" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "serde", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.4.1", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "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.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +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.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[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 = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.1", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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-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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "open" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" +dependencies = [ + "ttf-parser 0.20.0", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "parry2d" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104ae65232e20477a98f9f1e75ca9850eae24a2ea846a2b1a0af03ad752136ce" +dependencies = [ + "approx", + "arrayvec", + "bitflags 1.3.2", + "downcast-rs", + "either", + "nalgebra", + "num-derive", + "num-traits", + "rustc-hash", + "simba", + "slab", + "smallvec", + "spade", +] + +[[package]] +name = "parry3d" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55dc0e6db79bddbc5fd583569f7356cdcc63e1e9b2b93a9ab70dd8e717160e0" +dependencies = [ + "approx", + "arrayvec", + "bitflags 1.3.2", + "downcast-rs", + "either", + "nalgebra", + "num-derive", + "num-traits", + "rustc-hash", + "simba", + "slab", + "smallvec", + "spade", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[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 = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[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 = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primal-check" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0" +dependencies = [ + "num-integer", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" +dependencies = [ + "proc-macro2", +] + +[[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", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rapier2d" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94d294a9b96694c14888dd0e8ce77620dcc4f2f49264109ef835fa5e2285b84" +dependencies = [ + "approx", + "arrayvec", + "bit-vec", + "bitflags 1.3.2", + "crossbeam", + "downcast-rs", + "nalgebra", + "num-derive", + "num-traits", + "parry2d", + "rustc-hash", + "simba", +] + +[[package]] +name = "rapier3d" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62a8a0bd9d3135f7b4eb45d0796540e7bab47b6b7c974f90567ccc5a0454f42b" +dependencies = [ + "approx", + "arrayvec", + "bit-vec", + "bitflags 1.3.2", + "crossbeam", + "downcast-rs", + "nalgebra", + "num-derive", + "num-traits", + "parry3d", + "rustc-hash", + "simba", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[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 = "realfft" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953d9f7e5cdd80963547b456251296efc2626ed4e3cbf36c869d9564e0220571" +dependencies = [ + "rustfft", +] + +[[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 = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "robust" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags 2.4.1", + "serde", + "serde_derive", +] + +[[package]] +name = "rpg" +version = "0.1.0" +dependencies = [ + "fyrox", +] + +[[package]] +name = "rubato" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6dd52e80cfc21894deadf554a5673002938ae4625f7a283e536f9cf7c17b0d5" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "realfft", +] + +[[package]] +name = "rust-fuzzy-search" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustfft" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", + "version_check", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[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.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.3", + "smithay-client-toolkit 0.18.0", + "tiny-skia", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[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", + "dlib", + "lazy_static", + "log", + "memmap2 0.5.10", + "nix 0.24.3", + "pkg-config", + "wayland-client 0.29.5", + "wayland-cursor 0.29.5", + "wayland-protocols 0.29.5", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" +dependencies = [ + "bitflags 2.4.1", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.3", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client 0.31.1", + "wayland-csd-frame", + "wayland-cursor 0.31.0", + "wayland-protocols 0.31.0", + "wayland-protocols-wlr", + "wayland-scanner 0.31.0", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +dependencies = [ + "smithay-client-toolkit 0.16.1", + "wayland-client 0.29.5", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + +[[package]] +name = "spade" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd774eb23cff002036706e6ea83c3f4ab4c80dad89da76fe16d49f77ab71682f" +dependencies = [ + "hashbrown 0.14.3", + "num-traits", + "robust", + "smallvec", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.44", +] + +[[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.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d27c2c202598d05175a6dd3af46824b7f747f8d8e9b14c623f19fa5069735d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sysinfo" +version = "0.29.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + +[[package]] +name = "tbc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5488807ecc13530932d661bc4ea7ea621080fdbf354b7f54506fcc46d4d63bf5" + +[[package]] +name = "thiserror" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[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 = "tiny-skia" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a067b809476893fce6a254cf285850ff69c847e6cfbade6a20b655b6c7e80d" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de35e8a90052baaaf61f171680ac2f8e925a1e43ea9d2e3a00514772250e541" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyaudio" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bd6687ec414a127fe390bbd4b547a307947ee38e16f98aa48b5d662bea1ef" +dependencies = [ + "aaudio", + "alsa-sys", + "core-foundation-sys", + "coreaudio-sys", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "transpose" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6522d49d03727ffb138ae4cbc1283d3774f0d10aa7f9bf52e6784c45daf9b23" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[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.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.44", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wayland-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" +dependencies = [ + "cc", + "downcast-rs", + "nix 0.26.4", + "scoped-tls", + "smallvec", + "wayland-sys 0.31.1", +] + +[[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 0.29.5", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-client" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +dependencies = [ + "bitflags 2.4.1", + "nix 0.26.4", + "wayland-backend", + "wayland-scanner 0.31.0", +] + +[[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 0.29.5", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.4.1", + "cursor-icon", + "wayland-backend", +] + +[[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 0.29.5", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" +dependencies = [ + "nix 0.26.4", + "wayland-client 0.31.1", + "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 0.29.5", + "wayland-commons", + "wayland-scanner 0.29.5", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client 0.31.1", + "wayland-scanner 0.31.0", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client 0.31.1", + "wayland-protocols 0.31.0", + "wayland-scanner 0.31.0", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client 0.31.1", + "wayland-protocols 0.31.0", + "wayland-scanner 0.31.0", +] + +[[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-scanner" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[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 = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +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 = "wide" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[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-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +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-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-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[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-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[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_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[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_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[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_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[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_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[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_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[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_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[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 = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winit" +version = "0.29.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc1a7ae1076890701c7dd71ea35b2aebaf9aeb7b8868ac2d33b1c7e8ef93c00" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.4.1", + "bytemuck", + "calloop", + "cfg_aliases", + "core-foundation", + "core-graphics", + "cursor-icon", + "icrate", + "js-sys", + "libc", + "log", + "memmap2 0.9.3", + "ndk", + "ndk-sys", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "redox_syscall 0.3.5", + "rustix", + "sctk-adwaita", + "serde", + "smithay-client-toolkit 0.18.0", + "smol_str", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client 0.31.1", + "wayland-protocols 0.31.0", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.48.0", + "x11-dl", + "x11rb 0.13.0", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-clipboard" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980b9aa9226c3b7de8e2adb11bf20124327c054e0e5812d2aac0b5b5a87e7464" +dependencies = [ + "x11rb 0.10.1", +] + +[[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 = "x11rb" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +dependencies = [ + "gethostname 0.2.3", + "nix 0.24.3", + "winapi", + "winapi-wsapoll", + "x11rb-protocol 0.10.0", +] + +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "as-raw-xcb-connection", + "gethostname 0.4.3", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol 0.13.0", +] + +[[package]] +name = "x11rb-protocol" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +dependencies = [ + "nix 0.24.3", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" +dependencies = [ + "bitflags 2.4.1", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] diff --git a/code/tutorials/rpg/Cargo.toml b/code/tutorials/rpg/Cargo.toml new file mode 100644 index 00000000..f022ac0e --- /dev/null +++ b/code/tutorials/rpg/Cargo.toml @@ -0,0 +1,19 @@ + +[workspace] +members = ["editor", "executor", "executor-wasm", "executor-android", "game"] +resolver = "2" + +[workspace.dependencies.fyrox] +git = "https://github.com/FyroxEngine/Fyrox" +rev = "57d70fe286901e11673716653751083792ad0ba1" + +[workspace.dependencies.fyroxed_base] +git = "https://github.com/FyroxEngine/Fyrox" +rev = "57d70fe286901e11673716653751083792ad0ba1" + +# Optimize the engine in debug builds, but leave project's code non-optimized. +# By using this technique, you can still debug you code, but engine will be fully +# optimized and debug builds won't be terribly slow. With this option, you can +# compile your game in debug mode, which is much faster (at least x3), than release. +[profile.dev.package."*"] +opt-level = 3 diff --git a/code/tutorials/rpg/data/models/Bark_DiffuseColor.jpg b/code/tutorials/rpg/data/models/Bark_DiffuseColor.jpg new file mode 100644 index 00000000..2ad817d4 Binary files /dev/null and b/code/tutorials/rpg/data/models/Bark_DiffuseColor.jpg differ diff --git a/code/tutorials/rpg/data/models/Leaves_DiffuseColor.png b/code/tutorials/rpg/data/models/Leaves_DiffuseColor.png new file mode 100644 index 00000000..a8786718 Binary files /dev/null and b/code/tutorials/rpg/data/models/Leaves_DiffuseColor.png differ diff --git a/code/tutorials/rpg/data/models/barrel/barrel.fbx b/code/tutorials/rpg/data/models/barrel/barrel.fbx new file mode 100644 index 00000000..6f4c9cf8 Binary files /dev/null and b/code/tutorials/rpg/data/models/barrel/barrel.fbx differ diff --git a/code/tutorials/rpg/data/models/barrel/barrel.rgs b/code/tutorials/rpg/data/models/barrel/barrel.rgs new file mode 100644 index 00000000..24e28a1c Binary files /dev/null and b/code/tutorials/rpg/data/models/barrel/barrel.rgs differ diff --git a/code/tutorials/rpg/data/models/barrel/metal.jpg b/code/tutorials/rpg/data/models/barrel/metal.jpg new file mode 100644 index 00000000..d278f01a Binary files /dev/null and b/code/tutorials/rpg/data/models/barrel/metal.jpg differ diff --git a/code/tutorials/rpg/data/models/barrel/metal_NRM.jpg b/code/tutorials/rpg/data/models/barrel/metal_NRM.jpg new file mode 100644 index 00000000..d5e83e40 Binary files /dev/null and b/code/tutorials/rpg/data/models/barrel/metal_NRM.jpg differ diff --git a/code/tutorials/rpg/data/models/barrel/wood_3.jpg b/code/tutorials/rpg/data/models/barrel/wood_3.jpg new file mode 100644 index 00000000..103e3463 Binary files /dev/null and b/code/tutorials/rpg/data/models/barrel/wood_3.jpg differ diff --git a/code/tutorials/rpg/data/models/barrel/wood_3_NRM.jpg b/code/tutorials/rpg/data/models/barrel/wood_3_NRM.jpg new file mode 100644 index 00000000..72fa92d1 Binary files /dev/null and b/code/tutorials/rpg/data/models/barrel/wood_3_NRM.jpg differ diff --git a/code/tutorials/rpg/data/models/bush/bush.fbx b/code/tutorials/rpg/data/models/bush/bush.fbx new file mode 100644 index 00000000..3102453b Binary files /dev/null and b/code/tutorials/rpg/data/models/bush/bush.fbx differ diff --git a/code/tutorials/rpg/data/models/bush/bush01.png b/code/tutorials/rpg/data/models/bush/bush01.png new file mode 100644 index 00000000..df605849 Binary files /dev/null and b/code/tutorials/rpg/data/models/bush/bush01.png differ diff --git a/code/tutorials/rpg/data/models/bush/bush02.png b/code/tutorials/rpg/data/models/bush/bush02.png new file mode 100644 index 00000000..fb504235 Binary files /dev/null and b/code/tutorials/rpg/data/models/bush/bush02.png differ diff --git a/code/tutorials/rpg/data/models/bush/bush03.png b/code/tutorials/rpg/data/models/bush/bush03.png new file mode 100644 index 00000000..087c12af Binary files /dev/null and b/code/tutorials/rpg/data/models/bush/bush03.png differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire.fbx b/code/tutorials/rpg/data/models/campfire/Campfire.fbx new file mode 100644 index 00000000..55a80eff Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire.fbx differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_AO.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_AO.jpg new file mode 100644 index 00000000..3e01ee9d Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_AO.jpg differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_BaseColor_00.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_BaseColor_00.jpg new file mode 100644 index 00000000..9c416070 Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_BaseColor_00.jpg differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_BaseColor_01.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_BaseColor_01.jpg new file mode 100644 index 00000000..b1055914 Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_BaseColor_01.jpg differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Metallic.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Metallic.jpg new file mode 100644 index 00000000..5a1617ba Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Metallic.jpg differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Normal_DX.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Normal_DX.jpg new file mode 100644 index 00000000..152d82ff Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Normal_DX.jpg differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Normal_JL.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Normal_JL.jpg new file mode 100644 index 00000000..dd2357ff Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Normal_JL.jpg differ diff --git a/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Roughness.jpg b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Roughness.jpg new file mode 100644 index 00000000..4094671e Binary files /dev/null and b/code/tutorials/rpg/data/models/campfire/Campfire_MAT_Roughness.jpg differ diff --git a/code/tutorials/rpg/data/models/paladin/Paladin_diffuse.jpg b/code/tutorials/rpg/data/models/paladin/Paladin_diffuse.jpg new file mode 100644 index 00000000..e4de673b Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/Paladin_diffuse.jpg differ diff --git a/code/tutorials/rpg/data/models/paladin/Paladin_normal.jpg b/code/tutorials/rpg/data/models/paladin/Paladin_normal.jpg new file mode 100644 index 00000000..2836438f Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/Paladin_normal.jpg differ diff --git a/code/tutorials/rpg/data/models/paladin/Paladin_specular.jpg b/code/tutorials/rpg/data/models/paladin/Paladin_specular.jpg new file mode 100644 index 00000000..c976f2ed Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/Paladin_specular.jpg differ diff --git a/code/tutorials/rpg/data/models/paladin/Running.fbx b/code/tutorials/rpg/data/models/paladin/Running.fbx new file mode 100644 index 00000000..659a629b Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/Running.fbx differ diff --git a/code/tutorials/rpg/data/models/paladin/idle.fbx b/code/tutorials/rpg/data/models/paladin/idle.fbx new file mode 100644 index 00000000..1584d074 Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/idle.fbx differ diff --git a/code/tutorials/rpg/data/models/paladin/paladin.fbx b/code/tutorials/rpg/data/models/paladin/paladin.fbx new file mode 100644 index 00000000..d8236bfb Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/paladin.fbx differ diff --git a/code/tutorials/rpg/data/models/paladin/paladin.rgs b/code/tutorials/rpg/data/models/paladin/paladin.rgs new file mode 100644 index 00000000..72dd3895 Binary files /dev/null and b/code/tutorials/rpg/data/models/paladin/paladin.rgs differ diff --git a/code/tutorials/rpg/data/models/tree.fbx b/code/tutorials/rpg/data/models/tree.fbx new file mode 100644 index 00000000..d392ebef Binary files /dev/null and b/code/tutorials/rpg/data/models/tree.fbx differ diff --git a/code/tutorials/rpg/data/models/tree.rgs b/code/tutorials/rpg/data/models/tree.rgs new file mode 100644 index 00000000..3577c8e6 Binary files /dev/null and b/code/tutorials/rpg/data/models/tree.rgs differ diff --git a/code/tutorials/rpg/data/models/wood_cabin/WoodCabinDif.jpg b/code/tutorials/rpg/data/models/wood_cabin/WoodCabinDif.jpg new file mode 100644 index 00000000..acbbefe2 Binary files /dev/null and b/code/tutorials/rpg/data/models/wood_cabin/WoodCabinDif.jpg differ diff --git a/code/tutorials/rpg/data/models/wood_cabin/WoodCabinNM.jpg b/code/tutorials/rpg/data/models/wood_cabin/WoodCabinNM.jpg new file mode 100644 index 00000000..ec0c4c4f Binary files /dev/null and b/code/tutorials/rpg/data/models/wood_cabin/WoodCabinNM.jpg differ diff --git a/code/tutorials/rpg/data/models/wood_cabin/WoodCabinSM.jpg b/code/tutorials/rpg/data/models/wood_cabin/WoodCabinSM.jpg new file mode 100644 index 00000000..669d853a Binary files /dev/null and b/code/tutorials/rpg/data/models/wood_cabin/WoodCabinSM.jpg differ diff --git a/code/tutorials/rpg/data/models/wood_cabin/WoodenCabinFbx.fbx b/code/tutorials/rpg/data/models/wood_cabin/WoodenCabinFbx.fbx new file mode 100644 index 00000000..26ef518c Binary files /dev/null and b/code/tutorials/rpg/data/models/wood_cabin/WoodenCabinFbx.fbx differ diff --git a/code/tutorials/rpg/data/models/wood_cabin/wooden_cabin.rgs b/code/tutorials/rpg/data/models/wood_cabin/wooden_cabin.rgs new file mode 100644 index 00000000..ef18718a Binary files /dev/null and b/code/tutorials/rpg/data/models/wood_cabin/wooden_cabin.rgs differ diff --git a/code/tutorials/rpg/data/scene.rgs b/code/tutorials/rpg/data/scene.rgs new file mode 100644 index 00000000..d3c68ca8 Binary files /dev/null and b/code/tutorials/rpg/data/scene.rgs differ diff --git a/code/tutorials/rpg/data/textures/Grass2_DiffuseColor.jpg b/code/tutorials/rpg/data/textures/Grass2_DiffuseColor.jpg new file mode 100644 index 00000000..f5ce2c00 Binary files /dev/null and b/code/tutorials/rpg/data/textures/Grass2_DiffuseColor.jpg differ diff --git a/code/tutorials/rpg/data/textures/Grass_DiffuseColor.jpg b/code/tutorials/rpg/data/textures/Grass_DiffuseColor.jpg new file mode 100644 index 00000000..af12e9cb Binary files /dev/null and b/code/tutorials/rpg/data/textures/Grass_DiffuseColor.jpg differ diff --git a/code/tutorials/rpg/data/textures/Soil_DiffuseColor.jpg b/code/tutorials/rpg/data/textures/Soil_DiffuseColor.jpg new file mode 100644 index 00000000..15a1d893 Binary files /dev/null and b/code/tutorials/rpg/data/textures/Soil_DiffuseColor.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/back.jpg b/code/tutorials/rpg/data/textures/skybox/back.jpg new file mode 100644 index 00000000..d8ad9269 Binary files /dev/null and b/code/tutorials/rpg/data/textures/skybox/back.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/back.jpg.options b/code/tutorials/rpg/data/textures/skybox/back.jpg.options new file mode 100644 index 00000000..e0ee7f7b --- /dev/null +++ b/code/tutorials/rpg/data/textures/skybox/back.jpg.options @@ -0,0 +1,8 @@ +( + minification_filter: LinearMipMapLinear, + magnification_filter: Linear, + s_wrap_mode: Repeat, + t_wrap_mode: Repeat, + anisotropy: 16, + compression: NoCompression, +) \ No newline at end of file diff --git a/code/tutorials/rpg/data/textures/skybox/down.jpg b/code/tutorials/rpg/data/textures/skybox/down.jpg new file mode 100644 index 00000000..eac0191e Binary files /dev/null and b/code/tutorials/rpg/data/textures/skybox/down.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/down.jpg.options b/code/tutorials/rpg/data/textures/skybox/down.jpg.options new file mode 100644 index 00000000..e0ee7f7b --- /dev/null +++ b/code/tutorials/rpg/data/textures/skybox/down.jpg.options @@ -0,0 +1,8 @@ +( + minification_filter: LinearMipMapLinear, + magnification_filter: Linear, + s_wrap_mode: Repeat, + t_wrap_mode: Repeat, + anisotropy: 16, + compression: NoCompression, +) \ No newline at end of file diff --git a/code/tutorials/rpg/data/textures/skybox/front.jpg b/code/tutorials/rpg/data/textures/skybox/front.jpg new file mode 100644 index 00000000..b7bb32c0 Binary files /dev/null and b/code/tutorials/rpg/data/textures/skybox/front.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/front.jpg.options b/code/tutorials/rpg/data/textures/skybox/front.jpg.options new file mode 100644 index 00000000..e0ee7f7b --- /dev/null +++ b/code/tutorials/rpg/data/textures/skybox/front.jpg.options @@ -0,0 +1,8 @@ +( + minification_filter: LinearMipMapLinear, + magnification_filter: Linear, + s_wrap_mode: Repeat, + t_wrap_mode: Repeat, + anisotropy: 16, + compression: NoCompression, +) \ No newline at end of file diff --git a/code/tutorials/rpg/data/textures/skybox/left.jpg b/code/tutorials/rpg/data/textures/skybox/left.jpg new file mode 100644 index 00000000..e0baf4fd Binary files /dev/null and b/code/tutorials/rpg/data/textures/skybox/left.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/left.jpg.options b/code/tutorials/rpg/data/textures/skybox/left.jpg.options new file mode 100644 index 00000000..e0ee7f7b --- /dev/null +++ b/code/tutorials/rpg/data/textures/skybox/left.jpg.options @@ -0,0 +1,8 @@ +( + minification_filter: LinearMipMapLinear, + magnification_filter: Linear, + s_wrap_mode: Repeat, + t_wrap_mode: Repeat, + anisotropy: 16, + compression: NoCompression, +) \ No newline at end of file diff --git a/code/tutorials/rpg/data/textures/skybox/right.jpg b/code/tutorials/rpg/data/textures/skybox/right.jpg new file mode 100644 index 00000000..fba9ea20 Binary files /dev/null and b/code/tutorials/rpg/data/textures/skybox/right.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/right.jpg.options b/code/tutorials/rpg/data/textures/skybox/right.jpg.options new file mode 100644 index 00000000..e0ee7f7b --- /dev/null +++ b/code/tutorials/rpg/data/textures/skybox/right.jpg.options @@ -0,0 +1,8 @@ +( + minification_filter: LinearMipMapLinear, + magnification_filter: Linear, + s_wrap_mode: Repeat, + t_wrap_mode: Repeat, + anisotropy: 16, + compression: NoCompression, +) \ No newline at end of file diff --git a/code/tutorials/rpg/data/textures/skybox/up.jpg b/code/tutorials/rpg/data/textures/skybox/up.jpg new file mode 100644 index 00000000..32c3bf0d Binary files /dev/null and b/code/tutorials/rpg/data/textures/skybox/up.jpg differ diff --git a/code/tutorials/rpg/data/textures/skybox/up.jpg.options b/code/tutorials/rpg/data/textures/skybox/up.jpg.options new file mode 100644 index 00000000..e0ee7f7b --- /dev/null +++ b/code/tutorials/rpg/data/textures/skybox/up.jpg.options @@ -0,0 +1,8 @@ +( + minification_filter: LinearMipMapLinear, + magnification_filter: Linear, + s_wrap_mode: Repeat, + t_wrap_mode: Repeat, + anisotropy: 16, + compression: NoCompression, +) \ No newline at end of file diff --git a/code/tutorials/rpg/data/textures/smoke_04.tga b/code/tutorials/rpg/data/textures/smoke_04.tga new file mode 100644 index 00000000..262d0f13 Binary files /dev/null and b/code/tutorials/rpg/data/textures/smoke_04.tga differ diff --git a/code/tutorials/rpg/editor/Cargo.toml b/code/tutorials/rpg/editor/Cargo.toml new file mode 100644 index 00000000..412f35ab --- /dev/null +++ b/code/tutorials/rpg/editor/Cargo.toml @@ -0,0 +1,10 @@ + +[package] +name = "editor" +version = "0.1.0" +edition = "2021" + +[dependencies] +fyrox = {workspace = true} +fyroxed_base = {workspace = true} +rpg = { path = "../game" } \ No newline at end of file diff --git a/code/tutorials/rpg/editor/src/main.rs b/code/tutorials/rpg/editor/src/main.rs new file mode 100644 index 00000000..84e8aac4 --- /dev/null +++ b/code/tutorials/rpg/editor/src/main.rs @@ -0,0 +1,17 @@ +//! Editor with your game connected to it as a plugin. +use fyrox::event_loop::EventLoop; +use fyroxed_base::{Editor, StartupData}; +use rpg::GameConstructor; + +fn main() { + let event_loop = EventLoop::new().unwrap(); + let mut editor = Editor::new( + &event_loop, + Some(StartupData { + working_directory: Default::default(), + scene: "data/scene.rgs".into(), + }), + ); + editor.add_game_plugin(GameConstructor); + editor.run(event_loop) +} diff --git a/code/tutorials/rpg/executor-android/Cargo.toml b/code/tutorials/rpg/executor-android/Cargo.toml new file mode 100644 index 00000000..804e74c5 --- /dev/null +++ b/code/tutorials/rpg/executor-android/Cargo.toml @@ -0,0 +1,16 @@ + +[package] +name = "executor-android" +version = "0.1.0" +edition = "2021" + +[package.metadata.android] +assets = "../data" +strip = "strip" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +fyrox = { workspace = true } +rpg = { path = "../game" } \ No newline at end of file diff --git a/code/tutorials/rpg/executor-android/src/lib.rs b/code/tutorials/rpg/executor-android/src/lib.rs new file mode 100644 index 00000000..bb96aff9 --- /dev/null +++ b/code/tutorials/rpg/executor-android/src/lib.rs @@ -0,0 +1,17 @@ +//! Android executor with your game connected to it as a plugin. +use fyrox::{ + core::io, engine::executor::Executor, event_loop::EventLoopBuilder, + platform::android::EventLoopBuilderExtAndroid, +}; +use rpg::GameConstructor; + +#[no_mangle] +fn android_main(app: fyrox::platform::android::activity::AndroidApp) { + io::ANDROID_APP + .set(app.clone()) + .expect("ANDROID_APP cannot be set twice."); + let event_loop = EventLoopBuilder::new().with_android_app(app).build(); + let mut executor = Executor::from_params(event_loop, Default::default()); + executor.add_plugin_constructor(GameConstructor); + executor.run() +} \ No newline at end of file diff --git a/code/tutorials/rpg/executor-wasm/Cargo.toml b/code/tutorials/rpg/executor-wasm/Cargo.toml new file mode 100644 index 00000000..f58a559c --- /dev/null +++ b/code/tutorials/rpg/executor-wasm/Cargo.toml @@ -0,0 +1,12 @@ + +[package] +name = "executor-wasm" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +fyrox = {workspace = true} +rpg = { path = "../game" } \ No newline at end of file diff --git a/code/tutorials/rpg/executor-wasm/index.html b/code/tutorials/rpg/executor-wasm/index.html new file mode 100644 index 00000000..32e51e05 --- /dev/null +++ b/code/tutorials/rpg/executor-wasm/index.html @@ -0,0 +1,20 @@ + + +
+ + +mod player;
line somewhere at the beginning of the game/src/lib.rs
.
The second place is PluginConstructor::register
method - every script must be registered before use. Let's do so by adding
the following code to the method:
-#![allow(unused)] +
#![allow(unused)] fn main() { -extern crate fyrox; -use fyrox::{ - core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*, TypeUuidProvider}, - impl_component_provider, - plugin::{Plugin, PluginConstructor, PluginContext, PluginRegistrationContext}, - script::ScriptTrait, -}; - -#[derive(Clone, Visit, Reflect, Debug, Default)] -struct Player; - -impl_component_provider!(Player); - -impl TypeUuidProvider for Player { - fn type_uuid() -> Uuid { - unimplemented!() - } -} - -impl ScriptTrait for Player { - fn id(&self) -> Uuid { - unimplemented!() - } -} - -pub struct GameConstructor; - impl PluginConstructor for GameConstructor { - fn register(&self, context: PluginRegistrationContext) { - context - .serialization_context - .script_constructors - .add::<Player>("Player"); - } - - fn create_instance( - &self, - scene_path: Option<&str>, - context: PluginContext, - ) -> Box<dyn Plugin> { - unimplemented!() - } -} + fn register(&self, context: PluginRegistrationContext) { + context + .serialization_context + .script_constructors + .add::<Player>("Player"); + } }
Preparation steps are now finished, and we can start filling the script with some useful code. Navigate to the
-player.rs
and you'll see quite a lot of code. Most of the methods, however, can be removed, and we're only interested inon_update
andon_os_event
. But for now, let's add the following fields in thePlayer
struct:#![allow(unused)] +
#![allow(unused)] fn main() { -extern crate fyrox; -use fyrox::{ - core::{ - math::SmoothAngle, pool::Handle, reflect::prelude::*, variable::InheritableVariable, - visitor::prelude::*, - }, - scene::node::Node, -}; - #[derive(Visit, Reflect, Default, Debug, Clone)] pub struct Player { - #[visit(optional)] - camera_pivot: InheritableVariable<Handle<Node>>, + #[visit(optional)] + camera_pivot: InheritableVariable<Handle<Node>>, - #[visit(optional)] - camera_hinge: InheritableVariable<Handle<Node>>, + #[visit(optional)] + camera_hinge: InheritableVariable<Handle<Node>>, - #[visit(optional)] - state_machine: InheritableVariable<Handle<Node>>, + #[visit(optional)] + state_machine: InheritableVariable<Handle<Node>>, - #[visit(optional)] - model_pivot: InheritableVariable<Handle<Node>>, + #[visit(optional)] + model_pivot: InheritableVariable<Handle<Node>>, - #[visit(optional)] - model: InheritableVariable<Handle<Node>>, + #[visit(optional)] + model: InheritableVariable<Handle<Node>>, - #[visit(optional)] - model_yaw: InheritableVariable<SmoothAngle>, + #[visit(optional)] + model_yaw: InheritableVariable<SmoothAngle>, #[reflect(hidden)] #[visit(skip)] @@ -12389,7 +12344,7 @@
Player Script #[reflect(hidden)] #[visit(skip)] walk_right: bool, - + #[reflect(hidden)] #[visit(skip)] yaw: f32, @@ -12413,52 +12368,36 @@
Player Script
Let's start writing player controller's logic.
Event Handling
We'll start from keyboard and mouse event handling, add the following code to the
-impl ScriptTrait for Player
:+ Event::DeviceEvent { event, .. } => { + if let DeviceEvent::MouseMotion { delta } = event { + let mouse_sens = 0.2 * ctx.dt; + self.yaw -= (delta.0 as f32) * mouse_sens; + self.pitch = (self.pitch + (delta.1 as f32) * mouse_sens) + .clamp(-90.0f32.to_radians(), 90.0f32.to_radians()); + } + } + _ => (), + } + } +}#![allow(unused)] +
fn on_os_event(&mut self, event: &Event<()>, ctx: &mut ScriptContext) { + match event { + Event::WindowEvent { event, .. } => { + if let WindowEvent::KeyboardInput { event, .. } = event { + if let PhysicalKey::Code(code) = event.physical_key { + let pressed = event.state == ElementState::Pressed; + match code { + KeyCode::KeyW => self.walk_forward = pressed, + KeyCode::KeyS => self.walk_backward = pressed, + KeyCode::KeyA => self.walk_left = pressed, + KeyCode::KeyD => self.walk_right = pressed, + _ => (), + } + } + } } - } - } - Event::DeviceEvent { event, .. } => { - if let DeviceEvent::MouseMotion { delta } = event { - let mouse_sens = 0.2 * ctx.dt; - self.yaw -= (delta.0 as f32) * mouse_sens; - self.pitch = (self.pitch + (delta.1 as f32) * mouse_sens) - .clamp(-90.0f32.to_radians(), 90.0f32.to_radians()); - } - } - _ => (), - } -} -} -}#![allow(unused)] fn main() { -extern crate fyrox; -use fyrox::{ - event::{DeviceEvent, ElementState, Event, WindowEvent}, - keyboard::KeyCode, - script::ScriptContext, -}; - -struct Player { - walk_forward: bool, - walk_backward: bool, - walk_left: bool, - walk_right: bool, - yaw: f32, - pitch: f32, -} - -impl Player { -fn on_os_event(&mut self, event: &Event<()>, ctx: &mut ScriptContext) { - match event { - Event::WindowEvent { event, .. } => { - if let WindowEvent::KeyboardInput { event, .. } = event { - let pressed = event.state == ElementState::Pressed; - match event.physical_key { - KeyCode::KeyW => self.walk_forward = pressed, - KeyCode::KeyS => self.walk_backward = pressed, - KeyCode::KeyA => self.walk_left = pressed, - KeyCode::KeyD => self.walk_right = pressed, - _ => (), +
This code consists of two major sections:
@@ -12467,69 +12406,24 @@KeyboardInput
event handling andMouseMotion
event handling. Let's start fromKeyboardInput
event. At the beginning of it we're checking if a key was pressed or not and saving it to thepressed
flag, then we check forW
,S
,A
,D
keys and set each movement flag accordingly.Event Handling<
Logic
The next important step is to apply all the data we have to a bunch of scene nodes the player consists of. Let's fill the
-on_update
method with the following code:#![allow(unused)] +
fn on_update(&mut self, ctx: &mut ScriptContext) { + // Step 1. Fetch the velocity vector from the animation blending state machine. + let transform = ctx.scene.graph[*self.model].global_transform(); + let mut velocity = Vector3::default(); + if let Some(state_machine) = ctx + .scene + .graph + .try_get(*self.state_machine) + .and_then(|node| node.query_component_ref::<AnimationBlendingStateMachine>()) + { + if let Some(root_motion) = state_machine.machine().pose().root_motion() { + velocity = transform + .transform_vector(&root_motion.delta_position) + .scale(1.0 / ctx.dt); + } + } // Step 2. Apply the velocity to the rigid body and lock rotations. if let Some(body) = ctx.scene.graph.try_get_mut_of_type::<RigidBody>(ctx.handle) { @@ -12613,11 +12507,6 @@#![allow(unused)] fn main() { -extern crate fyrox; -use fyrox::{ - animation::machine::Parameter, - core::{ - algebra::{UnitQuaternion, Vector3}, - math::SmoothAngle, - pool::Handle, - reflect::prelude::*, - uuid::{uuid, Uuid}, - variable::InheritableVariable, - visitor::prelude::*, - TypeUuidProvider, - }, - event::{DeviceEvent, ElementState, Event, WindowEvent}, - impl_component_provider, - keyboard::KeyCode, - scene::{animation::absm::AnimationBlendingStateMachine, node::Node, rigidbody::RigidBody}, - script::{ScriptContext, ScriptTrait}, -}; - -#[derive(Visit, Reflect, Default, Debug, Clone)] -pub struct Player { - camera_pivot: InheritableVariable<Handle<Node>>, - camera_hinge: InheritableVariable<Handle<Node>>, - state_machine: InheritableVariable<Handle<Node>>, - model_pivot: InheritableVariable<Handle<Node>>, - model: InheritableVariable<Handle<Node>>, - model_yaw: InheritableVariable<SmoothAngle>, - walk_forward: bool, - walk_backward: bool, - walk_left: bool, - walk_right: bool, - yaw: f32, - pitch: f32, -} - -impl_component_provider!(Player); - -impl TypeUuidProvider for Player { - fn type_uuid() -> Uuid { - unimplemented!(); - } -} - -impl ScriptTrait for Player { - fn on_update(&mut self, ctx: &mut ScriptContext) { - // Step 1. Fetch the velocity vector from the animation blending state machine. - let transform = ctx.scene.graph[*self.model].global_transform(); - let mut velocity = Vector3::default(); - if let Some(state_machine) = ctx - .scene - .graph - .try_get(*self.state_machine) - .and_then(|node| node.query_component_ref::<AnimationBlendingStateMachine>()) - { - if let Some(root_motion) = state_machine.machine().pose().root_motion() { - velocity = transform - .transform_vector(&root_motion.delta_position) - .scale(1.0 / ctx.dt); - } - } +
Logic
.set_parameter("Running", Parameter::Rule(moving)); } } - - fn id(&self) -> Uuid { - Self::type_uuid() - } -} }That's a big chunk of code, but it mostly consists of a set of separate steps. Let's try to understand what each step does.
Step 1 extracts the root motion vector from the animation blending state machine: at first, we're getting the current diff --git a/searchindex.js b/searchindex.js index 2a47c15c..c10fdf81 100644 --- a/searchindex.js +++ b/searchindex.js @@ -1 +1 @@ -Object.assign(window.search, {"doc_urls":["introduction.html#fyrox-game-engine-book","introduction.html#engine-version","introduction.html#how-to-read-the-book","introduction.html#api-documentation","introduction.html#required-knowledge","introduction.html#support-the-development","introduction/index.html#introduction","introduction/introduction.html#introduction-to-fyrox","introduction/introduction.html#what-can-the-engine-do","introduction/introduction.html#how-does-the-engine-work","introduction/introduction.html#programming-languages","introduction/introduction.html#engine-features","introduction/introduction.html#general","introduction/introduction.html#rendering","introduction/introduction.html#scene","introduction/introduction.html#sound","introduction/introduction.html#serialization","introduction/introduction.html#animation","introduction/introduction.html#asset-management","introduction/introduction.html#artificial-intelligence-ai","introduction/introduction.html#user-interface-ui","introduction/introduction.html#physics","introduction/requirements.html#system-requirements","introduction/requirements.html#supported-platforms","introduction/basic_concepts.html#basic-concepts","introduction/basic_concepts.html#classic-oop","introduction/basic_concepts.html#scenes","introduction/basic_concepts.html#nodes-and-scene-graph","introduction/basic_concepts.html#plugins","introduction/basic_concepts.html#scripts","introduction/philosophy_and_goals.html#design-philosophy-and-goals","introduction/philosophy_and_goals.html#safety","introduction/philosophy_and_goals.html#performance","introduction/philosophy_and_goals.html#ease-of-use","introduction/philosophy_and_goals.html#battle-tested","introduction/faq.html#frequently-asked-questions","introduction/faq.html#which-graphics-api-does-the-engine-use","introduction/faq.html#why-not-use-alternatives-now","introduction/faq.html#is-the-engine-based-on-ecs","introduction/faq.html#what-kinds-of-games-can-i-make-using-fyrox","beginning/getting_started.html#getting-started","beginning/scripting.html#editor-plugins-and-scripts","beginning/scripting.html#quick-start","beginning/scripting.html#platform-specific-dependencies","beginning/scripting.html#linux","beginning/scripting.html#project-generator","beginning/scripting.html#using-the-latest-engine-version","beginning/scripting.html#automatic","beginning/scripting.html#manual","beginning/scripting.html#adding-game-logic","beginning/editor_overview.html#fyroxed-overview","beginning/editor_overview.html#windows","beginning/editor_overview.html#creating-or-loading-a-scene","beginning/editor_overview.html#populating-a-scene","beginning/editor_overview.html#saving-a-scene","beginning/editor_overview.html#undoing-and-redoing","beginning/editor_overview.html#controls","beginning/editor_overview.html#editor-camera-movement","beginning/editor_overview.html#others","beginning/editor_overview.html#play-mode","beginning/editor_overview.html#additional-utilities","beginning/scene_and_scene_graph.html#scene-and-scene-graph","beginning/scene_and_scene_graph.html#building-blocks-or-scene-nodes","beginning/scene_and_scene_graph.html#local-and-global-coordinates","beginning/assets.html#assets","beginning/assets.html#asset-types","beginning/assets.html#asset-management","beginning/assets.html#asset-instantiation","beginning/assets.html#loading-assets","beginning/data_management.html#data-management","beginning/data_management.html#motivation","beginning/data_management.html#technical-details","beginning/data_management.html#advantages","beginning/data_management.html#disadvantages","beginning/data_management.html#usage","beginning/data_management.html#borrowing","beginning/data_management.html#freeing","beginning/data_management.html#take-and-reserve","beginning/data_management.html#iterators","beginning/data_management.html#direct-access","beginning/data_management.html#validation","beginning/data_management.html#type-erased-handles","beginning/data_management.html#getting-a-handle-to-an-object-by-its-reference","beginning/data_management.html#iterate-over-and-filter-out-objects","scripting/scripting.html#scripting","scripting/plugin.html#plugins","scripting/plugin.html#structure","scripting/plugin.html#control-flow","scripting/plugin.html#plugin-context","scripting/plugin.html#editor-and-plugins","scripting/executor.html#executor","scripting/executor.html#usage","scripting/executor.html#typical-use-cases","scripting/executor.html#setting-window-title","scripting/script.html#scripts","scripting/script.html#when-to-use-scripts-and-when-not","scripting/script.html#script-structure","scripting/script.html#script-template-generator","scripting/script.html#script-registration","scripting/script.html#script-attachment","scripting/script.html#script-context","scripting/script.html#execution-order","scripting/script.html#message-passing","scripting/tasks.html#tasks","scripting/tasks.html#how-it-works","scripting/tasks.html#examples","scripting/tasks.html#performance","scene/scene.html#scene","scene/scene.html#how-to-create","scene/scene.html#using-fyroxed","scene/scene.html#create-scene-manually","scene/scene.html#where-all-my-scenes-located","scene/scene.html#building-scene-asynchronously","scene/scene.html#managing-multiple-scenes","scene/scene.html#ambient-lighting","scene/graph.html#graph","scene/graph.html#how-to-create","scene/graph.html#adding-nodes","scene/graph.html#using-node-builders","scene/graph.html#adding-a-node-manually","scene/graph.html#how-to-modify-the-hierarchy","scene/graph.html#how-to-remove-nodes","scene/transform.html#transformation","scene/prefab.html#prefabs","scene/prefab.html#how-to-create-and-use-a-prefab","scene/prefab.html#property-inheritance","scene/prefab.html#hierarchical-prefabs","scene/inheritance.html#property-inheritance","scene/inheritance.html#how-to-create-inheritable-properties","scene/inheritance.html#which-fields-should-be-inheritable","scene/inheritance.html#editor","scene/base_node.html#base-node","scene/base_node.html#how-to-create","scene/base_node.html#building-a-complex-hierarchy","scene/base_node.html#transform","scene/base_node.html#visibility","scene/base_node.html#enablingdisabling-scene-nodes","scene/mesh_node.html#mesh-node","scene/mesh_node.html#surfaces","scene/mesh_node.html#how-to-create","scene/mesh_node.html#using-a-3d-modelling-software","scene/mesh_node.html#creating-a-procedural-mesh","scene/mesh_node.html#animation","scene/mesh_node.html#data-buffers","scene/light_node.html#light-node","scene/light_node.html#light-types","scene/light_node.html#directional-light","scene/light_node.html#point-light","scene/light_node.html#spotlight","scene/light_node.html#light-scattering","scene/light_node.html#shadows","scene/light_node.html#performance","scene/sprite_node.html#sprite","scene/sprite_node.html#how-to-create","scene/sprite_node.html#general-rules","scene/sprite_node.html#limitations","scene/particle_system_node.html#particle-system","scene/particle_system_node.html#basic-concepts","scene/particle_system_node.html#particle","scene/particle_system_node.html#emitters","scene/particle_system_node.html#how-to-create","scene/particle_system_node.html#using-the-editor","scene/particle_system_node.html#using-the-code","scene/particle_system_node.html#using-prefabs","scene/particle_system_node.html#soft-particles","scene/particle_system_node.html#restarting-emission","scene/particle_system_node.html#enabling-or-disabling-particle-systems","scene/particle_system_node.html#performance","scene/particle_system_node.html#limitations","scene/terrain_node.html#terrain","scene/terrain_node.html#basic-concepts","scene/terrain_node.html#heightmap","scene/terrain_node.html#layers","scene/terrain_node.html#creating-terrain-in-the-editor","scene/terrain_node.html#creating-terrain-from-code","scene/terrain_node.html#physics","scene/terrain_node.html#performance","scene/terrain_node.html#chunking","scene/terrain_node.html#level-of-detail","scene/terrain_node.html#limitations-and-known-issues","scene/camera_node.html#camera-node","scene/camera_node.html#how-to-create","scene/camera_node.html#projection-modes","scene/camera_node.html#perspective","scene/camera_node.html#orthographic","scene/camera_node.html#performance","scene/camera_node.html#skybox","scene/camera_node.html#color-grading-look-up-tables","scene/camera_node.html#picking","scene/camera_node.html#advanced-picking","scene/camera_node.html#exposure-and-hdr","scene/decal_node.html#decal-node","scene/decal_node.html#how-to-create","scene/decal_node.html#textures","scene/decal_node.html#rendering","scene/decal_node.html#bounds","scene/decal_node.html#layers","scene/decal_node.html#performance","scene/rectangle.html#rectangle-node","scene/rectangle.html#how-to-create","scene/rectangle.html#specifying-image-portion-for-rendering","scene/rectangle.html#performance","scene/rectangle.html#limitations","scene/custom_node.html#custom-scene-node","scene/custom_node.html#limitations","scene/custom_node.html#editor-support","physics/physics.html#physics","physics/physics.html#differences-between-3d-and-2d","physics/rigid_body.html#rigid-body-node","physics/rigid_body.html#how-to-create","physics/rigid_body.html#colliders","physics/rigid_body.html#force-and-torque","physics/rigid_body.html#kinematic-rigid-bodies","physics/rigid_body.html#continuous-collision-detection","physics/rigid_body.html#dominance","physics/rigid_body.html#2d-rigid-bodies","physics/collider.html#collider-node","physics/collider.html#shapes","physics/collider.html#how-to-create","physics/collider.html#collision-filtering","physics/collider.html#using-colliders-for-hit-boxes","physics/joint.html#joint","physics/joint.html#bodies-binding","physics/joint.html#how-to-create","physics/joint.html#limits","physics/joint.html#usage","physics/ray.html#ray-casting","physics/ray.html#avoiding-unnecessary-allocations","physics/ragdoll.html#ragdoll","physics/ragdoll.html#how-to-create","physics/ragdoll.html#video-tutorials","sound/index.html#sound-system","sound/bus.html#audio-bus","sound/bus.html#graph","sound/bus.html#effects","sound/bus.html#editor","sound/sound.html#sound","sound/sound.html#how-to-create","sound/sound.html#from-editor","sound/sound.html#from-code","sound/sound.html#2d-and-3d","sound/sound.html#audio-bus","sound/hrtf.html#head-related-transfer-function","sound/hrtf.html#hrtf-on-practice","sound/hrtf.html#performance","animation/animation.html#animation","animation/animation.html#web-demo","animation/animation.html#track-binding","animation/animation.html#time-slice-and-looping","animation/animation.html#speed","animation/animation.html#enabling-or-disabling-animations","animation/animation.html#signals","animation/animation.html#creating-from-code","animation/animation.html#importing","animation/animation.html#from-editor","animation/animation.html#from-code","animation/animation.html#playing-an-animation","animation/anim_editor.html#animation-editor","animation/anim_editor.html#typical-workflow","animation/anim_editor.html#toolbar","animation/anim_editor.html#track-list","animation/anim_editor.html#track-context-menu","animation/anim_editor.html#curve-editor","animation/anim_editor.html#time-ruler-context-menu","animation/anim_editor.html#key-frame-context-menu","animation/anim_editor.html#property-binding","animation/anim_editor.html#animation-importing","animation/anim_editor.html#preview-mode","animation/anim_editor.html#root-motion","animation/anim_editor.html#limitations","animation/blending.html#animation-blending","animation/blending.html#how-to-create","animation/blending.html#from-editor","animation/blending.html#from-code","animation/absm_editor.html#animation-blending-state-machine-absm-editor","animation/absm_editor.html#toolbar","animation/absm_editor.html#parameters","animation/absm_editor.html#state-graph","animation/absm_editor.html#state-context-menu","animation/absm_editor.html#transition-context-menu","animation/absm_editor.html#state-properties","animation/absm_editor.html#transition-properties","animation/absm_editor.html#state-viewer","animation/absm_editor.html#play-animation-properties","animation/absm_editor.html#blend-animations-properties","animation/absm_editor.html#blend-animations-by-index-properties","animation/absm_editor.html#connection-context-menu","animation/absm_editor.html#node-context-menu","animation/absm_editor.html#layer-mask","animation/absm_editor.html#preview-mode","animation/signal.html#signals","animation/signal.html#how-to-add","animation/signal.html#from-animation-editor","animation/signal.html#from-code","animation/signal.html#reacting-to-signal-events","animation/signal.html#events-from-absm","animation/root_motion/root_motion.html#root-motion","animation/root_motion/root_motion.html#how-to-enable","animation/root_motion/root_motion.html#how-to-use","animation/root_motion/root_motion.html#raw-root-motion-values","animation/root_motion/root_motion.html#combining-root-motion-with-procedural-motion","input/input.html#input","input/keyboard.html#keyboard-input-wip","input/mouse.html#mouse-input-wip","input/text.html#raw-text-input-wip","ai/ai.html#artificial-intelligence-wip","ai/beh_tree.html#behaviour-trees-wip","ai/pathfinding.html#path-finding","ai/pathfinding.html#examples","ai/pathfinding.html#what-to-use","ai/pathfinding.html#performance","ai/navmesh.html#navigational-meshes","ai/navmesh.html#how-to-create","ai/navmesh.html#using-the-editor","ai/navmesh.html#using-automatic-generation","ai/navmesh.html#using-external-data","ai/navmesh.html#agents","ai/navmesh.html#radius","rendering/rendering.html#rendering-wip","rendering/shaders.html#shaders","rendering/shaders.html#shaders-language","rendering/shaders.html#structure","rendering/shaders.html#properties","rendering/shaders.html#built-in-properties","rendering/shaders.html#predefined-render-passes","rendering/shaders.html#drawing-parameters","rendering/shaders.html#vertex-shader","rendering/shaders.html#pixel-shader","rendering/materials.html#materials","rendering/materials.html#performance","rendering/materials.html#standard-material","rendering/materials.html#transparency","rendering/materials.html#material-import","rendering/materials.html#blender","rendering/materials.html#3ds-max","rendering/lightmaps.html#light-maps","rendering/lightmaps.html#how-to-generate","rendering/lightmaps.html#from-editor","rendering/lightmaps.html#from-code","rendering/lightmaps.html#using-the-lightmap","rendering/lightmaps.html#limitations","rendering/settings.html#settings","rendering/settings.html#presets","rendering/settings.html#how-to-apply","rendering/render_pass.html#render-pass","rendering/render_pass.html#creating-a-render-pass","rendering/render_pass.html#registering-a-render-pass","rendering/normal_maps.html#normal-maps","rendering/normal_maps.html#format","rendering/normal_maps.html#solving-issues","resources/resources.html#asset-management","resources/resources.html#general-info","resources/resources.html#best-practices","resources/resources.html#asset-browser","resources/resources.html#api-docs","resources/model.html#model-resources","resources/model.html#supported-formats","resources/model.html#instantiation","resources/model.html#material-import","resources/model.html#tips-for-blender","resources/model.html#tips-for-3ds-max","resources/texture.html#textures","resources/texture.html#supported-formats","resources/texture.html#compressed-textures","resources/texture.html#import-options","resources/texture.html#render-target","resources/sound.html#sound-wip","resources/curve.html#curve-wip","resources/custom.html#custom-resources","resources/custom.html#example","resources/custom.html#editor-support","resources/hot_reloading.html#hot-reloading-wip","ui/ui.html#user-interface","ui/ui.html#web-demo","ui/basic_concepts/basic_concepts.html#basic-concepts","ui/basic_concepts/basic_concepts.html#stateful","ui/basic_concepts/basic_concepts.html#model-view-controller","ui/basic_concepts/basic_concepts.html#node-based-architecture","ui/basic_concepts/basic_concepts.html#composition","ui/basic_concepts/basic_concepts.html#component-querying","ui/basic_concepts/basic_concepts.html#message-passing","ui/basic_concepts/basic_concepts.html#message-routing-strategies","ui/basic_concepts/basic_concepts.html#layout","ui/basic_concepts/basic_concepts.html#code-first-and-editor-first-approaches","ui/basic_concepts/basic_concepts.html#limitations","ui/editor/editor.html#ui-editor","ui/editor/editor.html#appearance","ui/editor/editor.html#introduction","ui/editor/editor.html#widgets","ui/editor/editor.html#video","ui/rendering.html#rendering","ui/rendering.html#offscreen-rendering","ui/rendering.html#embedding-scene-into-ui","ui/font.html#font","ui/font.html#create-new-font","ui/font.html#loading-font-from-file","ui/font.html#creating-font-from-memory","ui/font.html#default-font","ui/font.html#how-to-change-font-size","ui/font.html#important-notes","ui/style.html#styles","ui/widgets.html#widgets","ui/widgets.html#containers","ui/widgets.html#visual","ui/widgets.html#controls","ui/custom.html#custom-widget","ui/custom.html#custom-logic","ui/custom.html#builder","ui/custom.html#using-the-builder","ui/custom.html#reacting-to-click-messages","ui/custom.html#custom-widget-or-composition-of-widgets","ui/custom.html#source-code-and-web-demo","ui/button.html#button","ui/button.html#simple-button-with-text","ui/button.html#a-button-with-image","ui/button.html#message-handling","ui/button.html#using-a-button-to-exit-the-game","ui/border.html#border","ui/canvas.html#canvas","ui/canvas.html#how-to-create","ui/canvas.html#how-to-position-children-nodes","ui/canvas.html#tips","ui/checkbox/check_box.html#check-box","ui/checkbox/check_box.html#how-it-looks","ui/checkbox/check_box.html#how-to-create","ui/checkbox/check_box.html#message-handling","ui/checkbox/check_box.html#theme","ui/curve_editor.html#curve-editor-wip","ui/decorator.html#decorator","ui/dock.html#docking-manager-wip","ui/dropdown_list.html#dropdown-list-wip","ui/expander.html#expander-wip","ui/file_browser.html#file-browser-wip","ui/grid.html#grid","ui/image.html#image","ui/image.html#usage","ui/image.html#equal-size-to-source","ui/image.html#vertical-flip","ui/inspector.html#inspector-wip","ui/list_view.html#list-view-wip","ui/menu.html#menu-wip","ui/message_box.html#message-box-wip","ui/numeric.html#numericupdown-widget","ui/numeric.html#how-to-create","ui/numeric.html#limits","ui/numeric.html#step","ui/numeric.html#precision","ui/popup.html#popup-wip","ui/progress_bar.html#progress-bar-wip","ui/range.html#range-wip","ui/rect.html#rect-editor-wip","ui/scroll_bar.html#scroll-bar","ui/scroll_bar.html#example","ui/scroll_bar.html#orientation","ui/scroll_bar.html#show-values","ui/scroll_bar.html#step","ui/scroll_panel.html#scroll-panel","ui/scroll_panel.html#examples","ui/scroll_panel.html#scrolling","ui/scroll_panel.html#bringing-child-into-view","ui/scroll_viewer.html#scroll-viewer","ui/scroll_viewer.html#example","ui/scroll_viewer.html#scrolling-speed-and-controls","ui/scroll_viewer.html#bringing-a-child-into-view","ui/screen.html#screen","ui/screen.html#how-to-create","ui/screen.html#using-the-editor","ui/screen.html#from-code","ui/stack_panel.html#stack-panel","ui/stack_panel.html#stack-panel-orientation","ui/tab_control.html#tab-control","ui/tab_control.html#tab-header-styling","ui/text.html#text","ui/text.html#how-to-create","ui/text.html#text-alignment-and-word-wrapping","ui/text.html#background","ui/text.html#fonts-and-colors","ui/text.html#font-size","ui/text.html#shadows","ui/text.html#messages","ui/text_box.html#text-box","ui/text_box.html#how-to-create","ui/text_box.html#text-alignment-and-word-wrapping","ui/text_box.html#fonts-and-colors","ui/text_box.html#font-size","ui/text_box.html#messages","ui/text_box.html#shortcuts","ui/text_box.html#multiline-text-box","ui/text_box.html#read-only-mode","ui/text_box.html#mask-character","ui/text_box.html#text-commit-mode","ui/text_box.html#filtering","ui/text_box.html#style","ui/tree.html#tree-wip","ui/vector_image.html#vector-image","ui/vector_image.html#how-to-create","ui/vector_image.html#using-the-editor","ui/vector_image.html#from-code","ui/window.html#window","ui/window.html#initial-open-state","ui/window.html#styling-the-buttons","ui/window.html#modal-aka-forced-focus","ui/wrap_panel.html#wrap-panel","ui/wrap_panel.html#how-to-create","ui/wrap_panel.html#orientation","ui/wrap_panel.html#use-cases","serialization/index.html#serialization","serialization/index.html#usage","serialization/index.html#proc-macro-derivevisit","serialization/index.html#manual-implementation","serialization/index.html#serialization-and-deserialization","serialization/index.html#environment","serialization/index.html#limitations","serialization/save.html#saved-games","serialization/save.html#saved-game-structure","serialization/save.html#usage","serialization/save.html#additional-data","editor/index.html#editor","editor/property_editors.html#property-editors","editor/property_editors.html#adding-property-editors","editor/property_editors.html#structures","editor/property_editors.html#enumerations","editor/property_editors.html#inheritable-properties","editor/property_editors.html#collections","editor/property_editors.html#custom-property-editors","editor/settings.html#settings","editor/settings.html#selection","editor/settings.html#graphics","editor/settings.html#debugging","editor/settings.html#move-mode-settings","editor/settings.html#rotate-mode-settings","editor/settings.html#model","editor/settings.html#camera","misc/misc.html#miscellaneous","misc/log.html#logging","misc/log.html#writing-to-the-log","tutorials/tutorials.html#tutorials","tutorials/fps/intro.html#first-person-shooter-tutorial","tutorials/fps/intro.html#fyrox-and-fyroxed-version","tutorials/fps/tutorial-1/tutorial-part-1.html#fps-tutorial-part-1---character-controller","tutorials/fps/tutorial-1/tutorial-part-1.html#table-of-contents","tutorials/fps/tutorial-1/tutorial-part-1.html#introduction","tutorials/fps/tutorial-1/tutorial-part-1.html#creating-a-window","tutorials/fps/tutorial-1/tutorial-part-1.html#creating-your-first-scene","tutorials/fps/tutorial-1/tutorial-part-1.html#using-the-scene","tutorials/fps/tutorial-1/tutorial-part-1.html#character-controller","tutorials/fps/tutorial-1/tutorial-part-1.html#finishing-touch","tutorials/fps/tutorial-1/tutorial-part-1.html#conclusion","tutorials/fps/tutorial-2/tutorial-part-2.html#fps-tutorial-part-2---weapons","tutorials/fps/tutorial-2/tutorial-part-2.html#table-of-contents","tutorials/fps/tutorial-2/tutorial-part-2.html#introduction","tutorials/fps/tutorial-2/tutorial-part-2.html#adding-weapons","tutorials/fps/tutorial-2/tutorial-part-2.html#game-architecture","tutorials/fps/tutorial-2/tutorial-part-2.html#recoil","tutorials/fps/tutorial-2/tutorial-part-2.html#impact-effects","tutorials/fps/tutorial-2/tutorial-part-2.html#conclusion","tutorials/fps/tutorial-3/tutorial-part-3.html#fps-tutorial-part-1---bots-and-ai","tutorials/fps/tutorial-3/tutorial-part-3.html#table-of-contents","tutorials/fps/tutorial-3/tutorial-part-3.html#introduction","tutorials/fps/tutorial-3/tutorial-part-3.html#bots","tutorials/fps/tutorial-3/tutorial-part-3.html#animations","tutorials/fps/tutorial-3/tutorial-part-3.html#simple-ai","tutorials/fps/tutorial-3/tutorial-part-3.html#conclusion","tutorials/rpg/intro.html#role-playing-game-tutorial","tutorials/rpg/intro.html#fyrox-and-fyroxed-version","tutorials/rpg/tutorial-1/tutorial-part-1.html#rpg-tutorial-part-1---character-controller","tutorials/rpg/tutorial-1/tutorial-part-1.html#table-of-contents","tutorials/rpg/tutorial-1/tutorial-part-1.html#introduction","tutorials/rpg/tutorial-1/tutorial-part-1.html#assets","tutorials/rpg/tutorial-1/tutorial-part-1.html#player-prefab","tutorials/rpg/tutorial-1/tutorial-part-1.html#camera","tutorials/rpg/tutorial-1/tutorial-part-1.html#animations","tutorials/rpg/tutorial-1/tutorial-part-1.html#player-script","tutorials/rpg/tutorial-1/tutorial-part-1.html#event-handling","tutorials/rpg/tutorial-1/tutorial-part-1.html#logic","tutorials/rpg/tutorial-1/tutorial-part-1.html#binding","tutorials/rpg/tutorial-1/tutorial-part-1.html#game-level","tutorials/rpg/tutorial-1/tutorial-part-1.html#conclusion","tutorials/platformer/part1.html#2d-platformer-tutorial","tutorials/platformer/part1.html#table-of-contents","tutorials/platformer/part1.html#introduction","tutorials/platformer/part1.html#project","tutorials/platformer/part1.html#using-the-editor","tutorials/platformer/part1.html#scripts---player","tutorials/platformer/part1.html#animation","tutorials/platformer/part1.html#conclusion","performance/index.html#performance","performance/index.html#ecs","performance/index.html#architecture","obsolete/obsolete.html#obsolete-features","obsolete/custom_game_loop.html#custom-game-loop"],"index":{"documentStore":{"docInfo":{"0":{"body":32,"breadcrumbs":5,"title":4},"1":{"body":22,"breadcrumbs":3,"title":2},"10":{"body":21,"breadcrumbs":5,"title":2},"100":{"body":186,"breadcrumbs":4,"title":2},"101":{"body":94,"breadcrumbs":4,"title":2},"102":{"body":285,"breadcrumbs":4,"title":2},"103":{"body":44,"breadcrumbs":3,"title":1},"104":{"body":151,"breadcrumbs":3,"title":1},"105":{"body":314,"breadcrumbs":3,"title":1},"106":{"body":121,"breadcrumbs":3,"title":1},"107":{"body":33,"breadcrumbs":2,"title":1},"108":{"body":23,"breadcrumbs":2,"title":1},"109":{"body":89,"breadcrumbs":3,"title":2},"11":{"body":7,"breadcrumbs":5,"title":2},"110":{"body":37,"breadcrumbs":4,"title":3},"111":{"body":60,"breadcrumbs":3,"title":2},"112":{"body":59,"breadcrumbs":4,"title":3},"113":{"body":48,"breadcrumbs":4,"title":3},"114":{"body":58,"breadcrumbs":3,"title":2},"115":{"body":19,"breadcrumbs":3,"title":1},"116":{"body":12,"breadcrumbs":3,"title":1},"117":{"body":11,"breadcrumbs":4,"title":2},"118":{"body":348,"breadcrumbs":5,"title":3},"119":{"body":41,"breadcrumbs":5,"title":3},"12":{"body":33,"breadcrumbs":4,"title":1},"120":{"body":72,"breadcrumbs":4,"title":2},"121":{"body":65,"breadcrumbs":4,"title":2},"122":{"body":79,"breadcrumbs":3,"title":1},"123":{"body":178,"breadcrumbs":3,"title":1},"124":{"body":24,"breadcrumbs":5,"title":3},"125":{"body":35,"breadcrumbs":4,"title":2},"126":{"body":29,"breadcrumbs":4,"title":2},"127":{"body":66,"breadcrumbs":5,"title":2},"128":{"body":93,"breadcrumbs":6,"title":3},"129":{"body":120,"breadcrumbs":5,"title":2},"13":{"body":83,"breadcrumbs":4,"title":1},"130":{"body":45,"breadcrumbs":4,"title":1},"131":{"body":39,"breadcrumbs":5,"title":2},"132":{"body":28,"breadcrumbs":4,"title":1},"133":{"body":101,"breadcrumbs":6,"title":3},"134":{"body":69,"breadcrumbs":4,"title":1},"135":{"body":82,"breadcrumbs":4,"title":1},"136":{"body":55,"breadcrumbs":6,"title":3},"137":{"body":22,"breadcrumbs":5,"title":2},"138":{"body":29,"breadcrumbs":4,"title":1},"139":{"body":29,"breadcrumbs":4,"title":1},"14":{"body":38,"breadcrumbs":4,"title":1},"140":{"body":85,"breadcrumbs":7,"title":4},"141":{"body":116,"breadcrumbs":6,"title":3},"142":{"body":14,"breadcrumbs":4,"title":1},"143":{"body":80,"breadcrumbs":5,"title":2},"144":{"body":9,"breadcrumbs":5,"title":2},"145":{"body":9,"breadcrumbs":5,"title":2},"146":{"body":106,"breadcrumbs":5,"title":2},"147":{"body":36,"breadcrumbs":5,"title":2},"148":{"body":36,"breadcrumbs":4,"title":1},"149":{"body":98,"breadcrumbs":5,"title":2},"15":{"body":29,"breadcrumbs":4,"title":1},"150":{"body":86,"breadcrumbs":4,"title":1},"151":{"body":39,"breadcrumbs":4,"title":1},"152":{"body":45,"breadcrumbs":4,"title":1},"153":{"body":59,"breadcrumbs":4,"title":1},"154":{"body":51,"breadcrumbs":5,"title":2},"155":{"body":22,"breadcrumbs":4,"title":1},"156":{"body":37,"breadcrumbs":6,"title":2},"157":{"body":30,"breadcrumbs":6,"title":2},"158":{"body":87,"breadcrumbs":5,"title":1},"159":{"body":206,"breadcrumbs":5,"title":1},"16":{"body":9,"breadcrumbs":4,"title":1},"160":{"body":11,"breadcrumbs":5,"title":1},"161":{"body":72,"breadcrumbs":6,"title":2},"162":{"body":117,"breadcrumbs":6,"title":2},"163":{"body":16,"breadcrumbs":6,"title":2},"164":{"body":46,"breadcrumbs":6,"title":2},"165":{"body":14,"breadcrumbs":6,"title":2},"166":{"body":32,"breadcrumbs":8,"title":4},"167":{"body":26,"breadcrumbs":5,"title":1},"168":{"body":74,"breadcrumbs":5,"title":1},"169":{"body":31,"breadcrumbs":4,"title":1},"17":{"body":16,"breadcrumbs":4,"title":1},"170":{"body":15,"breadcrumbs":5,"title":2},"171":{"body":38,"breadcrumbs":4,"title":1},"172":{"body":71,"breadcrumbs":4,"title":1},"173":{"body":95,"breadcrumbs":6,"title":3},"174":{"body":191,"breadcrumbs":6,"title":3},"175":{"body":57,"breadcrumbs":4,"title":1},"176":{"body":46,"breadcrumbs":4,"title":1},"177":{"body":82,"breadcrumbs":4,"title":1},"178":{"body":92,"breadcrumbs":5,"title":2},"179":{"body":20,"breadcrumbs":6,"title":3},"18":{"body":26,"breadcrumbs":5,"title":2},"180":{"body":22,"breadcrumbs":5,"title":2},"181":{"body":33,"breadcrumbs":4,"title":1},"182":{"body":10,"breadcrumbs":5,"title":2},"183":{"body":79,"breadcrumbs":4,"title":1},"184":{"body":85,"breadcrumbs":4,"title":1},"185":{"body":67,"breadcrumbs":4,"title":1},"186":{"body":127,"breadcrumbs":4,"title":1},"187":{"body":99,"breadcrumbs":8,"title":5},"188":{"body":52,"breadcrumbs":4,"title":1},"189":{"body":166,"breadcrumbs":5,"title":2},"19":{"body":4,"breadcrumbs":6,"title":3},"190":{"body":1,"breadcrumbs":5,"title":2},"191":{"body":35,"breadcrumbs":5,"title":2},"192":{"body":29,"breadcrumbs":4,"title":1},"193":{"body":9,"breadcrumbs":4,"title":1},"194":{"body":28,"breadcrumbs":4,"title":1},"195":{"body":80,"breadcrumbs":4,"title":1},"196":{"body":30,"breadcrumbs":4,"title":1},"197":{"body":15,"breadcrumbs":4,"title":1},"198":{"body":32,"breadcrumbs":5,"title":2},"199":{"body":43,"breadcrumbs":4,"title":1},"2":{"body":36,"breadcrumbs":3,"title":2},"20":{"body":113,"breadcrumbs":6,"title":3},"200":{"body":91,"breadcrumbs":7,"title":4},"201":{"body":15,"breadcrumbs":4,"title":1},"202":{"body":157,"breadcrumbs":4,"title":1},"203":{"body":116,"breadcrumbs":6,"title":3},"204":{"body":42,"breadcrumbs":4,"title":1},"205":{"body":12,"breadcrumbs":5,"title":2},"206":{"body":89,"breadcrumbs":3,"title":1},"207":{"body":50,"breadcrumbs":6,"title":4},"208":{"body":27,"breadcrumbs":7,"title":3},"209":{"body":40,"breadcrumbs":5,"title":1},"21":{"body":20,"breadcrumbs":4,"title":1},"210":{"body":111,"breadcrumbs":5,"title":1},"211":{"body":59,"breadcrumbs":6,"title":2},"212":{"body":50,"breadcrumbs":7,"title":3},"213":{"body":28,"breadcrumbs":7,"title":3},"214":{"body":48,"breadcrumbs":5,"title":1},"215":{"body":13,"breadcrumbs":7,"title":3},"216":{"body":40,"breadcrumbs":5,"title":2},"217":{"body":127,"breadcrumbs":4,"title":1},"218":{"body":53,"breadcrumbs":4,"title":1},"219":{"body":62,"breadcrumbs":5,"title":2},"22":{"body":62,"breadcrumbs":7,"title":2},"220":{"body":77,"breadcrumbs":7,"title":4},"221":{"body":106,"breadcrumbs":4,"title":1},"222":{"body":30,"breadcrumbs":5,"title":2},"223":{"body":87,"breadcrumbs":4,"title":1},"224":{"body":58,"breadcrumbs":4,"title":1},"225":{"body":43,"breadcrumbs":4,"title":1},"226":{"body":185,"breadcrumbs":6,"title":2},"227":{"body":94,"breadcrumbs":7,"title":3},"228":{"body":35,"breadcrumbs":4,"title":1},"229":{"body":208,"breadcrumbs":4,"title":1},"23":{"body":33,"breadcrumbs":7,"title":2},"230":{"body":10,"breadcrumbs":5,"title":2},"231":{"body":28,"breadcrumbs":4,"title":2},"232":{"body":43,"breadcrumbs":6,"title":2},"233":{"body":86,"breadcrumbs":5,"title":1},"234":{"body":98,"breadcrumbs":5,"title":1},"235":{"body":58,"breadcrumbs":5,"title":1},"236":{"body":8,"breadcrumbs":5,"title":1},"237":{"body":8,"breadcrumbs":5,"title":1},"238":{"body":245,"breadcrumbs":5,"title":1},"239":{"body":178,"breadcrumbs":5,"title":1},"24":{"body":14,"breadcrumbs":5,"title":2},"240":{"body":61,"breadcrumbs":6,"title":2},"241":{"body":30,"breadcrumbs":6,"title":2},"242":{"body":137,"breadcrumbs":7,"title":4},"243":{"body":54,"breadcrumbs":5,"title":2},"244":{"body":30,"breadcrumbs":4,"title":1},"245":{"body":200,"breadcrumbs":3,"title":1},"246":{"body":15,"breadcrumbs":4,"title":2},"247":{"body":33,"breadcrumbs":4,"title":2},"248":{"body":28,"breadcrumbs":5,"title":3},"249":{"body":27,"breadcrumbs":3,"title":1},"25":{"body":15,"breadcrumbs":5,"title":2},"250":{"body":13,"breadcrumbs":5,"title":3},"251":{"body":66,"breadcrumbs":3,"title":1},"252":{"body":164,"breadcrumbs":4,"title":2},"253":{"body":19,"breadcrumbs":3,"title":1},"254":{"body":74,"breadcrumbs":3,"title":1},"255":{"body":127,"breadcrumbs":3,"title":1},"256":{"body":95,"breadcrumbs":4,"title":2},"257":{"body":78,"breadcrumbs":6,"title":2},"258":{"body":41,"breadcrumbs":6,"title":2},"259":{"body":176,"breadcrumbs":5,"title":1},"26":{"body":38,"breadcrumbs":4,"title":1},"260":{"body":83,"breadcrumbs":6,"title":2},"261":{"body":15,"breadcrumbs":7,"title":3},"262":{"body":80,"breadcrumbs":6,"title":2},"263":{"body":21,"breadcrumbs":8,"title":4},"264":{"body":76,"breadcrumbs":8,"title":4},"265":{"body":38,"breadcrumbs":6,"title":2},"266":{"body":83,"breadcrumbs":6,"title":2},"267":{"body":29,"breadcrumbs":6,"title":2},"268":{"body":6,"breadcrumbs":6,"title":2},"269":{"body":25,"breadcrumbs":5,"title":1},"27":{"body":138,"breadcrumbs":6,"title":3},"270":{"body":270,"breadcrumbs":6,"title":2},"271":{"body":11,"breadcrumbs":5,"title":1},"272":{"body":8,"breadcrumbs":5,"title":1},"273":{"body":156,"breadcrumbs":5,"title":1},"274":{"body":161,"breadcrumbs":10,"title":6},"275":{"body":94,"breadcrumbs":5,"title":1},"276":{"body":79,"breadcrumbs":5,"title":1},"277":{"body":40,"breadcrumbs":6,"title":2},"278":{"body":23,"breadcrumbs":7,"title":3},"279":{"body":8,"breadcrumbs":7,"title":3},"28":{"body":15,"breadcrumbs":4,"title":1},"280":{"body":22,"breadcrumbs":6,"title":2},"281":{"body":59,"breadcrumbs":6,"title":2},"282":{"body":100,"breadcrumbs":6,"title":2},"283":{"body":18,"breadcrumbs":7,"title":3},"284":{"body":38,"breadcrumbs":7,"title":3},"285":{"body":48,"breadcrumbs":8,"title":4},"286":{"body":17,"breadcrumbs":7,"title":3},"287":{"body":23,"breadcrumbs":7,"title":3},"288":{"body":41,"breadcrumbs":6,"title":2},"289":{"body":51,"breadcrumbs":6,"title":2},"29":{"body":15,"breadcrumbs":4,"title":1},"290":{"body":47,"breadcrumbs":4,"title":1},"291":{"body":10,"breadcrumbs":4,"title":1},"292":{"body":51,"breadcrumbs":5,"title":2},"293":{"body":76,"breadcrumbs":4,"title":1},"294":{"body":97,"breadcrumbs":6,"title":3},"295":{"body":126,"breadcrumbs":5,"title":2},"296":{"body":117,"breadcrumbs":6,"title":2},"297":{"body":146,"breadcrumbs":5,"title":1},"298":{"body":150,"breadcrumbs":5,"title":1},"299":{"body":15,"breadcrumbs":8,"title":4},"3":{"body":11,"breadcrumbs":3,"title":2},"30":{"body":97,"breadcrumbs":7,"title":3},"300":{"body":24,"breadcrumbs":9,"title":5},"301":{"body":13,"breadcrumbs":4,"title":1},"302":{"body":0,"breadcrumbs":8,"title":3},"303":{"body":0,"breadcrumbs":8,"title":3},"304":{"body":0,"breadcrumbs":11,"title":4},"305":{"body":0,"breadcrumbs":6,"title":3},"306":{"body":0,"breadcrumbs":9,"title":3},"307":{"body":21,"breadcrumbs":7,"title":2},"308":{"body":105,"breadcrumbs":6,"title":1},"309":{"body":37,"breadcrumbs":6,"title":1},"31":{"body":62,"breadcrumbs":5,"title":1},"310":{"body":17,"breadcrumbs":6,"title":1},"311":{"body":49,"breadcrumbs":7,"title":2},"312":{"body":10,"breadcrumbs":6,"title":1},"313":{"body":54,"breadcrumbs":7,"title":2},"314":{"body":10,"breadcrumbs":8,"title":3},"315":{"body":115,"breadcrumbs":8,"title":3},"316":{"body":154,"breadcrumbs":6,"title":1},"317":{"body":35,"breadcrumbs":6,"title":1},"318":{"body":0,"breadcrumbs":3,"title":2},"319":{"body":50,"breadcrumbs":3,"title":1},"32":{"body":31,"breadcrumbs":5,"title":1},"320":{"body":63,"breadcrumbs":4,"title":2},"321":{"body":249,"breadcrumbs":3,"title":1},"322":{"body":37,"breadcrumbs":3,"title":1},"323":{"body":100,"breadcrumbs":4,"title":2},"324":{"body":83,"breadcrumbs":5,"title":3},"325":{"body":105,"breadcrumbs":4,"title":2},"326":{"body":38,"breadcrumbs":4,"title":2},"327":{"body":40,"breadcrumbs":4,"title":2},"328":{"body":102,"breadcrumbs":3,"title":1},"329":{"body":37,"breadcrumbs":3,"title":1},"33":{"body":34,"breadcrumbs":6,"title":2},"330":{"body":263,"breadcrumbs":4,"title":2},"331":{"body":61,"breadcrumbs":3,"title":1},"332":{"body":50,"breadcrumbs":4,"title":2},"333":{"body":16,"breadcrumbs":3,"title":1},"334":{"body":10,"breadcrumbs":4,"title":2},"335":{"body":99,"breadcrumbs":5,"title":2},"336":{"body":24,"breadcrumbs":4,"title":1},"337":{"body":171,"breadcrumbs":4,"title":1},"338":{"body":102,"breadcrumbs":4,"title":1},"339":{"body":42,"breadcrumbs":5,"title":2},"34":{"body":18,"breadcrumbs":6,"title":2},"340":{"body":32,"breadcrumbs":4,"title":1},"341":{"body":354,"breadcrumbs":3,"title":1},"342":{"body":18,"breadcrumbs":3,"title":1},"343":{"body":85,"breadcrumbs":3,"title":1},"344":{"body":33,"breadcrumbs":5,"title":2},"345":{"body":170,"breadcrumbs":6,"title":3},"346":{"body":57,"breadcrumbs":6,"title":3},"347":{"body":13,"breadcrumbs":5,"title":2},"348":{"body":92,"breadcrumbs":4,"title":1},"349":{"body":91,"breadcrumbs":5,"title":2},"35":{"body":6,"breadcrumbs":7,"title":3},"350":{"body":13,"breadcrumbs":4,"title":2},"351":{"body":13,"breadcrumbs":4,"title":2},"352":{"body":141,"breadcrumbs":4,"title":2},"353":{"body":103,"breadcrumbs":4,"title":2},"354":{"body":5,"breadcrumbs":4,"title":2},"355":{"body":0,"breadcrumbs":5,"title":2},"356":{"body":25,"breadcrumbs":5,"title":2},"357":{"body":52,"breadcrumbs":4,"title":1},"358":{"body":126,"breadcrumbs":5,"title":2},"359":{"body":25,"breadcrumbs":5,"title":2},"36":{"body":42,"breadcrumbs":8,"title":4},"360":{"body":24,"breadcrumbs":6,"title":3},"361":{"body":25,"breadcrumbs":4,"title":1},"362":{"body":20,"breadcrumbs":5,"title":2},"363":{"body":114,"breadcrumbs":5,"title":2},"364":{"body":77,"breadcrumbs":5,"title":2},"365":{"body":27,"breadcrumbs":5,"title":2},"366":{"body":0,"breadcrumbs":7,"title":2},"367":{"body":0,"breadcrumbs":6,"title":2},"368":{"body":114,"breadcrumbs":6,"title":2},"369":{"body":290,"breadcrumbs":5,"title":1},"37":{"body":24,"breadcrumbs":7,"title":3},"370":{"body":65,"breadcrumbs":6,"title":2},"371":{"body":0,"breadcrumbs":8,"title":3},"372":{"body":32,"breadcrumbs":4,"title":2},"373":{"body":15,"breadcrumbs":4,"title":2},"374":{"body":9,"breadcrumbs":6,"title":2},"375":{"body":75,"breadcrumbs":5,"title":1},"376":{"body":47,"breadcrumbs":7,"title":3},"377":{"body":80,"breadcrumbs":7,"title":3},"378":{"body":34,"breadcrumbs":5,"title":1},"379":{"body":89,"breadcrumbs":6,"title":2},"38":{"body":29,"breadcrumbs":7,"title":3},"380":{"body":137,"breadcrumbs":6,"title":2},"381":{"body":113,"breadcrumbs":7,"title":3},"382":{"body":92,"breadcrumbs":5,"title":1},"383":{"body":267,"breadcrumbs":9,"title":5},"384":{"body":58,"breadcrumbs":5,"title":1},"385":{"body":36,"breadcrumbs":5,"title":2},"386":{"body":33,"breadcrumbs":4,"title":1},"387":{"body":110,"breadcrumbs":4,"title":1},"388":{"body":20,"breadcrumbs":4,"title":1},"389":{"body":9,"breadcrumbs":4,"title":1},"39":{"body":19,"breadcrumbs":9,"title":5},"390":{"body":37,"breadcrumbs":4,"title":1},"391":{"body":333,"breadcrumbs":5,"title":2},"392":{"body":138,"breadcrumbs":6,"title":3},"393":{"body":18,"breadcrumbs":4,"title":1},"394":{"body":11,"breadcrumbs":6,"title":3},"395":{"body":21,"breadcrumbs":6,"title":3},"396":{"body":71,"breadcrumbs":6,"title":3},"397":{"body":54,"breadcrumbs":5,"title":2},"398":{"body":53,"breadcrumbs":6,"title":3},"399":{"body":68,"breadcrumbs":5,"title":2},"4":{"body":26,"breadcrumbs":3,"title":2},"40":{"body":58,"breadcrumbs":4,"title":2},"400":{"body":113,"breadcrumbs":4,"title":1},"401":{"body":22,"breadcrumbs":4,"title":1},"402":{"body":210,"breadcrumbs":4,"title":1},"403":{"body":94,"breadcrumbs":4,"title":1},"404":{"body":176,"breadcrumbs":4,"title":1},"405":{"body":173,"breadcrumbs":7,"title":2},"406":{"body":287,"breadcrumbs":7,"title":2},"407":{"body":198,"breadcrumbs":6,"title":1},"408":{"body":13,"breadcrumbs":7,"title":2},"409":{"body":52,"breadcrumbs":8,"title":3},"41":{"body":41,"breadcrumbs":8,"title":3},"410":{"body":53,"breadcrumbs":9,"title":4},"411":{"body":11,"breadcrumbs":9,"title":4},"412":{"body":1,"breadcrumbs":5,"title":1},"413":{"body":43,"breadcrumbs":7,"title":3},"414":{"body":30,"breadcrumbs":6,"title":2},"415":{"body":34,"breadcrumbs":6,"title":2},"416":{"body":50,"breadcrumbs":8,"title":4},"417":{"body":139,"breadcrumbs":5,"title":1},"418":{"body":38,"breadcrumbs":5,"title":1},"419":{"body":18,"breadcrumbs":5,"title":1},"42":{"body":33,"breadcrumbs":7,"title":2},"420":{"body":51,"breadcrumbs":7,"title":3},"421":{"body":24,"breadcrumbs":5,"title":1},"422":{"body":24,"breadcrumbs":7,"title":2},"423":{"body":8,"breadcrumbs":6,"title":1},"424":{"body":103,"breadcrumbs":6,"title":1},"425":{"body":63,"breadcrumbs":7,"title":2},"426":{"body":33,"breadcrumbs":6,"title":1},"427":{"body":2,"breadcrumbs":9,"title":3},"428":{"body":55,"breadcrumbs":6,"title":1},"429":{"body":18,"breadcrumbs":9,"title":3},"43":{"body":31,"breadcrumbs":8,"title":3},"430":{"body":23,"breadcrumbs":9,"title":3},"431":{"body":9,"breadcrumbs":7,"title":2},"432":{"body":13,"breadcrumbs":9,"title":3},"433":{"body":212,"breadcrumbs":5,"title":1},"434":{"body":22,"breadcrumbs":5,"title":1},"435":{"body":75,"breadcrumbs":5,"title":1},"436":{"body":139,"breadcrumbs":7,"title":3},"437":{"body":73,"breadcrumbs":6,"title":2},"438":{"body":11,"breadcrumbs":7,"title":2},"439":{"body":2,"breadcrumbs":9,"title":3},"44":{"body":35,"breadcrumbs":6,"title":1},"440":{"body":0,"breadcrumbs":7,"title":2},"441":{"body":2,"breadcrumbs":9,"title":3},"442":{"body":16,"breadcrumbs":7,"title":2},"443":{"body":50,"breadcrumbs":6,"title":1},"444":{"body":42,"breadcrumbs":6,"title":1},"445":{"body":44,"breadcrumbs":6,"title":1},"446":{"body":49,"breadcrumbs":6,"title":1},"447":{"body":0,"breadcrumbs":7,"title":2},"448":{"body":2,"breadcrumbs":9,"title":3},"449":{"body":2,"breadcrumbs":7,"title":2},"45":{"body":191,"breadcrumbs":7,"title":2},"450":{"body":1,"breadcrumbs":8,"title":3},"451":{"body":30,"breadcrumbs":7,"title":2},"452":{"body":81,"breadcrumbs":6,"title":1},"453":{"body":18,"breadcrumbs":6,"title":1},"454":{"body":23,"breadcrumbs":7,"title":2},"455":{"body":15,"breadcrumbs":6,"title":1},"456":{"body":41,"breadcrumbs":7,"title":2},"457":{"body":43,"breadcrumbs":6,"title":1},"458":{"body":33,"breadcrumbs":6,"title":1},"459":{"body":29,"breadcrumbs":8,"title":3},"46":{"body":25,"breadcrumbs":9,"title":4},"460":{"body":33,"breadcrumbs":7,"title":2},"461":{"body":48,"breadcrumbs":6,"title":1},"462":{"body":68,"breadcrumbs":8,"title":3},"463":{"body":32,"breadcrumbs":8,"title":3},"464":{"body":73,"breadcrumbs":5,"title":1},"465":{"body":9,"breadcrumbs":5,"title":1},"466":{"body":34,"breadcrumbs":6,"title":2},"467":{"body":117,"breadcrumbs":5,"title":1},"468":{"body":104,"breadcrumbs":7,"title":2},"469":{"body":43,"breadcrumbs":8,"title":3},"47":{"body":60,"breadcrumbs":6,"title":1},"470":{"body":156,"breadcrumbs":7,"title":2},"471":{"body":77,"breadcrumbs":8,"title":3},"472":{"body":13,"breadcrumbs":5,"title":1},"473":{"body":25,"breadcrumbs":5,"title":1},"474":{"body":96,"breadcrumbs":8,"title":4},"475":{"body":66,"breadcrumbs":5,"title":1},"476":{"body":106,"breadcrumbs":6,"title":2},"477":{"body":20,"breadcrumbs":6,"title":2},"478":{"body":72,"breadcrumbs":5,"title":1},"479":{"body":117,"breadcrumbs":5,"title":1},"48":{"body":154,"breadcrumbs":6,"title":1},"480":{"body":16,"breadcrumbs":7,"title":2},"481":{"body":25,"breadcrumbs":6,"title":1},"482":{"body":96,"breadcrumbs":9,"title":4},"483":{"body":105,"breadcrumbs":7,"title":2},"484":{"body":20,"breadcrumbs":7,"title":2},"485":{"body":99,"breadcrumbs":6,"title":1},"486":{"body":68,"breadcrumbs":6,"title":1},"487":{"body":15,"breadcrumbs":8,"title":3},"488":{"body":11,"breadcrumbs":7,"title":2},"489":{"body":19,"breadcrumbs":7,"title":2},"49":{"body":22,"breadcrumbs":8,"title":3},"490":{"body":69,"breadcrumbs":8,"title":3},"491":{"body":46,"breadcrumbs":6,"title":1},"492":{"body":13,"breadcrumbs":6,"title":1},"493":{"body":1,"breadcrumbs":7,"title":2},"494":{"body":24,"breadcrumbs":7,"title":2},"495":{"body":10,"breadcrumbs":6,"title":1},"496":{"body":31,"breadcrumbs":7,"title":2},"497":{"body":70,"breadcrumbs":6,"title":1},"498":{"body":201,"breadcrumbs":5,"title":1},"499":{"body":16,"breadcrumbs":7,"title":3},"5":{"body":10,"breadcrumbs":3,"title":2},"50":{"body":38,"breadcrumbs":6,"title":2},"500":{"body":14,"breadcrumbs":6,"title":2},"501":{"body":22,"breadcrumbs":8,"title":4},"502":{"body":21,"breadcrumbs":7,"title":2},"503":{"body":27,"breadcrumbs":6,"title":1},"504":{"body":14,"breadcrumbs":6,"title":1},"505":{"body":12,"breadcrumbs":7,"title":2},"506":{"body":62,"breadcrumbs":2,"title":1},"507":{"body":15,"breadcrumbs":2,"title":1},"508":{"body":131,"breadcrumbs":4,"title":3},"509":{"body":106,"breadcrumbs":3,"title":2},"51":{"body":143,"breadcrumbs":5,"title":1},"510":{"body":111,"breadcrumbs":3,"title":2},"511":{"body":62,"breadcrumbs":2,"title":1},"512":{"body":12,"breadcrumbs":2,"title":1},"513":{"body":24,"breadcrumbs":5,"title":2},"514":{"body":70,"breadcrumbs":6,"title":3},"515":{"body":272,"breadcrumbs":4,"title":1},"516":{"body":205,"breadcrumbs":5,"title":2},"517":{"body":56,"breadcrumbs":2,"title":1},"518":{"body":53,"breadcrumbs":5,"title":2},"519":{"body":22,"breadcrumbs":6,"title":3},"52":{"body":48,"breadcrumbs":7,"title":3},"520":{"body":53,"breadcrumbs":4,"title":1},"521":{"body":99,"breadcrumbs":4,"title":1},"522":{"body":91,"breadcrumbs":5,"title":2},"523":{"body":86,"breadcrumbs":4,"title":1},"524":{"body":9,"breadcrumbs":6,"title":3},"525":{"body":9,"breadcrumbs":3,"title":1},"526":{"body":38,"breadcrumbs":3,"title":1},"527":{"body":48,"breadcrumbs":3,"title":1},"528":{"body":57,"breadcrumbs":3,"title":1},"529":{"body":37,"breadcrumbs":5,"title":3},"53":{"body":91,"breadcrumbs":6,"title":2},"530":{"body":37,"breadcrumbs":5,"title":3},"531":{"body":29,"breadcrumbs":3,"title":1},"532":{"body":37,"breadcrumbs":3,"title":1},"533":{"body":8,"breadcrumbs":2,"title":1},"534":{"body":53,"breadcrumbs":3,"title":1},"535":{"body":38,"breadcrumbs":4,"title":2},"536":{"body":25,"breadcrumbs":2,"title":1},"537":{"body":27,"breadcrumbs":7,"title":4},"538":{"body":30,"breadcrumbs":6,"title":3},"539":{"body":13,"breadcrumbs":11,"title":6},"54":{"body":24,"breadcrumbs":6,"title":2},"540":{"body":13,"breadcrumbs":7,"title":2},"541":{"body":44,"breadcrumbs":6,"title":1},"542":{"body":787,"breadcrumbs":7,"title":2},"543":{"body":359,"breadcrumbs":8,"title":3},"544":{"body":260,"breadcrumbs":7,"title":2},"545":{"body":794,"breadcrumbs":7,"title":2},"546":{"body":171,"breadcrumbs":7,"title":2},"547":{"body":37,"breadcrumbs":6,"title":1},"548":{"body":13,"breadcrumbs":9,"title":5},"549":{"body":9,"breadcrumbs":6,"title":2},"55":{"body":21,"breadcrumbs":6,"title":2},"550":{"body":30,"breadcrumbs":5,"title":1},"551":{"body":386,"breadcrumbs":6,"title":2},"552":{"body":1137,"breadcrumbs":6,"title":2},"553":{"body":225,"breadcrumbs":5,"title":1},"554":{"body":311,"breadcrumbs":6,"title":2},"555":{"body":38,"breadcrumbs":5,"title":1},"556":{"body":13,"breadcrumbs":11,"title":6},"557":{"body":6,"breadcrumbs":7,"title":2},"558":{"body":27,"breadcrumbs":6,"title":1},"559":{"body":384,"breadcrumbs":6,"title":1},"56":{"body":12,"breadcrumbs":5,"title":1},"560":{"body":1130,"breadcrumbs":6,"title":1},"561":{"body":370,"breadcrumbs":7,"title":2},"562":{"body":30,"breadcrumbs":6,"title":1},"563":{"body":27,"breadcrumbs":7,"title":4},"564":{"body":32,"breadcrumbs":6,"title":3},"565":{"body":3,"breadcrumbs":11,"title":6},"566":{"body":14,"breadcrumbs":7,"title":2},"567":{"body":164,"breadcrumbs":6,"title":1},"568":{"body":34,"breadcrumbs":6,"title":1},"569":{"body":210,"breadcrumbs":7,"title":2},"57":{"body":24,"breadcrumbs":7,"title":3},"570":{"body":73,"breadcrumbs":6,"title":1},"571":{"body":437,"breadcrumbs":6,"title":1},"572":{"body":345,"breadcrumbs":7,"title":2},"573":{"body":149,"breadcrumbs":7,"title":2},"574":{"body":408,"breadcrumbs":6,"title":1},"575":{"body":24,"breadcrumbs":6,"title":1},"576":{"body":50,"breadcrumbs":7,"title":2},"577":{"body":38,"breadcrumbs":6,"title":1},"578":{"body":0,"breadcrumbs":7,"title":3},"579":{"body":13,"breadcrumbs":6,"title":2},"58":{"body":45,"breadcrumbs":5,"title":1},"580":{"body":37,"breadcrumbs":5,"title":1},"581":{"body":130,"breadcrumbs":5,"title":1},"582":{"body":489,"breadcrumbs":6,"title":2},"583":{"body":727,"breadcrumbs":6,"title":2},"584":{"body":176,"breadcrumbs":5,"title":1},"585":{"body":22,"breadcrumbs":5,"title":1},"586":{"body":19,"breadcrumbs":2,"title":1},"587":{"body":146,"breadcrumbs":2,"title":1},"588":{"body":277,"breadcrumbs":2,"title":1},"589":{"body":7,"breadcrumbs":3,"title":2},"59":{"body":68,"breadcrumbs":6,"title":2},"590":{"body":301,"breadcrumbs":7,"title":3},"6":{"body":27,"breadcrumbs":2,"title":1},"60":{"body":32,"breadcrumbs":6,"title":2},"61":{"body":145,"breadcrumbs":8,"title":3},"62":{"body":357,"breadcrumbs":9,"title":4},"63":{"body":95,"breadcrumbs":8,"title":3},"64":{"body":19,"breadcrumbs":4,"title":1},"65":{"body":126,"breadcrumbs":5,"title":2},"66":{"body":50,"breadcrumbs":5,"title":2},"67":{"body":94,"breadcrumbs":5,"title":2},"68":{"body":31,"breadcrumbs":5,"title":2},"69":{"body":23,"breadcrumbs":6,"title":2},"7":{"body":39,"breadcrumbs":5,"title":2},"70":{"body":183,"breadcrumbs":5,"title":1},"71":{"body":80,"breadcrumbs":6,"title":2},"72":{"body":65,"breadcrumbs":5,"title":1},"73":{"body":54,"breadcrumbs":5,"title":1},"74":{"body":83,"breadcrumbs":5,"title":1},"75":{"body":47,"breadcrumbs":5,"title":1},"76":{"body":29,"breadcrumbs":5,"title":1},"77":{"body":158,"breadcrumbs":6,"title":2},"78":{"body":54,"breadcrumbs":5,"title":1},"79":{"body":7,"breadcrumbs":6,"title":2},"8":{"body":16,"breadcrumbs":4,"title":1},"80":{"body":6,"breadcrumbs":5,"title":1},"81":{"body":39,"breadcrumbs":7,"title":3},"82":{"body":8,"breadcrumbs":8,"title":4},"83":{"body":9,"breadcrumbs":9,"title":5},"84":{"body":51,"breadcrumbs":2,"title":1},"85":{"body":108,"breadcrumbs":3,"title":1},"86":{"body":432,"breadcrumbs":3,"title":1},"87":{"body":36,"breadcrumbs":4,"title":2},"88":{"body":320,"breadcrumbs":4,"title":2},"89":{"body":90,"breadcrumbs":4,"title":2},"9":{"body":35,"breadcrumbs":5,"title":2},"90":{"body":40,"breadcrumbs":3,"title":1},"91":{"body":80,"breadcrumbs":3,"title":1},"92":{"body":6,"breadcrumbs":5,"title":3},"93":{"body":28,"breadcrumbs":5,"title":3},"94":{"body":16,"breadcrumbs":3,"title":1},"95":{"body":55,"breadcrumbs":4,"title":2},"96":{"body":205,"breadcrumbs":4,"title":2},"97":{"body":81,"breadcrumbs":5,"title":3},"98":{"body":119,"breadcrumbs":4,"title":2},"99":{"body":91,"breadcrumbs":4,"title":2}},"docs":{"0":{"body":"Practical reference and user guides for Fyrox Game Engine and its editor FyroxEd . ⚠️ Tip: If you want to start using the engine as fast as possible - read this chapter . Warning: The book is in early development stage, you can help to improve it by making a contribution in its repository . Don't be shy, every tip is helpful!","breadcrumbs":"About the Book » Fyrox Game Engine Book","id":"0","title":"Fyrox Game Engine Book"},"1":{"body":"Fyrox Team is trying to keep the book up-to-date with the latest version from master branch. If something does not compile with the latest release from crates.io, then you need to use the latest engine from GitHub repo .","breadcrumbs":"About the Book » Engine Version","id":"1","title":"Engine Version"},"10":{"body":"Everything of your game can be written entirely in Rust, utilizing its safety guarantees as well as speed. However, it is possible to use any scripting language you want, but that's have no built-in support, and you need to implement this manually.","breadcrumbs":"Introduction » Introduction to Fyrox » Programming languages","id":"10","title":"Programming languages"},"100":{"body":"Script context provides access to the environment that can be used to modify engine and game state from scripts. Typical content of the context is something like this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# engine::{ScriptMessageDispatcher},\n# plugin::Plugin, asset::manager::ResourceManager,\n# scene::{node::Node, Scene},\n# script::ScriptMessageSender\n# };\npub struct ScriptContext<'a, 'b, 'c> { pub dt: f32, pub elapsed_time: f32, pub plugins: &'a mut [Box
], pub handle: Handle , pub scene: &'b mut Scene, pub resource_manager: &'a ResourceManager, pub message_sender: &'c ScriptMessageSender, pub message_dispatcher: &'c mut ScriptMessageDispatcher,\n} dt - amount of time passed since last frame. The value of the variable is implementation-defined, usually it is something like 1/60 (0.016) of a second. elapsed_time - amount of time that passed since start of your game (in seconds). plugins - a mutable reference to all registered plugins, it allows you to access some \"global\" game data that does not belong to any object. For example, a plugin could store key mapping used for player controls, you can access it using plugins field and find desired plugin. In case of a single plugin, you just need to cast the reference to a particular type using context.plugins[0].cast:: ().unwrap() call. handle - a handle of the node to which the script is assigned to (parent node). You can borrow the node using context.scene.graph[handle] call. Typecasting can be used to obtain a reference to a particular node type. scene - a reference to parent scene of the script, it provides you full access to scene content, allowing you to add/modify/remove scene nodes. resource_manager - a reference to resource manager, you can use it to load and instantiate assets. message_sender - a message sender. Every message sent via this sender will be then passed to every ScriptTrait::on_message method of every script. message_dispatcher - a message dispatcher. If you need to receive messages of a particular type, you must subscribe to a type explicitly.","breadcrumbs":"Scripting » Scripts » Script Context","id":"100","title":"Script Context"},"101":{"body":"Scripts have strictly defined execution order for their methods (the order if execution is linear and do not depend on actual tree structure of the graph where the script is located): on_init - called first for every script instance on_start - called after every on_init is called on_update - called zero or more times per one render frame. The engine stabilizes update rate of the logic, so if your game runs at 15 FPS, the logic will still run at 60 FPS thus the on_update will be called 4 times per frame. The method can also be not called at all, if the FPS is very high. For example, if your game runs at 240 FPS, then on_update will be called once per 4 frames. on_message - called once per incoming message. on_os_event - called once per incoming OS event. on_deinit - called at the end of the update cycle once when the script (or parent node) is about to be deleted.","breadcrumbs":"Scripting » Scripts » Execution order","id":"101","title":"Execution order"},"102":{"body":"Script system of Fyrox supports message passing for scripts. Message passing is a mechanism that allows you to send some data (message) to a node, hierarchy of nodes or the entire graph. Each script can subscribe for a specific message type. It is an efficient way for decoupling scripts from each other. For instance, you may want to detect and respond to some event in your game. In this case when the event has happened, you send a message of a type and every \"subscriber\" will react to it. This way subscribers will not know anything about sender(s); they'll only use message data to do some actions. A simple example where the message passing can be useful is when you need to react to some event in your game. Imagine, that you have weapons in your game, and they can have a laser sight that flashes with a different color when some target was hit. In very naive approach you can handle all laser sights where you handle all intersection for projectiles, but this adds a very tight coupling between laser sight and projectiles. This is totally unnecessary coupling can be made loose by using message passing. Instead of handling laser sights directly, all you need to do is to broadcast an ActorDamaged { actor: Handle , attacker: Handle } message. Laser sight in its turn can subscribe for such message and handle all incoming messages and compare attacker with owner of the laser sight and if the hit was made by attacker flash with some different color. In code this would like so: # extern crate fyrox;\n# use fyrox::{\n# core::{pool::Handle, reflect::prelude::*, uuid::Uuid, visitor::prelude::*},\n# impl_component_provider,\n# scene::node::Node,\n# script::{ScriptContext, ScriptMessageContext, ScriptMessagePayload, ScriptTrait},\n# core::log::Log,\n# };\n# enum Message { Damage { actor: Handle , attacker: Handle , },\n} #[derive(Default, Clone, Reflect, Visit, Debug)]\nstruct Projectile;\n# # impl_component_provider!(Projectile); impl ScriptTrait for Projectile { fn on_update(&mut self, ctx: &mut ScriptContext) { // Broadcast the message globally. ctx.message_sender.send_global(Message::Damage { actor: Default::default(), attacker: ctx.handle, }); }\n# # fn id(&self) -> Uuid {\n# todo!()\n# }\n} #[derive(Default, Clone, Reflect, Visit, Debug)]\nstruct LaserSight;\n# # impl_component_provider!(LaserSight); impl ScriptTrait for LaserSight { fn on_start(&mut self, ctx: &mut ScriptContext) { // Subscript to messages. ctx.message_dispatcher.subscribe_to:: (ctx.handle); } fn on_message( &mut self, message: &mut dyn ScriptMessagePayload, _ctx: &mut ScriptMessageContext, ) { // React to message. if let Some(Message::Damage { actor, attacker }) = message.downcast_ref:: () { Log::info(format!(\"{actor} damaged {attacker}\",)) } }\n# # fn id(&self) -> Uuid {\n# todo!()\n# }\n} There are few key parts: You should explicitly subscribe script instance to a message type, otherwise messages of the type won't be delivered to your script. This is done using the message dispatcher: ctx.message_dispatcher.subscribe_to:: (ctx.handle);. This should be done in on_start method, however it is possible to subscribe/unsubscribe at runime. You can react to messages only in special method on_message - here you just need to check for message type using pattern matching and do something useful. Try to use message passing in all cases, loose coupling significantly improves code quality and readability, however in simple projects it can be ignored completely.","breadcrumbs":"Scripting » Scripts » Message passing","id":"102","title":"Message passing"},"103":{"body":"Fyrox supports task-based programming for both scripts and plugins. Task is a closure that does something in a separate thread and then the result of it is returned back to the main thread. This is very useful technique, that allows you to perform heavy calculations using all available CPU power, not just one CPU core with a single main thread. Tasks could be used for pretty much anything, that can be done as a separate piece of work.","breadcrumbs":"Scripting » Tasks » Tasks","id":"103","title":"Tasks"},"104":{"body":"Main thread spawns a task which is then sent to the task pool. There's a fixed set of worker threads, that extracts tasks from the task pool when there's any. Task's code is then executed in one of the worker thread, which may take any amount of time. When the task is completed, its result is sent to the main thread and then a callback closure is executed to do a desired action on task completion. Usually it's something relatively fast - for example you may spawn a task that calculates a path on a large navigational mesh and when it is done, you store that path in one of your script instance from which the task was spawned. As you can see, there are two major parts - the task itself and the closure. Graphically it can be represented like this: task Green line represents the main thread and the two purple lines are the worker threads. There could be any number of worker threads, and usually it is a worker thread per each CPU core. Let's take a look at a typical task path on this image (yellow-ish one). At first, we spawn a task, and it is immediately put in the task pool (in the same thread), after this if we have a free worker thread it extracts our task from the pool and sends it to execution. As you can see any task must implement Send trait, otherwise you'll get a compilation error . When the task is complete, the worker thread sends the result (again, the result must be Send) to the main thread and an associated callback closure is executed to do something with the result. While the task is being executed, the main thread is not blocked, and it can do other useful stuff.","breadcrumbs":"Scripting » Tasks » How it works","id":"104","title":"How it works"},"105":{"body":"The following example calculates a path on a navigational mesh in using task-based approach described above. At first, it prepares the \"environment\" for the task by cloning a shared navigational mesh (Arc >) into a local variable. Then it spawns a new task (async move { .. } block) which reads the shared navigational mesh and calculates a long path, that could take a few frames to compute (imagine a huge island, and we need to get a path from one corner to another). As the last argument to the spawn_script_task method we pass a closure that will be executed on the main thread when the task is complete. It just saves the computed path in the script's field which is then used for visualization. # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::Vector3, log::Log, pool::Handle, reflect::prelude::*, uuid::Uuid,\n# visitor::prelude::*,\n# },\n# impl_component_provider,\n# scene::{debug::Line, navmesh::NavigationalMesh, node::Node},\n# script::{ScriptContext, ScriptTrait},\n# };\n# #[derive(Visit, Default, Reflect, Debug, Clone)]\nstruct MyScript { navmesh: Handle , path: Option >>,\n} # impl_component_provider!(MyScript);\n# impl ScriptTrait for MyScript { fn on_start(&mut self, ctx: &mut ScriptContext) { // Borrow a navigational mesh scene node first. if let Some(navmesh_node) = ctx .scene .graph .try_get_of_type:: (self.navmesh) { // Take a shared reference to the internal navigational mesh. let shared_navmesh = navmesh_node.navmesh(); // Spawn a task, that will calculate a long path. ctx.task_pool.spawn_script_task( ctx.scene_handle, ctx.handle, async move { let navmesh = shared_navmesh.read(); if let Some((_, begin_index)) = navmesh.query_closest(Vector3::new(1.0, 0.0, 3.0)) { if let Some((_, end_index)) = navmesh.query_closest(Vector3::new(500.0, 0.0, 800.0)) { let mut path = Vec::new(); if navmesh .build_path(begin_index, end_index, &mut path) .is_ok() { return Some(path); } } } None }, |path, this: &mut MyScript, _ctx| { this.path = path; Log::info(\"Path is calculated!\"); }, ); } } fn on_update(&mut self, ctx: &mut ScriptContext) { // Draw the computed path. if let Some(path) = self.path.as_ref() { for segment in path.windows(2) { ctx.scene.drawing_context.add_line(Line { begin: segment[0], end: segment[1], color: Default::default(), }) } } } # # fn id(&self) -> Uuid { # todo!() # }\n} Plugins could also spawn tasks, which operates on application scale basis, unlike script tasks which operates with separate script instances. A plugin task is a bit easier to use: # extern crate fyrox;\n# use fyrox::plugin::{Plugin, PluginConstructor, PluginContext};\n# use std::{fs::File, io::Read};\n#\nstruct MyGameConstructor; impl PluginConstructor for MyGameConstructor { fn create_instance( &self, _scene_path: Option<&str>, context: PluginContext, ) -> Box { Box::new(MyGame::new(context)) }\n} struct MyGame { data: Option >,\n} impl MyGame { pub fn new(context: PluginContext) -> Self { context.task_pool.spawn_plugin_task( // Emulate heavy task by reading a potentially large file. The game will be fully // responsive while it runs. async move { let mut file = File::open(\"some/file.txt\").unwrap(); let mut data = Vec::new(); file.read_to_end(&mut data).unwrap(); data }, // This closure is called when the future above has finished, but not immediately - on // the next update iteration. |data, game: &mut MyGame, _context| { // Store the data in the game instance. game.data = Some(data); }, ); // Immediately return the new game instance with empty data. Self { data: None } }\n} impl Plugin for MyGame { fn update(&mut self, _context: &mut PluginContext) { // Do something with the data. if let Some(data) = self.data.take() { println!(\"The data is: {:?}\", data); } }\n}","breadcrumbs":"Scripting » Tasks » Examples","id":"105","title":"Examples"},"106":{"body":"You should avoid task-based approach for small (in time terms) tasks, because each task has additional cost which might be larger than the actual task executed in-place. This is because you need to send your task to a separate thread using a channel, then the callback closure is stored as a trait object which involves memory allocation. Since tasks uses type erasure technique, they perform dynamic type casting which is not free. Also, there could be any other implementation-defined \"slow\" spots. A general advice would be: run a profiler first to find hot spots in your game, then try to optimize them. If you hit the optimization limit, use tasks. Do not use tasks until you really need them, try to optimize your game first! If you're working on a simple 2D game, you'll never need to use tasks. You might need to use tasks when your have, for instance, a procedurally generated world that should be generated on the fly. For example, if you're making a dungeon crawler with infinite world. Tasks are also very useful for large games with loads of content and activities. You could off-thread AI, world manipulation (for example if you have a destructible world), etc. In other words - do not use a sledgehammer to hammer nails, unless you have a huge nail.","breadcrumbs":"Scripting » Tasks » Performance","id":"106","title":"Performance"},"107":{"body":"Scene is a container for game entities. Currently, scenes in the engine manage following entities: Graph Animations Physics (rigid bodies, colliders, joints) Sound Scene allows you to create isolated \"world\" which won't interact with other scenes, it is very useful for many more or less complex games.","breadcrumbs":"Scene » Scene","id":"107","title":"Scene"},"108":{"body":"A scene could be created either in FyroxEd or programmatically. You can also combine both approaches, where you build all \"static\" content in the editor and adding rest of the entities (bots, interactive objects, etc.) manually by instantiating respective prefabs at runtime.","breadcrumbs":"Scene » How to create","id":"108","title":"How to create"},"109":{"body":"There is a separate chapter in the book that should help you to create a scene. After a scene is created, you can load it as any other 3D model (or prefab) using the resource manager: # extern crate fyrox;\n# use fyrox::{\n# core::{futures::executor::block_on, pool::Handle},\n# asset::manager::{ResourceManager}, resource::model::{Model, ModelResourceExtension},\n# scene::{node::Node, Scene},\n# };\n# use std::path::Path; fn load_scene(resource_manager: ResourceManager) -> Scene { // Create parent scene. let mut scene = Scene::new(); // Request child scene and block until it loading. let scene_resource = block_on( resource_manager .request:: (\"path/to/your/scene.rgs\"), ) .unwrap(); // Create an instance of the scene in the parent scene. let child_scene = scene_resource.instantiate(&mut scene); scene\n} Please note that here we're creating an empty scene and only then instantiating another scene into it. Why is this needed? Child scene is considered as prefab , and it is \"instantiated\" in the parent scene. Considering it as prefab allows you modifying your scene separately and serialization/deserialization will be able to correctly apply any changes in the scene.","breadcrumbs":"Scene » Using FyroxEd","id":"109","title":"Using FyroxEd"},"11":{"body":"This is a more or less complete (yet, it can be outdated) list of engine features:","breadcrumbs":"Introduction » Introduction to Fyrox » Engine Features","id":"11","title":"Engine Features"},"110":{"body":"A scene could also be created manually: # extern crate fyrox;\n# use fyrox::{core::pool::Handle, engine::Engine, scene::Scene}; fn create_scene(engine: &mut Engine) -> Handle { let mut scene = Scene::new(); // Use node builders, create sounds, add physics, etc. here to fill the scene. engine.scenes.add(scene)\n} See respective node builders docs to populate the scene.","breadcrumbs":"Scene » Create scene manually","id":"110","title":"Create scene manually"},"111":{"body":"All scenes \"lives\" in the engine, the engine has ownership over your scene after you've added it in the engine. You can borrow a scene at any time using its handle and do some changes: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# event_loop::ControlFlow,\n# plugin::{Plugin, PluginContext},\n# scene::Scene,\n# };\n# struct Game { scene: Handle ,\n} impl Plugin for Game { fn update(&mut self, context: &mut PluginContext, control_flow: &mut ControlFlow) { // Borrow a scene using its handle. `try_get` performs immutable borrow, to mutably borrow the scene // use `try_get_mut`. if let Some(scene) = context.scenes.try_get(self.scene) { // Do something. println!(\"{:?}\", scene.graph.performance_statistics); } }\n}","breadcrumbs":"Scene » Where all my scenes located?","id":"111","title":"Where all my scenes located?"},"112":{"body":"You can create your scene in separate thread and then pass it to main thread to insert it in the engine. Why this is needed? Remember the last time you've played a relatively large game, you've probably noticed that it have loading screens and loading screen has some fancy interactive stuff with progress bar. Loading screen is fully responsive while the game doing hard job loading the world for you. Got it already? Asynchronous scene loading is needed to create/load large scenes with tons of resources without blocking main thread, thus leaving the game fully responsive.","breadcrumbs":"Scene » Building scene asynchronously","id":"112","title":"Building scene asynchronously"},"113":{"body":"Usually you should have only one scene active (unless you're making something very special), you should use .enabled flag of a scene to turn it off or on. Deactivated scenes won't be rendered, the physics won't be updated, the sound will stop, and so on. In other words the scene will be frozen. This is useful for situations when you often need to switch between scenes, leaving other scene in frozen state. One of the examples where this can be useful is menus. In most games when you're entering the menu, game world is paused.","breadcrumbs":"Scene » Managing multiple scenes","id":"113","title":"Managing multiple scenes"},"114":{"body":"Every scene has default ambient lighting, it is defined by a single RGB color. By default, every scene has some pre-defined ambient lighting, it is bright enough, so you can see your objects. In some cases you may need to adjust it or even make it black (for horror games for instance), this can be achieved by a single line of code: # extern crate fyrox;\n# use fyrox::scene::Scene;\n# use fyrox::core::color::Color;\n# let mut scene = Scene::default();\n# scene.rendering_options.ambient_lighting_color = Color::opaque(30, 30, 30); Please keep in mind that ambient lighting does not mean global illumination, it is a different lighting technique which is not available in the engine yet.","breadcrumbs":"Scene » Ambient lighting","id":"114","title":"Ambient lighting"},"115":{"body":"Graph is a set of objects with hierarchical relationships between each object. It is one of the most important entities in the engine. Graph takes care of your scene objects and does all the hard work for you.","breadcrumbs":"Scene » Graph » Graph","id":"115","title":"Graph"},"116":{"body":"You don't need to create a graph manually, every scene has its own instance of the graph. It can be accessed pretty easily: scene_ref.graph","breadcrumbs":"Scene » Graph » How to create","id":"116","title":"How to create"},"117":{"body":"There are two ways of adding nodes to the graph, either using node builders or manually by calling graph.add_node.","breadcrumbs":"Scene » Graph » Adding nodes","id":"117","title":"Adding nodes"},"118":{"body":"Every node in the engine has its respective builder which can be used to create an instance of the node. Using builders is a preferable way to create scene nodes. There are following node builders: BaseBuilder - creates an instance of base node. See Base node for more info. PivotBuilder - creates an instance of pivot node. See Base node for more info. CameraBuilder - creates an instance of camera node. See Camera node for more info. MeshBuilder - creates an instance of mesh node. See Mesh node for more info. LightBuilder - creates an instance of light node. See Light node for more info. SpriteBuilder - creates an instance of sprite node. See Sprite node for more info. ParticleSystemBuilder - creates an instance of particle system node. See Particle system node for more info. TerrainBuilder - creates an instance of terrain node. See Terrain node for more info. DecalBuilder - creates an instance of decal node. See Decal node for more info. RigidBody - creates an instance of rigid body node. See Rigid body for more info. Collider - creates an instance of collider node. See Collider for more info. Joint - creates an instance of joint node. See Joint for more info. Rectangle - creates an instance of 2D rectangle node. See Rectangle for more info. Every builder, other than BaseBuilder, accepts BaseBuilder as a parameter in .new(..) method. Why so? Because every node (other than Base) is \"derived\" from Base via composition and the derived builder must know how to build Base node. While it may sound confusing, it is actually very useful and clear. Consider this example: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{\n# base::BaseBuilder, camera::CameraBuilder, node::Node, transform::TransformBuilder,\n# Scene,\n# },\n# }; fn create_camera(scene: &mut Scene) -> Handle { CameraBuilder::new( // Here we passing a base builder. Note that, since we can build Base node separately // we can pass any custom values to it while building. BaseBuilder::new().with_local_transform( TransformBuilder::new() .with_local_position(Vector3::new(2.0, 0.0, 3.0)) .build(), ), ) // Here we just setting desired Camera properties. .with_fov(60.0f32.to_radians()) .build(&mut scene.graph)\n} As you can see, we're creating an instance of BaseBuilder and fill it with desired properties as well as filling the CameraBuilder's instance properties. This is a very flexible mechanism, allowing you to build complex hierarchies in a declarative manner: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{\n# base::BaseBuilder, camera::CameraBuilder, mesh::MeshBuilder, node::Node,\n# sprite::SpriteBuilder, transform::TransformBuilder, Scene,\n# },\n# }; fn create_node(scene: &mut Scene) -> Handle { CameraBuilder::new( BaseBuilder::new() // Add some children nodes. .with_children(&[ // A staff... MeshBuilder::new( BaseBuilder::new() .with_name(\"MyFancyStaff\") .with_local_transform( TransformBuilder::new() .with_local_position(Vector3::new(0.5, 0.5, 1.0)) .build(), ), ) .build(&mut scene.graph), // and a spell. SpriteBuilder::new( BaseBuilder::new() .with_name(\"MyFancyFireball\") .with_local_transform( TransformBuilder::new() .with_local_position(Vector3::new(-0.5, 0.5, 1.0)) .build(), ), ) .build(&mut scene.graph), ]) .with_local_transform( TransformBuilder::new() .with_local_position(Vector3::new(2.0, 0.0, 3.0)) .build(), ), ) .with_fov(60.0f32.to_radians()) .build(&mut scene.graph)\n} This code snippet creates a camera for first-person role-playing game's player, it will have a staff in \"right-hand\" and a spell in the left hand. Of course all of this is very simplified, but should give you the main idea. Note that staff and fireball will be children nodes of camera, and when setting their transform, we're actually setting local transform which means that the transform will be relative to camera's. The staff and the spell will move together with the camera.","breadcrumbs":"Scene » Graph » Using node builders","id":"118","title":"Using node builders"},"119":{"body":"For some rare cases you may also want to delay adding a node to the graph, specifically for that purpose, every node builder has .build_node method which creates an instance of Node but does not add it to the graph. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{base::BaseBuilder, camera::CameraBuilder, node::Node, Scene},\n# }; fn create_node(scene: &mut Scene) -> Handle { let node: Node = CameraBuilder::new(BaseBuilder::new()).build_node(); // We must explicitly add the node to the graph. scene.graph.add_node(node)\n}","breadcrumbs":"Scene » Graph » Adding a node manually","id":"119","title":"Adding a node manually"},"12":{"body":"Exceptional safety, reliability, and speed. PC (Windows, Linux, macOS), Android, Web (WebAssembly) support . Modern, PBR rendering pipeline. Comprehensive documentation . Guide book 2D support. Integrated editor. Fast iterative compilation. Classic object-oriented design. Lots of examples.","breadcrumbs":"Introduction » Introduction to Fyrox » General","id":"12","title":"General"},"120":{"body":"For many cases you can't use builders to create complex hierarchy, the simplest example of such situation when you're creating an instance of some 3D model. If you want the instance to be a child object of some other object, you should attach it explicitly by using graph.link_nodes(..): # extern crate fyrox;\n# use fyrox::{\n# core::{futures::executor::block_on, pool::Handle},\n# asset::manager::ResourceManager, resource::model::{Model, ModelResourceExtension},\n# scene::{base::BaseBuilder, camera::CameraBuilder, node::Node, Scene},\n# }; fn link_weapon_to_camera( scene: &mut Scene, camera: Handle , resource_manager: ResourceManager,\n) { let weapon = block_on( resource_manager .request:: (\"path/to/weapon.fbx\"), ) .unwrap() .instantiate(scene); // Link weapon to the camera. scene.graph.link_nodes(weapon, camera);\n} Here we've loaded a weapon 3D model, instantiated it on scene and attached to existing camera.","breadcrumbs":"Scene » Graph » How to modify the hierarchy","id":"120","title":"How to modify the hierarchy"},"121":{"body":"A node could be removed by simply calling graph.remove_node(handle), this method removes the node from the graph with all of its children nodes . Sometimes this is unwanted behaviour, and you want to preserve children nodes while deleting parent node. To do that, you need to explicitly detach children nodes of the node you're about to delete: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{node::Node, Scene},\n# }; fn remove_preserve_children(scene: &mut Scene, node_to_remove: Handle ) { for child in scene.graph[node_to_remove].children().to_vec() { scene.graph.unlink_node(child); } scene.graph.remove_node(node_to_remove);\n} After calling this function, every child node of node_to_remove will be detached from it and the node_to_remove will be deleted. remove_node has some limitations: it cannot be used to extract \"sub-graph\" from the graph, it just drops nodes immediately.","breadcrumbs":"Scene » Graph » How to remove nodes","id":"121","title":"How to remove nodes"},"122":{"body":"Transformation (transform for short) - is a special entity that changes coordinate system from one to another. It is used primarily in scene nodes to store their position/rotation/scale/pivots/etc. Fyrox has quite complex transformations, that supports: Position (T) Rotation (R) Scale (S) Pre-rotation (Rpre) Post-rotation (Rpost) Rotation Pivot (Rp) Rotation Offset (Roff) Scaling Offset (Soff) Scaling Pivot (Sp) Final transformation matrix will be Transform = T * Roff * Rp * Rpre * R * Rpost * Rp⁻¹ * Soff * Sp * S * Sp⁻¹. In 99.9% cases first three are enough for pretty much every task. Other six components used for specific stuff (mainly for nodes that imported from FBX file format).","breadcrumbs":"Scene » Transformation » Transformation","id":"122","title":"Transformation"},"123":{"body":"A prefab is a separate scene that can be instantiated in some other scene, while preserving links between properties of its instances and of its parent prefab. Prefabs allow you to create a part of a scene and have multiple instances of it in other scenes. Let's quickly check what that means on practice. The engine has a prefab system which allows you to build hierarchical scenes which can include any number of other scenes as child scenes. Child scenes can have their own child scenes and so on. This is very efficient decoupling mechanism that allows you to put pieces of the scene in separate scenes (prefabs) and modify them independently. The changes in child scenes will be automatically reflected to all parent scenes. Here is the very simple example of why this is important: imagine you need to populate a town with 3D models of cars. Each kind of car has its own 3D model and for example, a collision body that won't allow the player to walk through cars. How would you do this? The simplest (and dumbest) solution is to copy dozens of car models in the scene, and you're done. Imagine that now you need to change something in your car, for example, add a trunk that can be opened. What will you do? Of course, you should \"iterate\" over each car model and do the required changes, you simply don't have any other option. This will eat huge amount of time and in general it is very non-productive. This is where prefabs will save you hours of work. All you need to do is to create a car prefab and instantiate it multiple times in your scene. When you'll need to change something in the car, you simply go to the prefab and change it. After that every prefab instance will have your changes! Prefabs can be used to create self-contained entities in your game, examples of this includes: visual effects, any scripted game entities (bots, turrets, player, doors, etc.). Such prefabs can be either directly instantiated in a scene in the editor, or instantiated at runtime when needed.","breadcrumbs":"Scene » Prefabs » Prefabs","id":"123","title":"Prefabs"},"124":{"body":"All you need to do is to make a scene in the editor with all required objects and save it! After that, you can use the scene in other scenes and just do its instantiation, as in usual 3D models. You can either instantiate it from the editor by drag'n'drop a prefab to scene previewer, or do standard model resource instantiation","breadcrumbs":"Scene » Prefabs » How to create and use a prefab","id":"124","title":"How to create and use a prefab"},"125":{"body":"As already mentioned in the intro section, instances inherit properties from their parent prefabs. For example, you can change position of an object in prefab and every instance will reflect that change - the object's instances will also move. This works until there's no manual change to a property in instance, if you do so, your change is considered with higher priority. See this chapter for more info.","breadcrumbs":"Scene » Prefabs » Property inheritance","id":"125","title":"Property inheritance"},"126":{"body":"Prefabs can have other prefab instances inside it. This means that you can, for example, create a room populated with instances of other prefabs (bookshelves, chairs, tables, etc.) and then use the room prefab to build a bigger scene. The changes in the base prefabs will be reflected in their instances, regardless of how deep the hierarchy is.","breadcrumbs":"Scene » Prefabs » Hierarchical Prefabs","id":"126","title":"Hierarchical Prefabs"},"127":{"body":"Property inheritance is used to propagate changes of unmodified properties from a prefab to its instances. For example, you can change scale of a node in a prefab and its instances will have the same scale too, unless the scale is set explicitly in an instance. Such feature allows you to tweak instances, add some unique details to them, but take general properties from parent prefabs. Property inheritance works for prefab hierarchies of any depth, this means that you can create something like this: a room prefab can have multiple instances of various furniture prefabs in it, while the furniture prefabs can also be constructed from other prefabs and so on. In this case if you modify a property in one of the prefabs in the chain, all instance will immediately sync their unmodified properties.","breadcrumbs":"Scene » Property Inheritance » Property Inheritance","id":"127","title":"Property Inheritance"},"128":{"body":"It is possible to use property inheritance for script variables. To make a property of your script inheritable, all you need is to wrap its value using InheritableVariable wrapper. # extern crate fyrox;\n# use fyrox::core::variable::InheritableVariable;\n# use fyrox::core::visitor::prelude::*;\n# use fyrox::core::reflect::prelude::*;\n#[derive(Reflect, Visit, Default, Clone, Debug)]\nstruct MyScript { foo: InheritableVariable \n} The engine will automatically resolve the correct value for the property when a scene with the script is loaded. If your property was modified, then its value will remain the same, it won't be overwritten by parent's value. Keep in mind, that the type of the inheritable variable must be cloneable and support reflection. InheritableVariable implements the Deref + DerefMut traits, this means that any access via the DerefMut trait will mark the property as modified. This could be undesired in some cases so InheritableVariable supports special xxx_silent methods that don't touch the internal modifiers and allows you to substitute the value with some other \"silently\" - without marking the variable as modified.","breadcrumbs":"Scene » Property Inheritance » How To Create Inheritable Properties","id":"128","title":"How To Create Inheritable Properties"},"129":{"body":"Inheritable variables intended to be \"atomic\" - it means that the variable stores some simple variable (f32, String, Handle , etc.). While it is possible to store \"compound\" variables (InheritableVariable ), it is not advised because of inheritance mechanism. When the engine sees inheritable variable, it searches the same variable in a parent entity and copies its value to the child, thus completely replacing its content. In this case, even if you have inheritable variables inside compound field, they won't be inherited correctly. Let's demonstrate this in the following code snippet: # extern crate fyrox;\n# use fyrox::core::reflect::prelude::*;\n# use fyrox::core::variable::InheritableVariable;\n# #[derive(Reflect, Clone, PartialEq, Eq, Debug)]\nstruct SomeComplexData { foo: InheritableVariable , bar: InheritableVariable ,\n} #[derive(Reflect, Debug)]\nstruct MyEntity { some_field: InheritableVariable , // This field won't be inherited correctly - at first it will take parent's value and then // will try to inherit inner fields, but its is useless step, because inner data is already // a full copy of parent's field value. incorrectly_inheritable_data: InheritableVariable , // Subfields of this field will be correctly inherited, because the field itself is not inheritable. inheritable_data: SomeComplexData,\n} This code snippet should clarify, that inheritable fields should contain some \"simple\" data, and almost never - complex structs.","breadcrumbs":"Scene » Property Inheritance » Which Fields Should Be Inheritable?","id":"129","title":"Which Fields Should Be Inheritable?"},"13":{"body":"Custom shaders, materials, and rendering techniques. Physically-based rendering. Metallic workflow. High dynamic range (HDR) rendering. Tone mapping. Color grading. Auto-exposure. Gamma correction. Deferred shading. Directional light. Point lights + shadows. Spotlights + shadows. Screen-Space Ambient Occlusion (SSAO). Soft shadows. Volumetric light (spot, point). Batching. Instancing. Fast Approximate Anti-Aliasing (FXAA). Normal mapping. Parallax mapping. Render in texture. Forward rendering for transparent objects. Sky box. Deferred decals. Multi-camera rendering. Lightmapping. Soft particles. Fully customizable vertex format. Compressed textures support. High-quality mip-map on-demand generation.","breadcrumbs":"Introduction » Introduction to Fyrox » Rendering","id":"13","title":"Rendering"},"130":{"body":"The editor wraps all inheritable properties in a special widget that supports property reversion. Reversion allows you to drop current changes and take the parent's property value. This is useful if you want a property to inherit its parent's value. In the Inspector it looks like this: revert Clicking on the < button will take the value from the parent prefab and the property won't be marked as modified anymore. In case there is no parent prefab, the button will just drop modified flag.","breadcrumbs":"Scene » Property Inheritance » Editor","id":"130","title":"Editor"},"131":{"body":"Base node is a scene node that stores hierarchical information (a handle to the parent node and a set of handles to children nodes), local and global transform, name, tag, lifetime, etc. It has self-describing name - it is used as a base node for every other scene node (via composition). It has no graphical information, so it is invisible all the time, but it is useful as a \"container\" for children nodes.","breadcrumbs":"Scene » Base Node » Base node","id":"131","title":"Base node"},"132":{"body":"Use the PivotBuilder to create an instance of the Pivot node (remember Base node itself is used only to build other node types): # extern crate fyrox;\n# use fyrox::scene::{base::BaseBuilder, pivot::PivotBuilder, Scene};\n# fn build_node(scene: &mut Scene) {\nlet handle = PivotBuilder::new(BaseBuilder::new()).build(&mut scene.graph);\n# }","breadcrumbs":"Scene » Base Node » How to create","id":"132","title":"How to create"},"133":{"body":"To build a complex hierarchy of some nodes, use .with_children() method of the BaseBuilder, it allows you to build a hierarchy of any complexity: # extern crate fyrox;\n# use fyrox::scene::{base::BaseBuilder, pivot::PivotBuilder, camera::CameraBuilder, Scene};\n#\n# fn build_node(scene: &mut Scene) {\nlet handle = PivotBuilder::new(BaseBuilder::new() .with_children(&[ CameraBuilder::new(BaseBuilder::new()).build(&mut scene.graph), PivotBuilder::new(BaseBuilder::new() .with_children(&[PivotBuilder::new(BaseBuilder::new()).build(&mut scene.graph)])) .build(&mut scene.graph), ])) .build(&mut scene.graph);\n# } Note that when we're building a Camera instance, we're passing a new instance of BaseBuilder to it, this instance can also be used to set some properties and a set of children nodes. The \"fluent syntax\" is not mandatory to use, the above code snipped could be rewritten like this: # extern crate fyrox;\n# use fyrox::scene::{base::BaseBuilder, pivot::PivotBuilder, camera::CameraBuilder, Scene};\n# # fn build_node(scene: &mut Scene) {\nlet camera = CameraBuilder::new(BaseBuilder::new()).build(&mut scene.graph); let child_base = PivotBuilder::new(BaseBuilder::new()).build(&mut scene.graph); let base = PivotBuilder::new(BaseBuilder::new() .with_children(&[child_base])) .build(&mut scene.graph); let handle = PivotBuilder::new(BaseBuilder::new() .with_children(&[camera, base])) .build(&mut scene.graph);\n# } However, it looks less informative, because it loses the hierarchical view and it is harder to tell the relations between objects.","breadcrumbs":"Scene » Base Node » Building a complex hierarchy","id":"133","title":"Building a complex hierarchy"},"134":{"body":"Base node has a local transform that allows you to translate/scale/rotate/etc. your node as you want to. For example, to move a node at specific location you could use this: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{node::Node, Scene},\n# };\n#\n# fn translate_node(scene: &mut Scene, node_handle: Handle ) {\nscene.graph[node_handle] .local_transform_mut() .set_position(Vector3::new(1.0, 0.0, 2.0));\n# } You could also chain multiple set_x calls, like so: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{node::Node, Scene},\n# };\n#\n# fn transform_node(scene: &mut Scene, node_handle: Handle ) {\nscene.graph[node_handle] .local_transform_mut() .set_position(Vector3::new(1.0, 0.0, 2.0)) .set_scale(Vector3::new(2.0, 2.0, 2.0)) .set_rotation_offset(Vector3::new(1.0, 1.0, 0.0));\n# } See more info about transformations here .","breadcrumbs":"Scene » Base Node » Transform","id":"134","title":"Transform"},"135":{"body":"Base node stores all info about local visibility and global visibility (with parent's chain visibility included). Changing node's visibility could be useful if you want to improve performance by hiding distant objects (however it strongly advised to use level-of-detail for this) or to hide some objects in your scene. There are three main methods to set or fetch visibility: set_visibility - sets local visibility for a node. visibility - returns current local visibility of a node. global_visibility - returns combined visibility of a node. It includes visibility of every parent node in the hierarchy, so if you have a parent node with some children nodes and set parent's visibility to false, global visibility of children nodes will be false too, even if local visibility is true. This is useful technique for hiding complex objects with lots of children nodes.","breadcrumbs":"Scene » Base Node » Visibility","id":"135","title":"Visibility"},"136":{"body":"A scene node could be enabled or disabled. Disabled nodes are excluded from a game loop and has almost zero CPU consumption (their global transform/visibility/enabled state is still updated due to limitations of the engine). Disabling a node could be useful if you need to completely freeze some hierarchy and do keep it in this state until it is enabled again. It could be useful to disable parts of a scene with which a player cannot interact to improve performance. Keep in mind, that enabled state is hierarchical like visibility. When you're disabling a parent node with some children nodes, the children nodes will be disabled too.","breadcrumbs":"Scene » Base Node » Enabling/disabling scene nodes","id":"136","title":"Enabling/disabling scene nodes"},"137":{"body":"Mesh is a scene node that represents a 3D model. This one of the most commonly used nodes in almost every game. Meshes could be easily created either programmatically or be made in some 3D modelling software (like Blender) and loaded in your scene.","breadcrumbs":"Scene » Mesh Node » Mesh node","id":"137","title":"Mesh node"},"138":{"body":"Surface is a set of triangles that uses the same material . Mesh node could contain zero of more surfaces; each surface contains a set of vertices and indices that binds vertices with triangles. Mesh nodes split into surfaces to be rendered effectively by modern GPUs.","breadcrumbs":"Scene » Mesh Node » Surfaces","id":"138","title":"Surfaces"},"139":{"body":"There are basically two ways, how to pick one depends on your needs. In general, using a 3D modelling software is the way to go, especially with tons and tons of free 3D models available online. ⚠️ The engine supports only FBX file format for 3D models!","breadcrumbs":"Scene » Mesh Node » How to create","id":"139","title":"How to create"},"14":{"body":"Multiple scenes. Full-featured scene graph. Level-of-detail (LOD) support. GPU Skinning. Various scene nodes: Pivot. Camera. Decal. Mesh. Particle system. Sprite. Multilayer terrain. Rectangle (2D Sprites) Rigid body + Rigid Body 2D Collider + Collider 2D Joint + Joint 2D","breadcrumbs":"Introduction » Introduction to Fyrox » Scene","id":"14","title":"Scene"},"140":{"body":"To create a 3D model, you could use Blender and then export it to FBX file format. To load your 3D model in the game, you should do few simple steps (loading a 3D model does not differ from a prefab instantiation): # extern crate fyrox; use fyrox::{ core::{futures::executor::block_on, pool::Handle}, asset::manager::{ResourceManager}, resource::model::{Model, ModelResourceExtension}, scene::{node::Node, Scene},\n};\nuse std::path::Path; fn load_model_to_scene( scene: &mut Scene, path: &Path, resource_manager: ResourceManager,\n) -> Handle { // Request model resource and block until it loading. let model_resource = block_on(resource_manager.request:: (path)) .unwrap(); // Create an instance of the resource in the scene. model_resource.instantiate(scene)\n} This code snippet intentionally omits proper async/await usage (instead it just blocks current thread until model is loading) and error handling. In the real game you should carefully handle all errors and use async/await properly.","breadcrumbs":"Scene » Mesh Node » Using a 3D modelling software","id":"140","title":"Using a 3D modelling software"},"141":{"body":"A mesh instance could be created from code, such meshes are called \"procedural\". They're suitable for cases when you cannot create a mesh in 3D modelling software. # extern crate fyrox; use fyrox::{ core::{ algebra::{Matrix4, Vector3}, parking_lot::Mutex, pool::Handle, sstorage::ImmutableString, }, asset::manager::ResourceManager, resource::model::{Model, ModelResourceExtension}, resource::texture::Texture, material::{shader::SamplerFallback, Material, PropertyValue, SharedMaterial}, scene::{ base::BaseBuilder, mesh::{ surface::{SurfaceBuilder, SurfaceData, SurfaceSharedData}, MeshBuilder, }, node::Node, transform::TransformBuilder, Scene, },\n};\nuse std::sync::Arc; fn create_procedural_mesh( scene: &mut Scene, resource_manager: ResourceManager,\n) -> Handle { let mut material = Material::standard(); // Material is completely optional, but here we'll demonstrate that it is possible to // create procedural meshes with any material you want. material .set_property( &ImmutableString::new(\"diffuseTexture\"), PropertyValue::Sampler { value: Some(resource_manager.request:: (\"some_texture.jpg\")), fallback: SamplerFallback::White, }, ) .unwrap(); // Notice the MeshBuilder. MeshBuilder::new( BaseBuilder::new().with_local_transform( TransformBuilder::new() .with_local_position(Vector3::new(0.0, -0.25, 0.0)) .build(), ), ) .with_surfaces(vec![SurfaceBuilder::new(SurfaceSharedData::new( // Our procedural mesh will have a form of squashed cube. // A mesh can have unlimited amount of surfaces. SurfaceData::make_cube(Matrix4::new_nonuniform_scaling(&Vector3::new( 25.0, 0.25, 25.0, ))), )) .with_material(SharedMaterial::new(material)) .build()]) .build(&mut scene.graph)\n} As you can see, creating a mesh procedurally requires lots of manual work and not so easy.","breadcrumbs":"Scene » Mesh Node » Creating a procedural mesh","id":"141","title":"Creating a procedural mesh"},"142":{"body":"Mesh node supports bone-based animation (skinning) and blend shapes. See Animation chapter for more info.","breadcrumbs":"Scene » Mesh Node » Animation","id":"142","title":"Animation"},"143":{"body":"It is possible to access vertex buffer and index buffer of a mesh to either read or write some data there. For example, the following code extracts world-space positions of every vertex of an animated mesh: # extern crate fyrox;\nuse fyrox::{ core::algebra::{Point3, Vector3}, scene::{ graph::Graph, mesh::{ buffer::{VertexAttributeUsage, VertexReadTrait}, Mesh, }, },\n}; fn extract_world_space_vertices(mesh: &Mesh, graph: &Graph) -> Vec > { let mut vertices = Vec::new(); for surface in mesh.surfaces() { let data = surface.data().lock(); for vertex in data.vertex_buffer.iter() { let Ok(position) = vertex.read_3_f32(VertexAttributeUsage::Position) else { continue; }; let Ok(weights) = vertex.read_4_f32(VertexAttributeUsage::BoneWeight) else { continue; }; let Ok(indices) = vertex.read_4_u8(VertexAttributeUsage::BoneIndices) else { continue; }; let mut world_space_vertex = Vector3::default(); for (weight, index) in weights.iter().zip(indices.iter()) { if let Some(bone_node) = surface .bones() .get(*index as usize) .and_then(|bone_handle| graph.try_get(*bone_handle)) { let bone_transform = bone_node.global_transform() * bone_node.inv_bind_pose_transform(); world_space_vertex += bone_transform .transform_point(&Point3::from(position)) .coords .scale(*weight); } } vertices.push(world_space_vertex); } } vertices\n}","breadcrumbs":"Scene » Mesh Node » Data Buffers","id":"143","title":"Data Buffers"},"144":{"body":"The engine offers complex lighting system with various types of light sources.","breadcrumbs":"Scene » Light Node » Light node","id":"144","title":"Light node"},"145":{"body":"There are three main types of light sources: directional, point, and spot lights.","breadcrumbs":"Scene » Light Node » Light types","id":"145","title":"Light types"},"146":{"body":"Directional light does not have a position, its rays are always parallel, and it has a particular direction in space. An example of directional light in real-life could be our Sun. Even if it is a point light, it is so far away from the Earth, so we can assume that its rays are always parallel. Directional light sources are suitable for outdoor scenes. A directional light source could be created like this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# light::{directional::DirectionalLightBuilder, BaseLightBuilder},\n# node::Node,\n# Scene,\n# },\n# }; fn create_directional_light(scene: &mut Scene) -> Handle { DirectionalLightBuilder::new(BaseLightBuilder::new(BaseBuilder::new())) .build(&mut scene.graph)\n} By default, the light source will be oriented to lit \"the ground\". In other words its direction will be faced towards (0.0, -1.0, 0.0) vector. You can rotate it as you want by setting local transform of it while building. Something like this: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::{UnitQuaternion, Vector3},\n# pool::Handle,\n# },\n# scene::{\n# base::BaseBuilder,\n# light::{directional::DirectionalLightBuilder, BaseLightBuilder},\n# node::Node,\n# transform::TransformBuilder,\n# Scene,\n# },\n# }; fn create_directional_light(scene: &mut Scene) -> Handle { DirectionalLightBuilder::new(BaseLightBuilder::new( BaseBuilder::new().with_local_transform( TransformBuilder::new() .with_local_rotation(UnitQuaternion::from_axis_angle( &Vector3::x_axis(), -45.0f32.to_radians(), )) .build(), ), )) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Light Node » Directional light","id":"146","title":"Directional light"},"147":{"body":"Point light is a light source that emits lights in all directions, it has a position, but does not have an orientation. An example of a point light source: light bulb. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# light::{point::PointLightBuilder, BaseLightBuilder},\n# node::Node,\n# Scene,\n# },\n# }; fn create_point_light(scene: &mut Scene) -> Handle { PointLightBuilder::new(BaseLightBuilder::new(BaseBuilder::new())) .with_radius(5.0) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Light Node » Point light","id":"147","title":"Point light"},"148":{"body":"Spotlight is a light source that emits lights in cone shape, it has a position and orientation. An example of a spotlight source: flashlight. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# light::{spot::SpotLightBuilder, BaseLightBuilder},\n# node::Node,\n# Scene,\n# },\n# }; fn create_spot_light(scene: &mut Scene) -> Handle { SpotLightBuilder::new(BaseLightBuilder::new(BaseBuilder::new())) .with_distance(5.0) .with_hotspot_cone_angle(50.0f32.to_radians()) .with_falloff_angle_delta(10.0f32.to_radians()) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Light Node » Spotlight","id":"148","title":"Spotlight"},"149":{"body":"scattering Spot and point lights support light scattering effect. Imagine you're walking with a flashlight in a foggy weather, the fog will scatter the light from your flashlight making it, so you'll see the \"light volume\". Light scattering is enabled by default , so you don't have to do anything to enable it. However, in some cases you might want to disable it, you can do this either while building a light source or change light scattering options on existing light source. Here is the small example of how to do that. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{node::Node, light::BaseLight, Scene},\n# }; fn disable_light_scatter(scene: &mut Scene, light_handle: Handle ) { scene.graph[light_handle] .query_component_mut:: () .unwrap() .enable_scatter(false);\n} You could also change the amount of scattering per each color channel, using this you could imitate the Rayleigh scattering : # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{node::Node, light::BaseLight, Scene},\n# }; fn use_rayleigh_scattering(scene: &mut Scene, light_handle: Handle ) { scene.graph[light_handle] .query_component_mut:: () .unwrap() .set_scatter(Vector3::new(0.03, 0.035, 0.055));\n}","breadcrumbs":"Scene » Light Node » Light scattering","id":"149","title":"Light scattering"},"15":{"body":"High quality binaural sound with HRTF support . Generic and spatial sound sources. Built-in streaming for large sounds. Raw samples playback support. WAV/OGG format support. HRTF support for excellent positioning and binaural effects. Reverb effect.","breadcrumbs":"Introduction » Introduction to Fyrox » Sound","id":"15","title":"Sound"},"150":{"body":"By default, light sources cast shadows. You can change this by using set_cast_shadows method of a light source. You should carefully manage shadows: shadows giving the most significant performance impact, you should keep the amount of light sources that can cast shadows at lowest possible amount to keep performance at good levels. You can also turn on/off shadows when you need: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{node::Node, light::BaseLight, Scene},\n# }; fn switch_shadows(scene: &mut Scene, light_handle: Handle , cast_shadows: bool) { scene.graph[light_handle] .query_component_mut:: () .unwrap() .set_cast_shadows(cast_shadows);\n} Not every light should cast shadows, for example a small light that a player can see only in a distance can have shadows disabled. You should set the appropriate values depending on your scene, just remember: the fewer the shadows the better the performance. The most expensive shadows are from point lights, the less, from spotlights and directional lights.","breadcrumbs":"Scene » Light Node » Shadows","id":"150","title":"Shadows"},"151":{"body":"Lights are not cheap, every light source has some performance impact. As a general rule, try to keep the amount of light sources at reasonable levels and especially try to avoid creating tons of light sources in a small area. Keep in mind that the less area the light needs to \"cover\", the higher the performance. This means that you can have tons of small light sources for free.","breadcrumbs":"Scene » Light Node » Performance","id":"151","title":"Performance"},"152":{"body":"Sprite is just a quad mesh that is always facing camera. It has size, color, rotation around \"look\" axis and a texture. Sprites are useful mostly for projectiles, like glowing plasma, and for things that should always face a camera. ⚠️ It should be noted that sprites are not meant to be used for 2D games , they're only for 3D. Use Rectangle node if you need 2D sprites, they have optimized renderer which can handle tons of sprites at once (sprite batching).","breadcrumbs":"Scene » Sprite Node » Sprite","id":"152","title":"Sprite"},"153":{"body":"A sprite instance could be created using SpriteBuilder: # extern crate fyrox;\n# use fyrox::{\n# core::{color::Color, pool::Handle}, resource::texture::Texture,\n# scene::{base::BaseBuilder, node::Node, sprite::SpriteBuilder, Scene},\n# }; fn create_sprite(scene: &mut Scene) -> Handle { SpriteBuilder::new(BaseBuilder::new()) .with_size(2.0) .with_rotation(45.0f32.to_radians()) .with_color(Color::RED) .build(&mut scene.graph)\n} A sprite with a texture could be created by using .with_texture method of the builder: # extern crate fyrox;\nuse fyrox::{ core::pool::Handle, asset::manager::ResourceManager, resource::texture::Texture, scene::{base::BaseBuilder, node::Node, sprite::SpriteBuilder, Scene},\n}; fn create_sprite(scene: &mut Scene, resource_manager: ResourceManager) -> Handle { SpriteBuilder::new(BaseBuilder::new()) .with_texture(resource_manager.request:: (\"path/to/your/texture.png\")) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Sprite Node » How to create","id":"153","title":"How to create"},"154":{"body":"Sprites must not be used to create any visual effects that involve many particles. You should use particle systems for that. Why so? Particles systems are very well optimized for managing huge amounts of particles at the same time, but sprites are not. Each sprite is quite heavy to be used as a particle in particle systems, it has a lot of \"useless\" info that will eat a lot of memory. ⚠️ Currently, the renderer will render each sprite in a separate draw call, which is very inefficient. So you should avoid creating lots of sprites.","breadcrumbs":"Scene » Sprite Node » General rules","id":"154","title":"General rules"},"155":{"body":"Sprites are not supporting any sort of lighting, if you need lighted sprites, you need to create your own render pass and use Mesh node with custom shader that will orient all faces towards camera and will do lighting calculations.","breadcrumbs":"Scene » Sprite Node » Limitations","id":"155","title":"Limitations"},"156":{"body":"Particle system is a scene node that is used to create complex visual effects (VFX). It operates on huge amount of particles at once allowing you to do complex simulation that involves large amount of particles. Typically, particle systems are used to create following visual effects: smoke, sparks, blood splatters, steam, etc. smoke","breadcrumbs":"Scene » Particle System Node » Particle system","id":"156","title":"Particle system"},"157":{"body":"Particle system uses single texture for every particle in the system, only Red channel is used. Red channel interpreted as an alpha for all particles. Every particle is affected by Acceleration parameters of the particle system. It defines acceleration (in m/s2) that will affect velocities of every particle. It is used to simulate gravity.","breadcrumbs":"Scene » Particle System Node » Basic Concepts","id":"157","title":"Basic Concepts"},"158":{"body":"Particle is a square (not quadrilateral, this is important) with a texture which is always facing towards camera. It has the following properties: Position - defines a position in local coordinates of particle system (this means that if you rotate a particle system, all particles will be rotated too). Velocity - defines a speed vector (in local coordinates) that will be used to modify local position of the particle each frame. Size - size (in meters) of the square shape of the particle. Size Modifier - a numeric value (in meters per second), that will be added to the Size at each frame, it is used to modify size of the particles. Lifetime - amount of time (in seconds) that the particle can be active for. Rotation - angle (in radians) that defines rotation around particle-to-camera axis (clockwise). Rotation Speed - speed (in radians per second, rad/s) of rotation of the particle. Color - RGBA color of the particle.","breadcrumbs":"Scene » Particle System Node » Particle","id":"158","title":"Particle"},"159":{"body":"Particle system uses emitters to define a set of zones where particles will be spawned, it also defines initial ranges of parameters of particles. Particle system must have at least one emitter to generate particles. Emitter can be one of the following types: Cuboid - emits particles uniformly in a cuboid shape, the shape cannot be rotated, only translated. Sphere - emits particles uniformly in a sphere shape. Cylinder - emits particle uniformly in a cylinder shape, the shape cannot be rotated, only translated. Each emitter have fixed set of parameters that affects initial values for every spawned particle: Position - emitter have its own local position (position relative to parent particle system node), this helps you to create complex particle systems that may spawn particles from multiple zones in space at once. Max Particles - maximum amount of particles available for spawn. By default, it is None, which says that there is no limit. Spawn Rate - rate (in units per second) defines how fast the emitter will spawn particles. Lifetime Range - numeric range (in seconds) for particle lifetime values. The lower the beginning of the range the less spawned particles will live, and vice versa. Size Range - numeric range (in meters) for particle size. Size Modifier Range - numeric range (in meters per second, m/s) for particle size modifier parameter. X/Y/Z Velocity Range - a numeric range (in meters per second, m/s) for a respective velocity axis (X, Y, Z) that defines initial speed along the axis. Rotation Range - a numeric range (in radians) for initial rotation of a new particle. Rotation Speed Range - a numeric range (in radians per second, rad/s) for rotation speed of a new particle. Important: Every range (like Lifetime Range, Size Range, etc.) parameter generates random value for respective parameter of a particle. You can tweak the seed of current random number generator (fyrox::core::thread_rng()) to ensure that generated values will be different each time.","breadcrumbs":"Scene » Particle System Node » Emitters","id":"159","title":"Emitters"},"16":{"body":"Powerful serialization system Almost every entity of the engine can be serialized No need to write your own serialization.","breadcrumbs":"Introduction » Introduction to Fyrox » Serialization","id":"16","title":"Serialization"},"160":{"body":"There are multiple ways of creating a particle system, pick one that best suits your current needs.","breadcrumbs":"Scene » Particle System Node » How to create","id":"160","title":"How to create"},"161":{"body":"The best way to create a particle system is to configure it in the editor, creating from code is possible too (see below), but way harder and may be not intuitive, because of the large amount of parameters. The editor allows you see the result and tweak it very fast. Create a particle system by Create -> Particle System and then you can start editing its properties. By default, new particle system has one Sphere particle emitter, you can add new emitters by clicking + button at the right of Emitters property in the Inspector (or remove by clicking -). Here's a simple example: particle system Now start tweaking desired parameters, it is hard to give any recommendations of how to achieve a particular effect, only practice matters here.","breadcrumbs":"Scene » Particle System Node » Using the editor","id":"161","title":"Using the editor"},"162":{"body":"You can also create particle systems from code (in case if you need some procedurally-generated effects): # extern crate fyrox;\n# use fyrox::scene::particle_system::{\n# emitter::sphere::SphereEmitter, ParticleSystemBuilder, emitter::Emitter,\n# emitter::base::BaseEmitterBuilder, emitter::sphere::SphereEmitterBuilder\n# };\n# use fyrox::asset::manager::ResourceManager;\n# use fyrox::core::algebra::Vector3;\n# use fyrox::scene::graph::Graph;\n# use fyrox::scene::node::Node;\n# use fyrox::scene::transform::TransformBuilder;\n# use fyrox::core::color_gradient::{GradientPoint, ColorGradient};\n# use fyrox::scene::base::BaseBuilder;\n# use fyrox::core::color::Color;\n# use fyrox::resource::texture::Texture;\n# use std::path::Path;\n# use fyrox::resource::texture::TexturePixelKind;\nfn create_smoke(graph: &mut Graph, resource_manager: &mut ResourceManager, pos: Vector3 ) { ParticleSystemBuilder::new(BaseBuilder::new() .with_lifetime(5.0) .with_local_transform(TransformBuilder::new() .with_local_position(pos) .build())) .with_acceleration(Vector3::new(0.0, 0.0, 0.0)) .with_color_over_lifetime_gradient({ let mut gradient = ColorGradient::new(); gradient.add_point(GradientPoint::new(0.00, Color::from_rgba(150, 150, 150, 0))); gradient.add_point(GradientPoint::new(0.05, Color::from_rgba(150, 150, 150, 220))); gradient.add_point(GradientPoint::new(0.85, Color::from_rgba(255, 255, 255, 180))); gradient.add_point(GradientPoint::new(1.00, Color::from_rgba(255, 255, 255, 0))); gradient }) .with_emitters(vec![ SphereEmitterBuilder::new(BaseEmitterBuilder::new() .with_max_particles(100) .with_spawn_rate(50) .with_x_velocity_range(-0.01..0.01) .with_y_velocity_range(0.02..0.03) .with_z_velocity_range(-0.01..0.01)) .with_radius(0.01) .build() ]) .with_texture(resource_manager.request:: (Path::new(\"data/particles/smoke_04.tga\"))) .build(graph);\n} This code creates smoke effect with smooth dissolving (by using color-over-lifetime gradient). Please refer to API docs for particle system for more information.","breadcrumbs":"Scene » Particle System Node » Using the code","id":"162","title":"Using the code"},"163":{"body":"If you need to create particle systems made in the editor, you can always use prefabs. Create a scene with desired particle system and then instantiate it to your scene.","breadcrumbs":"Scene » Particle System Node » Using prefabs","id":"163","title":"Using prefabs"},"164":{"body":"Fyrox used special technique, called soft particles, that smooths sharp transitions between particles and scene geometry: soft particles This technique especially useful for effects such as smoke, fog, etc. where you don't want to see the \"edge\" between particles and scene geometry. You can tweak this effect using Soft Boundary Sharpness Factor, the larger the value the more \"sharp\" the edge will be and vice versa.","breadcrumbs":"Scene » Particle System Node » Soft particles","id":"164","title":"Soft particles"},"165":{"body":"You can \"rewind\" particle systems in the \"initial\" state by calling particle_system.clear_particles() method, it will remove all generated particles and emission will start over.","breadcrumbs":"Scene » Particle System Node » Restarting emission","id":"165","title":"Restarting emission"},"166":{"body":"By default, every particle system is enabled. Sometimes there is a need to create a particle system, but not enable it (for example for some delayed effect). You can achieve this by calling particle_system.set_enabled(true/false) method. Disabled particle systems will still be drawn, but emission and animation will be stopped. To hide particle system completely, use particle_system.set_visibility(false) method.","breadcrumbs":"Scene » Particle System Node » Enabling or disabling particle systems","id":"166","title":"Enabling or disabling particle systems"},"167":{"body":"Particle systems using special renderer that optimized to draw millions of particles with very low overhead, however particles simulated on CPU side and may significantly impact overall performance when there are many particle systems with lots of particles in each.","breadcrumbs":"Scene » Particle System Node » Performance","id":"167","title":"Performance"},"168":{"body":"Current particle system implementation is not deterministic , this means that the state of the particles will be different at each run of your game. Also you cannot rewind the particle system, nor set a particular position in time. This fact limits potential usages of the particle system, however it is still useful for any effects that does not have to be deterministic, like sparks, smoke, steam, etc. This is a known issue, and it will eventually be fixed by adding a new kind of particle systems. Tracking issue could be found here . Particle systems does not interact with lighting, this means that particles will not be lit by light sources in the scene. The editor still (in 0.27) does not have an ability to edit color-over-lifetime curve, you should set it manually from code after particle system instantiation using respective method .","breadcrumbs":"Scene » Particle System Node » Limitations","id":"168","title":"Limitations"},"169":{"body":"Terrain is a scene node that represents uniform grid of cells where each cell can have different height. Other, commonly known name for terrain is heightmap. Terrains used to create maps for open-world games, it be used to create hills, mountains, plateau, roads, etc. terrain","breadcrumbs":"Scene » Terrain Node » Terrain","id":"169","title":"Terrain"},"17":{"body":"Animation blending state machine - similar to Mecanim in Unity Engine. Animation retargetting - allows you to remap animation from one model to another.","breadcrumbs":"Introduction » Introduction to Fyrox » Animation","id":"17","title":"Animation"},"170":{"body":"There are few basic concepts that you should understand before trying to use terrains. This will help you to understand design decisions and potential use cases.","breadcrumbs":"Scene » Terrain Node » Basic concepts","id":"170","title":"Basic concepts"},"171":{"body":"As it was already mentioned, terrain is a uniform grid where X and Z coordinates of cells have fixed values, while Y can change. In this case we can store only width, height and resolution numerical parameters to calculate X and Z coordinates, while Y is stored in a separate array which is then used to modify heights of cells. Such array is called heightmap . terrain mesh","breadcrumbs":"Scene » Terrain Node » Heightmap","id":"171","title":"Heightmap"},"172":{"body":"Layer is a material + mask applied to terrain's mesh. Mask is a separate, greyscale texture that defines in which parts of the terrain the material should be visible or not. White pixels in the mask makes the material to be visible, black - completely transparent, everything between helps you to create smooth transitions between layers. Here's a simple example of multiple layers: terrain layers layout There are 3 layers: 1 - dirt, 2 - grass, 3 - rocks and grass. As you can see, there are smooth transitions between each layer, it is achieved by layer's mask. Each layer uses separate material, which can be edited from respective property editor in the Inspector: terrain layer material","breadcrumbs":"Scene » Terrain Node » Layers","id":"172","title":"Layers"},"173":{"body":"You can create a terrain node by clicking Create -> Terrain. It will create a terrain with fixed width, height, and resolution (see limitations ). Once the terrain is created, select it in the World Viewer and click on Hill icon on the toolbar. This will enable terrain editing, brush options panel should also appear. See the picture below with all the steps: terrain editing The green rectangle on the terrain under the cursor represents current brush. You can edit brush options in the Brush Options window: brush options You can select a shape (either circle or rectangle with configurable size) and a mode (either modify the height map, or draw on mask of specific layer). When editing terrain's height, left mouse button raises height map, but if Shift key is pressed it lowers it instead. Something similar is applied to the mask editing - left mouse button draws, but if hold Shift - it will erase mask content.","breadcrumbs":"Scene » Terrain Node » Creating terrain in the editor","id":"173","title":"Creating terrain in the editor"},"174":{"body":"Terrain can always be created from code, here's comprehensive example of how to create and modify terrain from code: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::Vector2, algebra::Vector3, parking_lot::Mutex, pool::Handle,\n# sstorage::ImmutableString,\n# },\n# asset::manager::ResourceManager, resource::texture::Texture,\n# material::{shader::SamplerFallback, Material, PropertyValue, SharedMaterial},\n# rand::{thread_rng, Rng},\n# scene::{\n# base::BaseBuilder,\n# graph::Graph,\n# node::Node,\n# terrain::{Brush, BrushMode, BrushShape, Layer, TerrainBuilder},\n# },\n# };\n# use std::sync::Arc;\n# fn setup_layer_material( material: &mut Material, resource_manager: ResourceManager, diffuse_texture: &str, normal_texture: &str,\n) { material .set_property( &ImmutableString::new(\"diffuseTexture\"), PropertyValue::Sampler { value: Some(resource_manager.request:: (diffuse_texture)), fallback: SamplerFallback::White, }, ) .unwrap(); material .set_property( &ImmutableString::new(\"normalTexture\"), PropertyValue::Sampler { value: Some(resource_manager.request:: (normal_texture)), fallback: SamplerFallback::Normal, }, ) .unwrap(); material .set_property( &ImmutableString::new(\"texCoordScale\"), PropertyValue::Vector2(Vector2::new(10.0, 10.0)), ) .unwrap();\n} fn create_random_two_layer_terrain(graph: &mut Graph, resource_manager: &ResourceManager) -> Handle { let terrain = TerrainBuilder::new(BaseBuilder::new()) .with_layers(vec![ Layer { material: { let mut material = Material::standard_terrain(); setup_layer_material( &mut material, resource_manager.clone(), \"examples/data/Grass_DiffuseColor.jpg\", \"examples/data/Grass_NormalColor.jpg\", ); SharedMaterial::new(material) }, .. Default::default() }, Layer { material: { let mut material = Material::standard_terrain(); setup_layer_material( &mut material, resource_manager.clone(), \"examples/data/Rock_DiffuseColor.jpg\", \"examples/data/Rock_Normal.jpg\", ); SharedMaterial::new(material) }, .. Default::default() }, ]) .build(graph); let terrain_ref = graph[terrain].as_terrain_mut(); // Draw something on the terrain. for _ in 0..60 { let x = thread_rng().gen_range(4.0..60.00); let z = thread_rng().gen_range(4.0..60.00); let radius = thread_rng().gen_range(2.0..4.0); let height = thread_rng().gen_range(1.0..3.0); // Pull terrain. terrain_ref.draw(&Brush { center: Vector3::new(x, 0.0, z), shape: BrushShape::Circle { radius }, mode: BrushMode::ModifyHeightMap { amount: height }, }); // Draw rock texture on top. terrain_ref.draw(&Brush { center: Vector3::new(x, 0.0, z), shape: BrushShape::Circle { radius }, mode: BrushMode::DrawOnMask { layer: 1, alpha: 1.0, }, }); } terrain\n} As you can see there is quite a lot of code, ideally you should use editor all the times, because handling everything from code could be very tedious. The result of its execution (if all textures are set correctly) could be something like this (keep in mind that terrain will be random everytime you run the code): terrain from code","breadcrumbs":"Scene » Terrain Node » Creating terrain from code","id":"174","title":"Creating terrain from code"},"175":{"body":"By default, terrains does not have respective physical body and shape, it should be added manually. Create a static rigid body node with a collider with Heightmap shape ( learn more about colliders ). Then attach the terrain to the rigid body. Keep in mind that terrain's origin differs from Heightmap rigid body, so you need to offset the terrain to match its physical representation. Enable physics visualization in editor settings to see physical shapes and move terrain. Now to move the terrain you should move the body, instead of the terrain (because of parent-child relations ).","breadcrumbs":"Scene » Terrain Node » Physics","id":"175","title":"Physics"},"176":{"body":"Terrain rendering complexity have linear dependency with the amount of layers terrain have. Each layer forces the engine to re-render terrain's geometry with different textures and mask. Typical amount of layers is from 4 to 8. For example, a terrain could have the following layers: dirt, grass, rock, snow. This is a relatively lightweight scheme. In any case, you should measure frame time to understand how each new layer affects performance in your case.","breadcrumbs":"Scene » Terrain Node » Performance","id":"176","title":"Performance"},"177":{"body":"Terrain itself does not define any geometry or rendering data, instead it uses one or more chunks for that purpose. Each chunk could be considered as a \"sub-terrain\". You can \"stack\" any amount of chunks from any side of the terrain. To do that, you define a range of chunks along each axes. This is very useful if you need to extend your terrain in a particular direction. Imagine that you've created a terrain with just one chunk (0..1 range on both axes), but suddenly you found that you need to extend the terrain to add some new game locations. In this case you can change the range of chunks at the desired axis. For instance, if you want to add a new location to the right from your single chunk, then you should change width_chunks range to 0..2 and leave length_chunks as is (0..1). This way terrain will be extended, and you can start shaping the new location.","breadcrumbs":"Scene » Terrain Node » Chunking","id":"177","title":"Chunking"},"178":{"body":"Terrain has automatic LOD system, which means that the closest portions of it will be rendered with the highest possible quality (defined by the resolution of height map and masks), while the furthest portions will be rendered with the lowest quality. This effectively balances GPU load and allows you to render huge terrains with low overhead. The main parameter that affects LOD system is block_size (Terrain::set_block_size), which defines size of the patch that will be used for rendering. It is used to divide the size of the height map into a fixed set of blocks using quad-tree algorithm. Current implementation uses modified version of CDLOD algorithm without patch morphing. Apparently it is not needed, since bilinear filtration in vertex shader prevents seams to occur. Current implementation makes it possible to render huge terrains (64x64 km) with 4096x4096 heightmap resolution in about a millisecond on average low-to-middle-end GPU.","breadcrumbs":"Scene » Terrain Node » Level-of-detail","id":"178","title":"Level-of-detail"},"179":{"body":"There is no way to cut holes in the terrain yet, it makes impossible to create caves. There is also no way to create ledges, use separate meshes to imitate this. See tracking issue for more info.","breadcrumbs":"Scene » Terrain Node » Limitations and known issues","id":"179","title":"Limitations and known issues"},"18":{"body":"Advanced asset manager. Fully asynchronous asset loading. PNG, JPG, TGA, DDS, etc. textures. FBX models loader. WAV, OGG sound formats. Compressed textures support (DXT1, DXT3, DTX5).","breadcrumbs":"Introduction » Introduction to Fyrox » Asset management","id":"18","title":"Asset management"},"180":{"body":"Camera is a special scene node that allows you to \"look\" at your scene from any point and with any orientation. Currently, the engine supports only perspective cameras, which could be represented as a frustum volume. Everything that \"intersects\" with the frustum will be rendered. Frustum","breadcrumbs":"Scene » Camera Node » Camera node","id":"180","title":"Camera node"},"181":{"body":"An instance of camera node could be created using CameraBuilder: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{base::BaseBuilder, camera::CameraBuilder, node::Node, Scene},\n# }; fn create_camera(scene: &mut Scene) -> Handle { CameraBuilder::new(BaseBuilder::new()) // Set some properties. .with_fov(80.0f32.to_radians()) .with_z_far(256.0) .build(&mut scene.graph)\n} Orientation and position should be set in BaseBuilder as usual.","breadcrumbs":"Scene » Camera Node » How to create","id":"181","title":"How to create"},"182":{"body":"Projection mode defines how your scene will look like after rendering, there are two projection modes available.","breadcrumbs":"Scene » Camera Node » Projection modes","id":"182","title":"Projection modes"},"183":{"body":"Perspective projection makes distant objects smaller and parallel lines converging when using it, it is the most common projection type for 3D games. By default, each camera uses perspective projection. It's defined by three parameters that describes frustum volume: Field of view angle Near clipping plane location Far clipping plane location Here is a simple example of how to create a camera with perspective projection: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# camera::{CameraBuilder, PerspectiveProjection, Projection},\n# graph::Graph,\n# node::Node,\n# },\n# };\nfn create_perspective_camera(graph: &mut Graph) -> Handle { CameraBuilder::new(BaseBuilder::new()) .with_projection(Projection::Perspective(PerspectiveProjection { // Keep in mind that field of view expressed in radians! fov: 60.0f32.to_radians(), z_near: 0.025, z_far: 1024.0, })) .build(graph)\n}","breadcrumbs":"Scene » Camera Node » Perspective","id":"183","title":"Perspective"},"184":{"body":"Orthographic projection prevents parallel lines from converging, it does not affect object size with distance. If you're making 2D games or isometric 3D games, this is the projection mode you're looking for. Orthographic projection defined by three parameters: Vertical Size Near Clipping Plane Far Clipping Plane Vertical size defines how large the \"box\" will be in vertical axis, horizontal size is derived from vertical size by multiplying vertical size with aspect ratio. Here is a simple example of how to create a camera with orthographic projection: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# camera::{CameraBuilder, OrthographicProjection, Projection},\n# graph::Graph,\n# node::Node,\n# },\n# };\nfn create_perspective_camera(graph: &mut Graph) -> Handle { CameraBuilder::new(BaseBuilder::new()) .with_projection(Projection::Orthographic(OrthographicProjection { vertical_size: 5.0, z_near: 0.025, z_far: 1024.0, })) .build(graph)\n}","breadcrumbs":"Scene » Camera Node » Orthographic","id":"184","title":"Orthographic"},"185":{"body":"Each camera forces engine to re-render scene one more time, which can be very resource-intensive (both CPU and GPU) operation. To reduce GPU load, try to keep the Far Clipping Plane at lowest possible values. For example, if you're making a game with closed environment (lots of corridors, small rooms, etc.) set the Far clipping Plane to max possible distance that can be \"seen\" in your game - if the largest thing is a corridor, then set the Far clipping Plane to slightly exceed the length. This will force the engine to clip everything that is out of bounds and do not draw such objects.","breadcrumbs":"Scene » Camera Node » Performance","id":"185","title":"Performance"},"186":{"body":"Outdoor scenes usually have distant objects that can't be reached, these can be mountains, sky, distant forest, etc. such objects can be pre-rendered and then applied to a huge cube around camera, it always will be rendered first and will be the background of your scene. To create a Skybox and set it to a camera, you can use the following code: # extern crate fyrox;\n# use fyrox::{\n# core::{futures::executor::block_on, pool::Handle},\n# asset::manager::ResourceManager,\n# resource::texture::{Texture, TextureWrapMode},\n# scene::{\n# base::BaseBuilder,\n# camera::{CameraBuilder, SkyBox, SkyBoxBuilder},\n# node::Node,\n# Scene,\n# },\n# }; async fn create_skybox(resource_manager: ResourceManager) -> SkyBox { // Load skybox textures in parallel. let (front, back, left, right, top, bottom) = fyrox::core::futures::join!( resource_manager.request:: (\"path/to/front.jpg\"), resource_manager.request:: (\"path/to/back.jpg\"), resource_manager.request:: (\"path/to/left.jpg\"), resource_manager.request:: (\"path/to/right.jpg\"), resource_manager.request:: (\"path/to/up.jpg\"), resource_manager.request:: (\"path/to/down.jpg\") ); // Unwrap everything. let skybox = SkyBoxBuilder { front: Some(front.unwrap()), back: Some(back.unwrap()), left: Some(left.unwrap()), right: Some(right.unwrap()), top: Some(top.unwrap()), bottom: Some(bottom.unwrap()), } .build() .unwrap(); // Set S and T coordinate wrap mode, ClampToEdge will remove any possible seams on edges // of the skybox. let skybox_texture = skybox.cubemap().unwrap(); let mut data = skybox_texture.data_ref(); data.set_s_wrap_mode(TextureWrapMode::ClampToEdge); data.set_t_wrap_mode(TextureWrapMode::ClampToEdge); skybox\n} fn create_camera(scene: &mut Scene, resource_manager: ResourceManager) -> Handle { CameraBuilder::new(BaseBuilder::new()) .with_skybox(block_on(create_skybox(resource_manager))) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Camera Node » Skybox","id":"186","title":"Skybox"},"187":{"body":"Color grading Look-Up Tables (LUT) allows you to transform color space of your frame. Probably everyone saw the famous \"mexican\" movie effect when everything becomes yellow-ish when action takes place in Mexico, this is done via color grading LUT effect. When used wisely, it can significantly improve perception of your scene. Here is the same scene having no color correction along with another case that has \"mexico\" color correction: Scene Look-up-table No Color Correction Neutral LUT With Color Correction Neutral LUT To use color grading LUT you could do something like this: # extern crate fyrox;\n# use fyrox::{\n# core::{futures::executor::block_on, pool::Handle},\n# asset::manager::ResourceManager, resource::texture::Texture,\n# scene::{\n# base::BaseBuilder,\n# camera::{CameraBuilder, ColorGradingLut},\n# node::Node,\n# Scene,\n# },\n# }; fn create_camera_with_lut( scene: &mut Scene, resource_manager: ResourceManager,\n) -> Handle { CameraBuilder::new(BaseBuilder::new()) .with_color_grading_enabled(true) .with_color_grading_lut( block_on(ColorGradingLut::new( resource_manager.request:: (\"path/to/lut.jpg\"), )) .unwrap(), ) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Camera Node » Color grading look-up tables","id":"187","title":"Color grading look-up tables"},"188":{"body":"In some games you may need to do mouse picking of objects in your scene. To do that, at first you need to somehow convert a point on the screen to ray in the world. Camera has make_ray method exactly for that purpose: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector2, math::ray::Ray},\n# renderer::Renderer,\n# scene::camera::Camera,\n# };\n# fn make_picking_ray(camera: &Camera, point: Vector2 , renderer: &Renderer) -> Ray { camera.make_ray(point, renderer.get_frame_bounds())\n} The ray then can be used to perform a ray cast over physics entities . This is the simplest way of camera picking, and you should prefer it most of the time.","breadcrumbs":"Scene » Camera Node » Picking","id":"188","title":"Picking"},"189":{"body":"Important : The following picking method is for advanced engine users only, if you don't know the math you should not use it. If you know the math and don't want to create physical entities, you can use this ray to perform manual ray intersection check: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::Vector3,\n# algebra::{Matrix4, Point3},\n# math::TriangleDefinition,\n# math::{ray::Ray, Vector3Ext},\n# },\n# scene::node::Node,\n# scene::mesh::{\n# buffer::{VertexAttributeUsage, VertexReadTrait},\n# surface::SurfaceData,\n# Mesh,\n# },\n# };\n# fn read_vertex_position(data: &SurfaceData, i: u32) -> Option > { data.vertex_buffer .get(i as usize) .and_then(|v| v.read_3_f32(VertexAttributeUsage::Position).ok())\n} fn transform_vertex(vertex: Vector3 , transform: &Matrix4 ) -> Vector3 { transform.transform_point(&Point3::from(vertex)).coords\n} fn read_triangle( data: &SurfaceData, triangle: &TriangleDefinition, transform: &Matrix4 ,\n) -> Option<[Vector3 ; 3]> { let a = transform_vertex(read_vertex_position(data, triangle[0])?, transform); let b = transform_vertex(read_vertex_position(data, triangle[1])?, transform); let c = transform_vertex(read_vertex_position(data, triangle[2])?, transform); Some([a, b, c])\n} pub fn precise_ray_test( node: &Node, ray: &Ray, ignore_back_faces: bool,\n) -> Option<(f32, Vector3 )> { let mut closest_distance = f32::MAX; let mut closest_point = None; if let Some(mesh) = node.query_component_ref:: () { let transform = mesh.global_transform(); for surface in mesh.surfaces().iter() { let data = surface.data(); let data = data.lock(); for triangle in data .geometry_buffer .iter() .filter_map(|t| read_triangle(&data, t, &transform)) { if ignore_back_faces { // If normal of the triangle is facing in the same direction as ray's direction, // then we skip such triangle. let normal = (triangle[1] - triangle[0]).cross(&(triangle[2] - triangle[0])); if normal.dot(&ray.dir) >= 0.0 { continue; } } if let Some(pt) = ray.triangle_intersection_point(&triangle) { let distance = ray.origin.sqr_distance(&pt); if distance < closest_distance { closest_distance = distance; closest_point = Some(pt); } } } } } closest_point.map(|pt| (closest_distance, pt))\n} precise_ray_test is what you need, it performs precise intersection check with geometry of a mesh node. It returns a tuple of the closest distance and the closest intersection point.","breadcrumbs":"Scene » Camera Node » Advanced picking","id":"189","title":"Advanced picking"},"19":{"body":"A* pathfinder. Navmesh. Behavior trees.","breadcrumbs":"Introduction » Introduction to Fyrox » Artificial Intelligence (AI)","id":"19","title":"Artificial Intelligence (AI)"},"190":{"body":"(WIP)","breadcrumbs":"Scene » Camera Node » Exposure and HDR","id":"190","title":"Exposure and HDR"},"191":{"body":"Decal nodes allow you to \"project\" a texture onto your scene within some specific bounds. It is widely used for bullet holes, blood splatter, dirt, cracks and so on. Here is the example of the decal applied to the scene: Decal The rust marks are applied on existing geometry of the scene by projecting a rust texture in specific direction.","breadcrumbs":"Scene » Decal Node » Decal node","id":"191","title":"Decal node"},"192":{"body":"A decal instance can be created using DecalBuilder: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::texture::Texture,\n# scene::{base::BaseBuilder, decal::DecalBuilder, node::Node, Scene},\n# }; fn create_decal(scene: &mut Scene, resource_manager: ResourceManager) -> Handle { DecalBuilder::new(BaseBuilder::new()) .with_diffuse_texture(resource_manager.request:: (\"path/to/your/decal.png\")) .build(&mut scene.graph)\n}","breadcrumbs":"Scene » Decal Node » How to create","id":"192","title":"How to create"},"193":{"body":"You can specify which textures the decal will be projecting, currently there is only diffuse and normal maps supported.","breadcrumbs":"Scene » Decal Node » Textures","id":"193","title":"Textures"},"194":{"body":"Currently, the engine supports only deferred decals , which means that decals modify the information stored in G-Buffer. This fact means that decals will be lit correctly with other geometry in the scene. However, if you have some objects in your scene that uses forward rendering path, your decals won't be applied to them.","breadcrumbs":"Scene » Decal Node » Rendering","id":"194","title":"Rendering"},"195":{"body":"Decal uses Object-Oriented Bounding Box (OOB) to determine pixels on which decal's textures will be projected, everything that got into OOB will be covered. Exact bounds can be set by tweaking local transform of a decal. If you want your decal to be larger, set its scale to some large value. To position a decal - use local position, to rotate - local rotation. A decal defines a cube that projects a texture on every pixel of a scene that got into the cube. Exact cube size is defined by decal's local scale. For example, if you have a decal with scale of (1.0, 2.0, 0.1) then the size of the cube (in local coordinates) will be width = 1.0, height = 2.0 and depth = 0.1. The decal can be rotated as any other scene node. Its final size and orientation are defined by the chain of transformations of parent nodes.","breadcrumbs":"Scene » Decal Node » Bounds","id":"195","title":"Bounds"},"196":{"body":"There are situations when you want to prevent some geometry from being covered with a decal, to do that the engine offers a concept of layers. A decal will be applied to a geometry if and only if they have matching layer index. This allows you to create environment damage decals and they won't affect dynamic objects since they're located on different layers.","breadcrumbs":"Scene » Decal Node » Layers","id":"196","title":"Layers"},"197":{"body":"Current implementation of decals is relatively cheap, this allows you to create many decals on scene. However, you should keep the amount of decals at a reasonable level.","breadcrumbs":"Scene » Decal Node » Performance","id":"197","title":"Performance"},"198":{"body":"Rectangle is the simplest \"2D\" node, it can be used to create \"2D\" graphics. 2D is in quotes here because the node is actually a 3D node, like everything else in the engine. Here is an example scene made with the rectangle nodes and an orthographic camera: 2d scene As you can see it is a good basis for 2D games.","breadcrumbs":"Scene » Rectangle Node » Rectangle node","id":"198","title":"Rectangle node"},"199":{"body":"Use the RectangleBuilder to create Rectangle nodes: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, color::Color, pool::Handle},\n# asset::manager::ResourceManager, resource::texture::Texture,\n# scene::{\n# base::BaseBuilder, dim2::rectangle::RectangleBuilder, graph::Graph, node::Node,\n# transform::TransformBuilder,\n# },\n# };\nfn create_rect(graph: &mut Graph, resource_manager: ResourceManager) -> Handle { RectangleBuilder::new( BaseBuilder::new().with_local_transform( TransformBuilder::new() // Size of the rectangle is defined only by scale. .with_local_scale(Vector3::new(0.4, 0.2, 1.0)) .build(), ), ) .with_color(Color::RED) .with_texture(resource_manager.request:: (\"path/to/your_texture.jpg\")) .build(graph)\n}","breadcrumbs":"Scene » Rectangle Node » How to create","id":"199","title":"How to create"},"2":{"body":"Almost every chapter in this book can be read in any order, but we recommend reading Chapters 1, 2, 3 (they're quite small) and then going through Platformer Tutorial (2D) while learning more about specific areas that interest you from the other chapters. There is also a First-Person Shooter Tutorial (3D) , but it is based on framework which considered obsolete, yet it is still very helpful.","breadcrumbs":"About the Book » How to read the book","id":"2","title":"How to read the book"},"20":{"body":"Advanced node-based UI with lots of widgets. More than 32 widgets Powerful layout system. Full TTF/OTF fonts support. Based on message passing. Fully customizable. GAPI-agnostic. OS-agnostic. Button widget. Border widget. Canvas widget. Color picker widget. Color field widget. Check box widget. Decorator widget. Drop-down list widget. Grid widget. Image widget. List view widget. Popup widget. Progress bar widget. Scroll bar widget. Scroll panel widget. Scroll viewer widget. Stack panel widget. Tab control widget. Text widget. Text box widget. Tree widget. Window widget. File browser widget. File selector widget. Docking manager widget. NumericUpDown widget. Vector3 editor widget. Menu widget. Menu item widget. Message box widget. Wrap panel widget. Curve editor widget. User defined widget.","breadcrumbs":"Introduction » Introduction to Fyrox » User Interface (UI)","id":"20","title":"User Interface (UI)"},"200":{"body":"By default, Rectangle node uses entire image for rendering, but for some applications it is not enough. For example, you may want to use sprite sheets to animate your 2D entities. In this case you need to be able to use only portion of an image. It is possible to do by using set_uv_rect method of the Rectangle node. Here's an example of setting right-top quarter of an image to be used by a Rectangle node: # extern crate fyrox;\n# use fyrox::{core::math::Rect, scene::dim2::rectangle::Rectangle};\n# fn set_2nd_quarter_image_portion(rectangle: &mut Rectangle) { rectangle.set_uv_rect(Rect::new( 0.5, // Offset by 50% to the right 0.0, // No need to offset to bottom. 0.5, // Use half (50%) of width and height 0.5, ));\n} Keep in mind that every part of uv rectangle is proportional. For example 0.5 means 50%, 1.5 = 150% and so on. If width or height is exceeding 1.0 and the texture being used is set to Wrapping mode at respective axis, the image will tile across axes.","breadcrumbs":"Scene » Rectangle Node » Specifying image portion for rendering","id":"200","title":"Specifying image portion for rendering"},"201":{"body":"Rectangles use specialized renderer that is heavily optimized to render tons of rectangles at once, so you can use rectangles almost for everything in 2D games.","breadcrumbs":"Scene » Rectangle Node » Performance","id":"201","title":"Performance"},"202":{"body":"Rectangle nodes does not support custom materials - it is a simplified version of a Mesh node that allows you draw a rectangle with a texture and a color. Its main purpose is to be able to start making games as quick as possible without diving too deep into details (shaders, render passes, etc.). You can still create a \"rectangle\" with custom material, use Mesh node with single rectangle surface: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::{Matrix4, Vector3},\n# parking_lot::Mutex,\n# pool::Handle,\n# },\n# material::{Material, SharedMaterial},\n# scene::{\n# base::BaseBuilder,\n# graph::Graph,\n# mesh::{\n# surface::{SurfaceBuilder, SurfaceData, SurfaceSharedData},\n# MeshBuilder, RenderPath,\n# },\n# node::Node,\n# transform::TransformBuilder,\n# },\n# };\n# use std::sync::Arc; fn create_rect_with_custom_material( graph: &mut Graph, material: SharedMaterial,\n) -> Handle { MeshBuilder::new( BaseBuilder::new().with_local_transform( TransformBuilder::new() .with_local_scale(Vector3::new(0.4, 0.2, 1.0)) .build(), ), ) .with_surfaces(vec![SurfaceBuilder::new(SurfaceSharedData::new( SurfaceData::make_quad(&Matrix4::identity()), )) .with_material(material) .build()]) .with_render_path(RenderPath::Forward) .build(graph)\n} This will effectively \"mimic\" the Rectangle node, but will allow you to use the full power of custom shaders. Keep in mind that Mesh nodes will be rendered via Deferred Renderer, while Rectangle nodes rendered with specialized renderer, that might result in some graphical artifacts. Rectangle nodes has limited lighting support, it means that they still will be lit by standard scene lights, but it will be a very simple diffuse lighting without any \"physically correct\" lighting. This is perfectly ok for 95% of 2D games, if you want to add custom lighting then you should use custom shader. Rectangle nodes works well with 2D physics nodes, check 2D physics section of the book for more info.","breadcrumbs":"Scene » Rectangle Node » Limitations","id":"202","title":"Limitations"},"203":{"body":"Sometimes there is a need to have custom scene nodes, it is possible to do, but it requires quite a lot of boilerplate code. # extern crate fyrox;\nuse fyrox::{ core::{ reflect::prelude::*, math::aabb::AxisAlignedBoundingBox, pool::Handle, uuid::{uuid, Uuid}, variable::InheritError, visitor::prelude::*, }, asset::manager::ResourceManager, scene::{ base::Base, node::{Node, NodeTrait}, },\n};\nuse std::ops::{Deref, DerefMut}; #[derive(Clone, Reflect, Visit, Debug)]\npub struct CustomNode { base: Base,\n} impl Deref for CustomNode { type Target = Base; fn deref(&self) -> &Self::Target { &self.base }\n} impl DerefMut for CustomNode { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.base }\n} impl NodeTrait for CustomNode { fyrox::impl_query_component!(); fn local_bounding_box(&self) -> AxisAlignedBoundingBox { self.base.local_bounding_box() } fn world_bounding_box(&self) -> AxisAlignedBoundingBox { self.base.world_bounding_box() } fn id(&self) -> Uuid { // Provide unique id for serialization needs. It must be unique, use https://www.uuidgenerator.net/ // to generate one. uuid!(\"f592e7f7-5e34-4043-9226-407c7457bb48\") }\n} Once the node is defined, you can create is as usual and put in the graph: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{camera::Camera, graph::Graph, node::Node},\n# };\n# type CustomNode = Camera;\n# fn add_custom_node(graph: &mut Graph) -> Handle { graph.add_node(Node::new(CustomNode::default()))\n}","breadcrumbs":"Scene » Custom Node » Custom Scene Node","id":"203","title":"Custom Scene Node"},"204":{"body":"Scene nodes have no access to outer context, this means that you cannot reference any data that is located outside graph easily. You still can define a global variable that will be accessible, but it is considered as a hack and should be avoided. If you want to add custom logic to scene nodes, then you should use scripts instead. Custom nodes are intended for very specific use cases, such as adding \"data sources\" for renderer, etc.","breadcrumbs":"Scene » Custom Node » Limitations","id":"204","title":"Limitations"},"205":{"body":"For now, you cannot create custom nodes from the editor. This will be available in future versions of the engine; when editor plugins will be supported.","breadcrumbs":"Scene » Custom Node » Editor support","id":"205","title":"Editor support"},"206":{"body":"The engine have full-featured physics engine under the hood (Rapier), it helps you to simulate physics in your games. There is first-class support for both 2D and 3D physics. There are three main physics entities in the engine: Rigid Body - responsible for rigid body dynamics simulation, must have at least one collider to be able to interact with other rigid bodies in the world. Collider - responsible for collision detection. Joint - responsible for motion restriction between two rigid bodies. All these entities are ordinary scene nodes, so they can be arranged into any hierarchy in the scene. However there some rules that have to be followed to make physics simulation work as intended: Rigid body node must have at least one direct child Collider node, otherwise rigid body won't interact with other rigid bodies in the world. Joint node must have two direct child rigid bodies, otherwise joint will have no effect.","breadcrumbs":"Scene » Physics » Physics","id":"206","title":"Physics"},"207":{"body":"There is a very few differences between 3D and 2D physics, the most obvious is that 2D physics does simulation only in oXY plane (the plane of the screen). 2D physics has less collider shapes available since some 3D shapes degenerate in 2D, for example cylinder 3D shape in 2D is just a rectangle. There is also lesser amount of joints available in 2D, there is no revolute joint for example. Unlike 3D physics entities, 2D physics entities exist in the separate scene::dim2 module.","breadcrumbs":"Scene » Physics » Differences between 3D and 2D","id":"207","title":"Differences between 3D and 2D"},"208":{"body":"Rigid body node is the one of main physical entities in the engine. Rigid body nodes can be affected by gravity, external forces and other rigid bodies. Use rigid body node everywhere you need natural physical behaviour for your objects.","breadcrumbs":"Scene » Physics » Rigid Body » Rigid body node","id":"208","title":"Rigid body node"},"209":{"body":"Use RigidBodyBuilder to create a rigid body instance: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{\n# base::BaseBuilder,\n# collider::{ColliderBuilder, ColliderShape},\n# graph::Graph,\n# node::Node,\n# rigidbody::RigidBodyBuilder,\n# },\n# };\nfn create_cube_rigid_body(graph: &mut Graph) -> Handle { RigidBodyBuilder::new(BaseBuilder::new().with_children(&[ // Rigid body must have at least one collider ColliderBuilder::new(BaseBuilder::new()) .with_shape(ColliderShape::cuboid(0.5, 0.5, 0.5)) .build(graph), ])) .with_mass(2.0) .with_lin_vel(Vector3::new(0.0, 3.0, 1.0)) .build(graph)\n}","breadcrumbs":"Scene » Physics » Rigid Body » How to create","id":"209","title":"How to create"},"21":{"body":"Advanced physics (thanks to the rapier physics engine) Rigid bodies. Rich set of various colliders. Joints. Ray cast. Many other useful features. 2D support.","breadcrumbs":"Introduction » Introduction to Fyrox » Physics","id":"21","title":"Physics"},"210":{"body":"Rigid body must have at least one collider to participate in simulation properly, multiple colliders can be used to create complex shapes from simple shapes, you can create concave objects this way. Every collider must be a direct child node of a rigid body. In the editor it could look like this: colliders Note that, Box node here is an instance of Rigid Body 2D, and it has Collider 2D as a child and some sprite. This structure (when a rigid body has a collider as a child) is mandatory for physics engine to work correctly! Collider won't work (participate in physical simulation) without a rigid body and a rigid body won't work without a collider. This applied to both 2D and 3D. Keep in mind, that your graphical representation of an object (some node like Mesh, Sprite, etc.) must be attached to a rigid body. Otherwise, the rigid body will move, but the graphical representation won't. You can also arrange it other way around: a graphical node can have rigid body with a collider, but that requires the rigid body to be kinematic. This is used to create hit boxes , or any other things that should have physical representation, but move together with graphical node.","breadcrumbs":"Scene » Physics » Rigid Body » Colliders","id":"210","title":"Colliders"},"211":{"body":"You can apply forces and torque to any rigid body, but only dynamic bodies will be affected. There is two ways of applying force to a rigid body: at center of mass or at particular point at the body: # extern crate fyrox;\n# use fyrox::{core::algebra::Vector3, scene::rigidbody::RigidBody};\nfn apply_force_and_torque(rigid_body: &mut RigidBody) { // Push rigid body forward at the center of mass. rigid_body.apply_force(Vector3::new(0.0, 0.0, 1.0)); // Kick rigid body at the side (this will also make it rotate) rigid_body.apply_force_at_point(Vector3::new(0.0, 0.0, 1.0), Vector3::new(1.0, 0.0, 0.0)); // Turn rigid body around center of mass. rigid_body.apply_torque(Vector3::new(0.0, 3.0, 0.0));\n}","breadcrumbs":"Scene » Physics » Rigid Body » Force and torque","id":"211","title":"Force and torque"},"212":{"body":"Sometimes you may want to have direct control over position/rotation of a rigid body and tell the physics engine to not do simulation for the body. This can be achieved by making the rigid body kinematic : # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{\n# base::BaseBuilder,\n# collider::{ColliderBuilder, ColliderShape},\n# graph::Graph,\n# node::Node,\n# rigidbody::{RigidBodyBuilder, RigidBodyType},\n# },\n# }; fn create_kinematic_rigid_body(graph: &mut Graph) -> Handle { RigidBodyBuilder::new(BaseBuilder::new().with_children(&[ // Rigid body must have at least one collider ColliderBuilder::new(BaseBuilder::new()) .with_shape(ColliderShape::cuboid(0.5, 0.5, 0.5)) .build(graph), ])) .with_body_type(RigidBodyType::KinematicPositionBased) .build(graph)\n}","breadcrumbs":"Scene » Physics » Rigid Body » Kinematic rigid bodies","id":"212","title":"Kinematic rigid bodies"},"213":{"body":"Fast-moving rigid bodies can \"fly through\" other objects (for example a bullet can completely ignore walls if it is moving too fast), this happens because of discrete calculation. This can be fixed by using continuous collision detection, to enable it use either .with_ccd_enabled(state) of RigidBodyBuilder or .set_ccd_enabled(state) of RigidBody.","breadcrumbs":"Scene » Physics » Rigid Body » Continuous collision detection","id":"213","title":"Continuous collision detection"},"214":{"body":"Dominance allows you to set a priority of forces applied to rigid bodies. It defines which rigid body can affect what rigid body, for example you can set the highest dominance for actors and leave dominance of everything else at zero, this way actors will be able to push any other dynamic bodies, but dynamic bodies won't affect actors. This is useful when you don't want your actors be pushed by surrounding objects (like if someone throws a box at an actor, it will stay still if it has higher dominance)","breadcrumbs":"Scene » Physics » Rigid Body » Dominance","id":"214","title":"Dominance"},"215":{"body":"2D rigid bodies have no difference with 3D, except the simulation happens in oXY plane and Z coordinate is ignored.","breadcrumbs":"Scene » Physics » Rigid Body » 2D rigid bodies","id":"215","title":"2D rigid bodies"},"216":{"body":"Collider is a geometrical shape that is used for collision detection, contact manifold generation, etc. Colliders are used in pair with rigid bodies, they make rigid body participate in collisions. Important: Colliders only works in pair with rigid bodies! Colliders won't be used by the engine, unless they're direct children of a rigid body. Read this chapter for more info.","breadcrumbs":"Scene » Physics » Collider » Collider node","id":"216","title":"Collider node"},"217":{"body":"Collider can have almost any shape, the engine offers the following shapes for 3D: Ball - dynamic sphere shape. Cylinder - dynamic cylinder shape. Cone - dynamic cone shape. Cuboid - dynamic box shape. Capsule - dynamic capsule shape. Segment - dynamic segment (\"line\") shape Triangle - simple dynamic triangle shape Triangle mesh - static concave shape, can be used together with any static level geometry (wall, floors, ceilings, anything else) Height field - static height field shape, can be used together with terrains. Polyhedron - dynamic concave shape. Also, there is a similar, but smaller set for 2D (because some shapes degenerate in 2D): Ball - dynamic circle shape. Cuboid - dynamic rectangle shape. Capsule - dynamic capsule shape. Segment - dynamic segment (\"line\") shape. Triangle - dynamic triangle shape. Trimesh - static triangle mesh shape. Heightfield - static height field shape. Dynamic in both lists means that such shapes can be used together with dynamic rigid bodies, they'll correctly handle all collisions and simulation will look as it should. Static means that such shape should be used only with static rigid bodies.","breadcrumbs":"Scene » Physics » Collider » Shapes","id":"217","title":"Shapes"},"218":{"body":"Use ColliderBuilder to create an instance of collider from code with any shape you want. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# collider::{ColliderBuilder, ColliderShape},\n# graph::Graph,\n# node::Node,\n# },\n# };\nfn create_capsule_collider(graph: &mut Graph) -> Handle { ColliderBuilder::new(BaseBuilder::new()) .with_shape(ColliderShape::capsule_y(0.5, 0.2)) .with_friction(1.0) .build(graph)\n} In the editor you can use MainMenu -> Create -> Physics -> Collider, or right-click on a node in World Viewer and select Add Child -> Physics -> Collider. Collider must be direct child of a rigid body, colliders do nothing on their own!","breadcrumbs":"Scene » Physics » Collider » How to create","id":"218","title":"How to create"},"219":{"body":"Sometimes there's a need to prevent collision between various groups of colliders. Fyrox supports bit-wise collision filtering exactly for this purpose. For instance, you may have two groups of colliders: actors and powerups, and you want the actors to completely ignore collisions with powerups (and vice versa). In this case you can set collision groups for actors like so: actors collision groups And set the collision groups for powerups like so: powerups collision groups As you can see, actors and powerups now have separate memberships (read - groups) and filters. This way, the actors will collide with everything, but powerups and vice versa.","breadcrumbs":"Scene » Physics » Collider » Collision filtering","id":"219","title":"Collision filtering"},"22":{"body":"As any other software, Fyrox has its own system requirements that will provide the best user experience. CPU - at least 2 core CPU with 1.5 GHz per each core. The more is better. GPU - any relatively modern GPU with OpenGL 3.3+ support. If the editor fails to start, then it is most likely your video card does not support OpenGL 3.3+. Do not try to run the editor on virtual machines, pretty much all of them have rudimentary support for graphics APIs which won't let you run the editor. RAM - at least 1 Gb of RAM. The more is better. VRAM - at least 256 Mb of video memory. It highly depends on your game.","breadcrumbs":"Introduction » System Requirements and Supported Platforms » System Requirements","id":"22","title":"System Requirements"},"220":{"body":"You can use colliders to simulate hit boxes for your game characters. It can be done by creating a rigid body with KinematicPositionBased type and an appropriate collider as a child node. As the last step you need to attach the body to a bone in your character's model. Here's a quick example from the editor: hitbox As you can see, the rigid body has a capsule collider as a child and the body is attached to the neck bone. The body has KinematicPositionBased type, which will ensure that the body won't be simulated, instead its position will be synchronized with the position of the parent bone. To actually use the hit boxes in your game, you can either use a ray-casting to perform a hit scan or you can use contacts information to fetch the stuff with which a hit box was contacted. See Ray casting chapter of the section.","breadcrumbs":"Scene » Physics » Collider » Using colliders for hit boxes","id":"220","title":"Using colliders for hit boxes"},"221":{"body":"Joint is a configurable link between two rigid bodies, it restricts relative motion of two bodies. Fyrox provides a fixed set of joints that are suitable for various applications. Fixed Joint - hard link between two bodies, it is the same is if two rigid bodies were \"welded\" to each other with a metal rod. Revolute Joint - restricts all translational movement and any rotations around Y and Z axes, but leaves rotation around local X axis free. An example of the joint from real world is a door hinge, it allows the door to rotate around single axis, but not move. Prismatic Joint - restricts all rotations, movement is allowed along single axis (local X of the joint). An example of the joint from real world could be a slider that supports drawers on a table. Ball Joint - restricts all movement, but leaves rotations unrestricted. An example of a ball joint from real world could be human shoulder. 2D joints does not have revolute joints, because it degenerates into ball joint.","breadcrumbs":"Scene » Physics » Joint » Joint","id":"221","title":"Joint"},"222":{"body":"When the joint is created and all bodies are set to it, it uses self global transform and bodies global transforms to calculate local frames for bodies. This process is called binding , it happens once when the joint is created, but can be initiated by moving the joint to some other position by changing local transform of the joint.","breadcrumbs":"Scene » Physics » Joint » Bodies Binding","id":"222","title":"Bodies Binding"},"223":{"body":"To create a joint from code use JointBuilder: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{\n# base::BaseBuilder,\n# graph::Graph,\n# joint::{BallJoint, JointBuilder, JointParams},\n# node::Node,\n# },\n# };\nfn create_joint(graph: &mut Graph, body1: Handle , body2: Handle ) -> Handle { JointBuilder::new(BaseBuilder::new()) .with_body1(body1) .with_body2(body2) .with_params(JointParams::BallJoint(BallJoint { x_limits_enabled: false, x_limits_angles: Default::default(), y_limits_enabled: false, y_limits_angles: Default::default(), z_limits_enabled: false, z_limits_angles: Default::default(), })) .build(graph)\n} Once the joint is created, it will bind given bodies, using the process describe in the above section. To create a joint from editor, use MainMenu -> Create -> Physics -> Joint, select the new joint and find Body1 and Body2 properties. Assign the fields by holding Alt key and drag'n'drop a rigid body to a field. Move the joint to correct position to ensure the binding will happen as intended.","breadcrumbs":"Scene » Physics » Joint » How to create","id":"223","title":"How to create"},"224":{"body":"You can restrict motion on primary joint axis (rotational and translational) by setting a limit to desired axis. Ball Joint have three angular limits, one per rotation around an axis. The angle range is given in radians. Prismatic Joint have only one limit it is maximum linear distance between two bodies along primary joint axis. Revolute Joint have a single angular limit around primary axis. The angle range is given in radians. Fixed Joint does not have any limit setting, because it locks all degrees of freedom.","breadcrumbs":"Scene » Physics » Joint » Limits","id":"224","title":"Limits"},"225":{"body":"Joints can be used to create many game entities, such as doors, chains and rag dolls. The most interesting here is rag doll. It is used to create realistic behaviour for humans and creatures in games. In general, it is a set of rigid bodies, colliders and joints. Where each joint configured to match joints of a creature, for example ball joint could be used for shoulders, revolute joints for knees and elbows.","breadcrumbs":"Scene » Physics » Joint » Usage","id":"225","title":"Usage"},"226":{"body":"Ray casting allows you to query intersections of a ray with rigid bodies in a scene. Typical usage for ray casting is hit-scan weapons (weapons that shoots high-speed projectiles), AI collision avoidance, etc. To query intersections, use physics world instance of a scene graph: # extern crate fyrox;\n# use fyrox::{\n# core::algebra::{Point3, Vector3},\n# scene::graph::{\n# physics::{Intersection, RayCastOptions},\n# Graph,\n# },\n# };\n# fn do_ray_cast(graph: &mut Graph, begin: Vector3 , end: Vector3 ) -> Vec { let mut buffer = Vec::new(); let ray_direction = end - begin; graph.physics.cast_ray( RayCastOptions { ray_origin: Point3::from(begin), ray_direction, max_len: ray_direction.norm(), groups: Default::default(), sort_results: true, }, &mut buffer, ); buffer\n} The function above will return a collection of intersections that are sorted by intersection distance (a distance from beginning of the ray to an intersection point). Each intersection is represented by the following structure: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::{Point3, Vector3},\n# pool::Handle,\n# },\n# scene::{graph::physics::FeatureId, node::Node},\n# };\npub struct Intersection { pub collider: Handle , pub normal: Vector3 , pub position: Point3 , pub feature: FeatureId, pub toi: f32,\n} collider - a handle of the collider with which intersection was detected. To obtain a handle to rigid body, borrow the collider and fetch its parent field: graph[collider].parent(). normal - a normal at the intersection position in world coordinates. position - a position of the intersection in world coordinates. feature - additional data that contains a kind of the feature with which intersection was detected as well as its index. FeatureId::Face might have index that is greater than amount of triangles in a triangle mesh, this means that intersection was detected from \"back\" side of a face. To \"fix\" that index, simply subtract amount of triangles of a triangle mesh from the value. toi - (time of impact) a distance from ray's origin to position.","breadcrumbs":"Scene » Physics » Ray Casting » Ray Casting","id":"226","title":"Ray Casting"},"227":{"body":"As you might've noticed, the function above return Vec which allocates intersections on heap. This is relatively slow and could be sped up a lot by using static array on stack: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# algebra::{Point3, Vector3},\n# arrayvec::ArrayVec,\n# },\n# scene::graph::{\n# physics::{Intersection, RayCastOptions},\n# Graph,\n# },\n# };\n# fn do_static_ray_cast ( graph: &mut Graph, begin: Vector3 , end: Vector3 ,\n) -> ArrayVec { let mut buffer = ArrayVec:: ::new(); let ray_direction = end - begin; graph.physics.cast_ray( RayCastOptions { ray_origin: Point3::from(begin), ray_direction, max_len: ray_direction.norm(), groups: Default::default(), sort_results: true, }, &mut buffer, ); buffer\n} fn usage_example(graph: &mut Graph, begin: Vector3 , end: Vector3 ) { // Fetch first 32 intersections. dbg!(do_static_ray_cast::<32>(graph, begin, end));\n} usage_example shows how to use the do_static_ray_cast function - all you need to do is to specify maximum amount of intersections you're interested in as a generic parameter.","breadcrumbs":"Scene » Physics » Ray Casting » Avoiding unnecessary allocations","id":"227","title":"Avoiding unnecessary allocations"},"228":{"body":"Ragdoll physics is a sort of procedural animation, that allows you to create naturally looking death animations and body physics in general. Ragdoll is just an arbitrary combination of rigid bodies, colliders, joints. Rigid bodies and colliders define physical \"boundaries\" for limbs of your character, while joints restrict relative motion (linear and rotational).","breadcrumbs":"Scene » Physics » Ragdoll » Ragdoll","id":"228","title":"Ragdoll"},"229":{"body":"Creating a ragdoll manually is a very tedious procedure, you need to create rigid bodies and colliders for every body part of your character, place them correctly, adjust their size, etc. Then you need to create a set of joints, that connects body parts, and then setup linear and angular limits. To save time, Fyrox has a special tool called Ragdoll Wizard: ragdoll wizard It can be opened from Utils menu and contains quite a lot of node handle fields that needs to be filled. Thankfully, there's an Autofill button, by pressing which, the wizard will try to find respective bones of the skeleton and put their handles in the respective fields in the wizard. For now, it is configured to work with mixamo skeletons. Other parameters are listed below: Total Mass - total mass of the ragdoll, it will be used to configure masses of rigid bodies of body parts. Use CCD - a flag, that defines whether the continuous collision detection (CCD) for body parts should be used or not. It is advised to keep this flag on, otherwise body parts might get stuck or fall through the floor, leading to \"explosive\" ragdoll behaviour. Can Sleep - a flag, that defines whether the body parts can \"sleep\" or not. Sleep in this case means, that a body part can be excluded from physical simulation if it is not moving for some time. Collision Groups and Solver Groups could be used to configure collision filtering . It is very important in case if your character has a physical capsule, that is used to \"standard\" character physics. In this case body parts must ignore physical capsule (and vice versa), otherwise your ragdoll will \"explode\". After everything is filled in, you can click OK button and if everything is correct, you should see a bunch of new scene nodes in the world viewer, located under a Ragdoll scene node: ragdoll result As you can see, the amount of entities you'd have to create and configure manually is quite high. Keep in mind, that ragdoll wizard can't generate perfect ragdoll, because of lack of information. The generated ragdoll will most likely require some minor tweaks (mostly joint angular limits).","breadcrumbs":"Scene » Physics » Ragdoll » How To Create","id":"229","title":"How To Create"},"23":{"body":"Platform Engine Editor Windows ✅ ✅ Linux ✅ ✅ macOS ✅¹ ✅ WebAssembly ✅ ❌² Android ✅ ❌² ✅ - first-class support ❌ - not supported ¹ - macOS suffers from bad GPU performance on Intel chipsets, M1+ works well. ² - the editor works only on PC, it requires rich filesystem functionality as well as decent threading support.","breadcrumbs":"Introduction » System Requirements and Supported Platforms » Supported Platforms","id":"23","title":"Supported Platforms"},"230":{"body":"There's one video tutorial about ragdoll wizard, it also shows the final results in game:","breadcrumbs":"Scene » Physics » Ragdoll » Video Tutorials","id":"230","title":"Video Tutorials"},"231":{"body":"Fyrox has quite powerful and flexible audio system which will be covered in this chapter. Basic \"building blocks\" are sound sources, sound buffers, audio processing buses with various sound effects, sound context. Read the next chapters to learn more.","breadcrumbs":"Scene » Sound » Sound System","id":"231","title":"Sound System"},"232":{"body":"Audio bus is an audio processing unit that takes audio samples from any number of sound sources and passes them through a chain of effects (zero or more). Processed samples then can be either sent to an audio playback device (speakers, headphones, etc.) or to some other audio bus. There's always one audio bus (primary) that sends its data to an audio playback device, every other audio buses are considered secondary.","breadcrumbs":"Scene » Sound » Audio Bus » Audio Bus","id":"232","title":"Audio Bus"},"233":{"body":"As stated above, any audio bus (except primary), can output its audio samples to some other audio bus (primary or secondary). Such relationship forms an audio bus graph: data flow diagram As you can see, there can be any number of sound sources which attached to the respective audio buses. Each audio bus can have any number of effects (such as lowpass, highpass, etc. filtering; reverb effect and more). Finally, each audio bus is connected to some other audio bus. Such complex audio processing structure allows you to create pretty much any sound environment. For example, you can create an audio bus with a reverb effect, that will represent a huge hangar with lots of echoes. Then you attach all sound sources located in this \"hangar\" to the audio bus and your sound sources will sound more naturally, according to environment.","breadcrumbs":"Scene » Sound » Audio Bus » Graph","id":"233","title":"Graph"},"234":{"body":"Audio bus can have zero or more audio processing effects. The effects applied one after another (see the arrows on the picture above). You can set any of the following effects: Attenuation - changes \"volume\" of input sound samples. Reverb - adds echoes, early and late reflections. Could be used to simulate environment with high reflectivity (hangars, parking lots, etc.) Low Pass Filter - passes all frequencies below the specified cut-off frequency. High Pass Filter - passes all frequencies above the specified cut-off frequency. Band Pass Filter - passes all frequencies in a given range around the specified cut-off frequency. All Pass Filter - shifts phase of the signal by 90 degrees at the specified cut-off frequency. Low Shelf Filter - reduces amplitude of frequencies in a shape like this ̅ _ at the cutoff frequency. High Shelf Filter - reduces amplitude of frequencies in a shape like this _/̅ at the cutoff frequency.","breadcrumbs":"Scene » Sound » Audio Bus » Effects","id":"234","title":"Effects"},"235":{"body":"In the editor, audio bus graph is located in the Audio Context panel: audio context Primary audio bus is located at the left of the panel, every other audio bus is located to the right. Each audio bus (except primary) has a dropdown list (at the bottom), that specifies output audio bus. The list of effect is located in the center; it can be edited in the Inspector (right side of the image). To attach a sound source to an audio bus, select in the scene and find Audio Bus property in the Inspector and set it to the name of desired audio bus.","breadcrumbs":"Scene » Sound » Audio Bus » Editor","id":"235","title":"Editor"},"236":{"body":"In Fyrox, sounds are nodes of type Sound, with all the consequent properties and workflows.","breadcrumbs":"Scene » Sound » Sound Node » Sound","id":"236","title":"Sound"},"237":{"body":"There are two major ways to create sound sources: from the editor and from code.","breadcrumbs":"Scene » Sound » Sound Node » How to create","id":"237","title":"How to create"},"238":{"body":"A sound source could be created from Create menu (or from the same menu by right-clicking on a node in the world viewer): create After the source is created, you can select it and start editing its properties: sound Buffer - a sound buffer resource, that will be used as a source of samples. If it is empty, then no sound will be played. Drag'n'drop a sound resource from the Asset Browser here to assign it to the source. Play Once - a flag, that defines whether the engine should automatically delete the sound source node from the scene when it is finished playing. Could be useful for one-shot sounds. Gain - a numeric value in [0..1] range, that defines total volume of the sound source. Keep in mind, that this value sets the volume in linear scale, while physically-correct approach would be to use logarithmic scale. This will be fixed in future versions. Panning - a numeric value in [-1..1] range, that defines how loud audio channels will be. -1 - all the sound will be routed to the left channel, 1 - to the right channel. This option works only with 2D sounds (whose spatial blend factor is 0.0) Status - a switch with three possible states: Stopped, Playing, Paused. By default, every sound source is in stopped state, do not forget to switch it to the Playing state, otherwise you won't hear anything. Looping - a flag, that defines whether the sound source should be playing infinitely, or not. Looping sound source will never switch their status to Stopped. Pitch - playback speed multiplier. By default, it is 1.0 which means default speed. Max Distance - maximum distance, at which the sound source is affected by distance attenuation (for 3D sounds). By default, it set to max possible value. Lower values could be used to prevent sound source from be silent at certain distance. Rolloff Factor - a numeric value, that defines how fast the volume of the sound source will decay with increasing distance to a listener. Playback Time - desired time from which the playback should start (in seconds). Spatial Blend - a numeric value, that defines blending factor between 2D and 3D sound, where 0.0 - the sound is fully 2D, 1.0 - the sound is fully 3D. By default, the value is 1.0. Audio Bus - a name of an audio bus, that will be used to process the samples from the sound source. By default, it is set to Primary. It should match the name of some audio bus, that will be used in your scene. More info about audio processing could found here .","breadcrumbs":"Scene » Sound » Sound Node » From Editor","id":"238","title":"From Editor"},"239":{"body":"Audio files are loaded using the resource manager: # extern crate fyrox;\n# use fyrox::{engine::Engine, scene::Scene, scene::sound::SoundBuffer};\n# fn build_node(engine: Engine, scene: &mut Scene) {\nlet sound = engine .resource_manager .request:: (\"/path/to/resource.ogg\");\n# } Then, the node is built using the standard builder pattern: # extern crate fyrox;\n# use fyrox::{\n# engine::Engine,\n# scene::{\n# base::BaseBuilder,\n# sound::{SoundBuilder, Status, SoundBuffer},\n# Scene,\n# },\n# };\n# fn build_node(engine: Engine, scene: &mut Scene) {\n# let sound = engine\n# .resource_manager\n# .request:: (\"/path/to/resource.ogg\");\n#\nlet sound_handle = SoundBuilder::new(BaseBuilder::new()) .with_buffer(Some(sound)) .with_status(Status::Playing) .with_play_once(true) .build(&mut scene.graph);\n# } There are a few notable things in the example above. The first is that sounds don't play automatically; in order to do so, we need to invoke .with_status(Status::Playing). The second is that sound nodes are not dropped automatically after playback; dropping it can be performed in two ways. One way is to use the convenient builder API .with_play_once(true); another is to use the graph APIs: # extern crate fyrox;\n# use fyrox::{\n# engine::Engine,\n# scene::{\n# base::BaseBuilder,\n# sound::{SoundBuilder, Status},\n# Scene,\n# },\n# };\n# fn build_node(engine: Engine, scene: &mut Scene) {\nlet sound_handle = SoundBuilder::new(BaseBuilder::new()).build(&mut scene.graph); let sound = scene.graph[sound_handle].as_sound(); if sound.status() == Status::Stopped { scene.graph.remove_node(sound_handle);\n}\n# } If we want to play background music (or anyway a repeated sound), we just set the looping property when building the node: # extern crate fyrox;\n# use fyrox::{\n# engine::Engine,\n# scene::{base::BaseBuilder, sound::SoundBuilder, Scene},\n# };\n# fn build_node(engine: Engine, scene: &mut Scene) {\nSoundBuilder::new(BaseBuilder::new()) .with_looping(true) // etc. .build(&mut scene.graph);\n# } In order to stream large audio files, instead of loading them entirely in memory, the simplest strategy is to create a corresponding .options file, with the following content: ( stream: true\n) If the audio file is called, for example, /path/to/background.ogg, call this /path/to/background.ogg.options.","breadcrumbs":"Scene » Sound » Sound Node » From Code","id":"239","title":"From Code"},"24":{"body":"Let's briefly get over some basic concepts of the engine, there's not much, but all of them are crucial to understand design decisions made in the engine.","breadcrumbs":"Introduction » Basic concepts » Basic concepts","id":"24","title":"Basic concepts"},"240":{"body":"There's no strict separation between 2D and 3D sound sources. The same source could be switched from 2D to 3D (and vice versa) at runtime, by just adjusting Spatial Blend property. Spatial blend factor is a numeric value, that defines blending factor between 2D and 3D sound, where 0.0 - the sound is fully 2D, 1.0 - the sound is fully 3D. By default, the value is 1.0 which makes it 3D. Intermediate values could be used to create \"ambisonic\" sound sources - when the source sounds like it is placed at some position in the world, but some part of it is just 2D and does not depend on positioning.","breadcrumbs":"Scene » Sound » Sound Node » 2D and 3D","id":"240","title":"2D and 3D"},"241":{"body":"It is possible to specify target audio bus to which the sound will output its audio samples. Audio bus is responsible for various audio processing, such as filtering, reverb, etc. To specify output audio bus, just use the set_audio_bus method and set the name of an audio bus.","breadcrumbs":"Scene » Sound » Sound Node » Audio bus","id":"241","title":"Audio bus"},"242":{"body":"Head Related Transfer Function (HRTF for short) is special audio processing technique that improves audio spatialization. By default, sound spatialization is very simple - volume of each audio channel (left and right) changes accordingly to orientation of the listener. While this simple and fast, it does not provide good audio spatialization - sometimes it is hard to tell from which direction the actual sound is coming from. To solve this issue, we can use head-related transfer function. Despite its scary, mathematical name, it is easy to understand what it's doing. Instead of uniformly changing volume of all frequencies of the signal (as the naive spatialization does), it changes them separately for each channel. The exact \"gains\" of each frequency of each channel is depends on the contents of head-related transfer function. This is done for each azimuth and elevation angles, which gives full picture of how audio signal from each direction travels to each ear. HRTF is usually recorded using a head model with ears with a microphone inside each ear. To capture head-related impulse response (time domain) at a fixed distance and angle pair (azimuth and elevation), a very short impulse of sound is produced. Microphones inside each ear records the signal, and then HRIR (time domain) can be converted in HRTF (frequency domain).","breadcrumbs":"Scene » Sound » HRTF » Head Related Transfer Function","id":"242","title":"Head Related Transfer Function"},"243":{"body":"The theory above could be boring, however it is very simple to use HRTF on practice. Pick a HRIR sphere from the database (any of *.bin files) and load it in the Audio Context panel: hrtf Once it is loaded, all sounds in the scene will use the HRTF for rendering. The same can be achieved by code: # extern crate fyrox;\n# use fyrox::scene::{\n# graph::Graph,\n# sound::{self, HrirSphere, HrirSphereResource, HrirSphereResourceExt, HrtfRenderer, Renderer},\n# };\n# fn use_hrtf(graph: &mut Graph) { let hrir_sphere = HrirSphereResource::from_hrir_sphere( HrirSphere::from_file(\"path/to/hrir.bin\", sound::SAMPLE_RATE).unwrap(), \"path/to/hrir.bin\".into()); graph .sound_context .state() .set_renderer(Renderer::HrtfRenderer(HrtfRenderer::new(hrir_sphere)));\n}","breadcrumbs":"Scene » Sound » HRTF » HRTF on practice","id":"243","title":"HRTF on practice"},"244":{"body":"HRTF is heavy. It is 5-6 times slower than the simple spatialization, so use it only on middle-end or high-end hardware. HRTF performance is linearly dependent on the amount of sound sources: the more sound sources use HRTF, the worse performance will be and vice versa.","breadcrumbs":"Scene » Sound » HRTF » Performance","id":"244","title":"Performance"},"245":{"body":"Animation allows you to change properties of scene nodes at runtime using a set of key frames. Animation consists of multiple tracks, where each track is bound to a property of a scene node. A track can animate any numeric properties, starting from numbers (including bool) end ending by 2/3/4 dimensional vectors. Each component (number, x/y/z/w vector components) is stored in a parametric curve (see [crate::core::curve::Curve] docs for more info). Every parametric curve contains zero or more key frames . Graphically this could be represented like so: Timeline v Time > |---------------|------------------------------------> | | Track1 > | node.position | | X curve |..1..........5...........10.......... | Y curve |..2.........-2..................1.... < Curve key frames | Z curve |..1..........9......................4 |_______________| Track2 | node.property | | ............ |..................................... | ............ |..................................... | ............ |..................................... Each key frame is just a real number with interpolation mode. Interpolation mode tells the engine how to calculate intermediate values between key frames. There are three kinds of interpolation used in animations (you can skip \"boring math\" if you want): Constant - intermediate value will be calculated using leftmost value of two. Constant \"interpolation\" is usually used to create step-like behaviour, the most common case is to \"interpolate\" two boolean values. Linear - intermediate value will be calculated using linear interpolation i = left + (right - left) / t, where t = (time_position - left) / (right - left). t is always in 0..1 range. Linear interpolation is usually used to create \"straight\" transitions between two values. Cubic - intermediate value will be calculated using Hermite cubic spline: i = (2t^3 - 3t^2 + 1) * left + (t^3 - 2t^2 + t) * left_tangent + (-2t^3 + 3t^2) * right + (t^3 - t^2) * right_tangent, where t = (time_position - left) / (right - left) (t is always in 0..1 range), left_tangent and right_tangent is usually a tan(angle). Cubic interpolation is usually used to create \"smooth\" transitions between two values.","breadcrumbs":"Scene » Animation » Animation","id":"245","title":"Animation"},"246":{"body":"You can explore animation system capabilities in this web demo . Keep in mind, that it was designed to run on PC and wasn't tested on mobile devices.","breadcrumbs":"Scene » Animation » Web Demo","id":"246","title":"Web Demo"},"247":{"body":"Each track is always bound to a property in a node, either by its name or by a special binding. The name is used to fetch the property using reflection, the special binding is a faster way of fetching built-in properties. It is usually used to animate position, scale and rotation (these are the most common properties available in every scene node).","breadcrumbs":"Scene » Animation » Track binding","id":"247","title":"Track binding"},"248":{"body":"While key frames on the curves can be located at arbitrary position in time, animations usually plays a specific time slice. By default, each animation will play on a given time slice infinitely - it is called animation looping , it works in both playback directions.","breadcrumbs":"Scene » Animation » Time slice and looping","id":"248","title":"Time slice and looping"},"249":{"body":"You can vary playback speed in wide range, by default every animation has playback speed multiplier set to 1.0. The multiplier tells how faster (>1) or slower (<1) the animation needs to be played. Negative speed multiplier values will reverse playback.","breadcrumbs":"Scene » Animation » Speed","id":"249","title":"Speed"},"25":{"body":"The engine uses somewhat classic OOP with composition over inheritance - complex objects in the engine can be constructed using simpler objects.","breadcrumbs":"Introduction » Basic concepts » Classic OOP","id":"25","title":"Classic OOP"},"250":{"body":"Sometimes there's a need to disable/enable an animation or check if it is enabled or not, you can do this by using the pair of respective methods - [Animation::set_enabled] and [Animation::is_enabled].","breadcrumbs":"Scene » Animation » Enabling or disabling animations","id":"250","title":"Enabling or disabling animations"},"251":{"body":"Signal is a named marker on specific time position on the animation timeline. Signal will emit an event if the animation playback time passes signal's position from left-to-right (or vice versa depending on playback direction). Signals are usually used to attach some specific actions to a position in time. For example, you can have a walking animation and you want to emit sounds when character's feet touch ground. In this case you need to add a few signals at times when each foot touches the ground. After that all you need to do is to fetch animation events one-by-one and emit respective sounds. See respective chapter for more info.","breadcrumbs":"Scene » Animation » Signals","id":"251","title":"Signals"},"252":{"body":"Usually, animations are created from the editor or some external tool and then imported in the engine. Before trying the example below, please read the docs for [crate::scene::animation::AnimationPlayer] node, it is much more convenient way of animating other nodes. The node can be created from the editor and you don't even need to write any code. Use the following example code as a guide only if you need to create procedural animations: # extern crate fyrox;\n# use fyrox::{\n# animation::{\n# container::{TrackDataContainer, TrackValueKind},\n# track::Track,\n# value::ValueBinding,\n# Animation,\n# },\n# core::{\n# curve::{Curve, CurveKey, CurveKeyKind},\n# pool::Handle,\n# },\n# scene::{\n# node::Node,\n# base::BaseBuilder,\n# graph::Graph,\n# pivot::PivotBuilder\n# }\n# };\nfn create_animation(node: Handle ) -> Animation { let mut frames_container = TrackDataContainer::new(TrackValueKind::Vector3); // We'll animate only X coordinate (at index 0). frames_container.curves_mut()[0] = Curve::from(vec![ CurveKey::new(0.5, 2.0, CurveKeyKind::Linear), CurveKey::new(0.75, 1.0, CurveKeyKind::Linear), CurveKey::new(1.0, 3.0, CurveKeyKind::Linear), ]); // Create a track that will animated the node using the curve above. let mut track = Track::new(frames_container, ValueBinding::Position); track.set_target(node); // Finally create an animation and set its time slice and turn it on. let mut animation = Animation::default(); animation.add_track(track); animation.set_time_slice(0.0..1.0); animation.set_enabled(true); animation\n}\n// Create a graph with a node.\nlet mut graph = Graph::new();\nlet some_node = PivotBuilder::new(BaseBuilder::new()).build(&mut graph);\n// Create the animation.\nlet mut animation = create_animation(some_node);\n// Emulate some ticks (like it was updated from the main loop of your game).\nfor _ in 0..10 { animation.tick(1.0 / 60.0); animation.pose().apply(&mut graph);\n} The code above creates a simple animation that moves a node along X axis in various ways. The usage of the animation is only for the sake of completeness of the example. In the real games you need to add the animation to an animation player scene node, and it will do the job for you.","breadcrumbs":"Scene » Animation » Creating From Code","id":"252","title":"Creating From Code"},"253":{"body":"It is also possible to import an animation from external source (such as FBX files). You can do this in two major ways: from code or from the editor. The following sections shows how to use both ways.","breadcrumbs":"Scene » Animation » Importing","id":"253","title":"Importing"},"254":{"body":"At first, make sure that you have your 3D model instantiated in the scene. The following example has agent.fbx instance in the scene (to do that, just drag'n'drop your 3D model in the scene from the Asset Browser). To import an animation you need to create an Animation Player scene node, open the Animation Editor and click the button with arrow-down icon: Step 1 Now you need to pick the root node of your 3D model to which you'll import your animation. Usually it will be called the same as your 3D model (agent.fbx on the screenshot below): Step 2 The last thing you need to do is to pick the animation you want to import: Step 3 If everything is correct, you can preview your animation by clicking Preview checkbox: Step 4","breadcrumbs":"Scene » Animation » From Editor","id":"254","title":"From Editor"},"255":{"body":"You can do the same as in the previous section, but from code: # extern crate fyrox;\n# use fyrox::{\n# asset::manager::ResourceManager,\n# core::pool::Handle,\n# resource::model::{Model, ModelResourceExtension},\n# scene::{animation::AnimationPlayerBuilder, base::BaseBuilder, node::Node, Scene},\n# };\n# async fn create_animated_character( scene: &mut Scene, resource_manager: &ResourceManager,\n) -> (Handle , Handle ) { // Load a character model first. let character_resource = resource_manager .request:: (\"path/to/my/character.fbx\") .await .unwrap(); // Create its instance. let character_instance = character_resource.instantiate(scene); // Create a new animation player. let animation_player = AnimationPlayerBuilder::new(BaseBuilder::new()).build(&mut scene.graph); // Load an animation. let animation_resource = resource_manager .request:: (\"path/to/my/animation.fbx\") .await .unwrap(); // \"Instantiate\" an animation from the animation resource to the animation player. // You can call this method multiple times with different animations, each time it // will create a new animation instance and put it in the animation player. let _animations = animation_resource.retarget_animations_to_player( character_instance, animation_player, &mut scene.graph, ); (character_instance, animation_player)\n} As you can see, at first this code creates an instance of a 3D model. Then it loads an animation and creates its instance in the animation player. Please note, that this code uses async, which produces a future which should be driven by some executor. You can use block_on method to execute it at call site (this won't work on WebAssembly). It is advised to prefer the editor to code approach, because it hides all this tedious code and properly handles asynchronous loading on all platforms.","breadcrumbs":"Scene » Animation » From Code","id":"255","title":"From Code"},"256":{"body":"Animations will be played automatically if the respective animation player is has the property Auto Apply set to true. Since the animation player can contain multiple animations, all of them will be played at once. You can enable/disable animations when needed by finding them by name from code and switching Enabled property: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{animation::AnimationPlayer, graph::Graph, node::Node},\n# };\n# fn enable_animation( animation_player: Handle , graph: &mut Graph, name: &str, enabled: bool,\n) { if let Some(animation_player) = graph.try_get_mut_of_type:: (animation_player) { // `get_value_mut_silent` prevents marking the variable as modified (see Property Inheritance // chapter for more info). let animations = animation_player.animations_mut().get_value_mut_silent(); // Find an animation with the given name. if let Some((_animation_handle, animation)) = animations.find_by_name_mut(name) { // You could also store _animation_handle somewhere and use animations.get_mut/get(handle) // to fetch an animation faster. // Turn the animation on/off. animation.set_enabled(enabled); } }\n} This code could also be used to change animation properties at runtime. To do that, replace set_enabled with some other methods, such as set_speed, set_loop, set_root_motion_settings etc.","breadcrumbs":"Scene » Animation » Playing an Animation","id":"256","title":"Playing an Animation"},"257":{"body":"anim editor Animation Editor is a tool that helps you to create and preview animations. This is a powerful tool that can be used to animate pretty much any numeric property. It has three main parts: Toolbar - contains a set of tools that changes a particular part of an animation (name, length, speed, etc.) Track List - contains a list of tracks of nodes that will be animated. Curve Editor - curve editor allows you to edit behaviour of a numeric parameter over the time. The editor can be opened in two ways - using Utils -> Animation Editor or by selecting an animation player node and clicking Open Animation Editor button in the inspector. open1 open2 In both ways you still need to select an animation player for editing.","breadcrumbs":"Scene » Animation » Animation Editor » Animation Editor","id":"257","title":"Animation Editor"},"258":{"body":"At first, you need to create or import an animation, then you need to set its time slice to desired range (see Time Slice in the section below), then you need to add a few tracks for desired properties and finally add some keys. You can preview the results at any time, keep in mind that any attempt to change an animation while it is the preview mode, will revert every change from the preview mode and only then apply your change.","breadcrumbs":"Scene » Animation » Animation Editor » Typical Workflow","id":"258","title":"Typical Workflow"},"259":{"body":"The toolbar contains a set of tools that changes a particular part of an animation (name, length, speed, etc.). It looks like this: toolbar Animation Name - name of a currently selected animation. Add Animation - adds a new empty animation with the name from the text box at the left to the animation player. Import Animation - starts animation importing process. See Animation Importing section for more info. Reimport Animation - re-imports the animation from an external file, it is useful if you need to change animation's content, while keep references to it valid. Rename Animation - renames a currently selected animation using the name from the text box at the left. Animation Selector - allows you to switch currently edited animation. Delete Animation - deletes a currently selected animation, tries to select last animation from the list if possible. Duplicate Animation - clones a currently selected animation. Loop Animation - enables or disables looping of a currently selected animation. Enable Animation - enables or disables a currently selected animation. Animation Speed - sets a new playback speed of a currently selected animation. Time Slice - a time range (in seconds) which defines start and end time of a currently selected animation. The range is highlighted in the curve editor. Root Motion - open root motion settings. See Root Motion section for more info. Preview Switch - enables or disables animation preview. See Preview Mode section for more info. Play/Pause - plays or pauses a currently selected animation (allowed only in the preview mode). Stop - stops a currently selected animation (allowed only in the preview mode).","breadcrumbs":"Scene » Animation » Animation Editor » Toolbar","id":"259","title":"Toolbar"},"26":{"body":"In Fyrox, you break down your game in a set of reusable scenes. Pretty much anything can be a scene: a player, a weapon, a bot, level parts, etc. Scenes can be nested one into another, this helps you to break down complex scenes into reusable parts. Scene in Fyrox is also plays a role of prefab, there's pretty much no difference between them.","breadcrumbs":"Introduction » Basic concepts » Scenes","id":"26","title":"Scenes"},"260":{"body":"The track list contains a list of tracks of nodes that will be animated. It looks like this: track list Filter Bar - filters the track list by finding tracks whose names matching the filter. You can use this to find tracks that belong to a particular scene node. Clear Filter - clears the filter, the track list will show all the tracks after this. Collapse All - collapses all the tracks in the list. Expand All - expands all the tracks in the list. Track - a track with some number of children parametric curves. Track Component Curve - parametric curve that serves a data source for the animation for a particular track. Track Switch - enables or disables a track; disabled tracks won't \"touch\" their properties. Add Track - starts property binding process, see Property Binding section for more info.","breadcrumbs":"Scene » Animation » Animation Editor » Track List","id":"260","title":"Track List"},"261":{"body":"context menu Remove Selected Tracks - removes selected tracks; you can remove multiple tracks at a time by selecting them while holding Ctrl.","breadcrumbs":"Scene » Animation » Animation Editor » Track Context Menu","id":"261","title":"Track Context Menu"},"262":{"body":"Curve editor allows you to edit parametric curves (one at a time). A curve consists of zero or more key frames with various transition rules between current and the next. The editor looks like this: curve editor Time Ruler - shows time values and every signal of a currently selected animation. A click on the time ruler will move the playback cursor at the click position. You can move it by clicking at the cursor and moving the mouse while holding the left mouse button. Animation signals can be moved in the same fashion. Parametric Curve - a curve that defines how a value changes over time. Time Thumb - animation playback cursor, useful only for preview. Animation Signal - some animation signal that will produce animation events when the playback cursor passes it.","breadcrumbs":"Scene » Animation » Animation Editor » Curve Editor","id":"262","title":"Curve Editor"},"263":{"body":"time ruler context menu Remove Signal - removes an animation signal under the mouse cursor. Add Signal - adds a new animation signal at the mouse cursor position.","breadcrumbs":"Scene » Animation » Animation Editor » Time Ruler Context Menu","id":"263","title":"Time Ruler Context Menu"},"264":{"body":"key frame context menu Location - shows a key location and allows you to change it. Useful for setting precise values. Value - shows a key value and allows you to change it. Useful for setting precise values. Add Key - adds a new key to the curve. Remove - removes all selected keys. You can select multiple keys either by box selection (click and drag the mouse to active box selection) or by clicking on separate keys while holding Ctrl. Key... - allows you to change the interpolation type of key. It could be one of the following values: Constant, Linear, Cubic. Zoom To Fit - tries to find zooming values (for both axes) and the view position with which the entire curve fits in the viewport.","breadcrumbs":"Scene » Animation » Animation Editor » Key Frame Context Menu","id":"264","title":"Key Frame Context Menu"},"265":{"body":"To animate a property all you need to do is to click on Add Track... button at the bottom of the track list, select a node to animate and then select a property that will be animated. There are two windows that will be shown one after another: step1 step2 You can cancel property binding at any time by clicking Cancel in any of the windows. Keep in mind that you can animate only numeric properties, so not every property is shown in the window.","breadcrumbs":"Scene » Animation » Animation Editor » Property Binding","id":"265","title":"Property Binding"},"266":{"body":"Animations can be stored in separate files, but the engine requires all of them to be in a single Animation Player. To put an animation from an external resource (an FBX, for instance) in the animation player you can use animation importing. To do that, click on animation import icon and then select a root node of the hierarchy that is animated in the external animation file, then select the animation file and click Ok. The engine will try to import the animation and map it to the given hierarchy, mapping is done using node names, so animated node names must match in both your scene and your external animation file. step1 step2 Content of existing animations can be replaced by reimporting. Click on a button with two circular arrows to reimport your animation. It could be useful if you changed your animation in some external editor (Blender for example) and want to apply changes in your game.","breadcrumbs":"Scene » Animation » Animation Editor » Animation Importing","id":"266","title":"Animation Importing"},"267":{"body":"Preview mode helps you to see and debug your animation. After activating the mode, you need to play the animation by clicking the Play/Pause button: anim editor Any significant change made in the scene will automatically deactivate the preview mode reverting all the changes made by playing animation.","breadcrumbs":"Scene » Animation » Animation Editor » Preview Mode","id":"267","title":"Preview Mode"},"268":{"body":"See Root Motion chapter for more info.","breadcrumbs":"Scene » Animation » Animation Editor » Root Motion","id":"268","title":"Root Motion"},"269":{"body":"For now there's no dopesheet mode in the editor, you can edit only one numeric parameter at a time. Also, there's no capture mode - this is a special mode in which the editor automatically adds your changes in the scene to the animation. These limitations will be removed in the future versions.","breadcrumbs":"Scene » Animation » Animation Editor » Limitations","id":"269","title":"Limitations"},"27":{"body":"A scene is made of one or more nodes (every scene must have at least one root node, to which everything else is attached). Scene node contains specific set of properties as well as one optional script instance which is responsible for custom game logic. Typical structure of a scene node could be represented by the following example. The base object for every scene node is a Base node, it contains a transform, a list of children, etc. A more complex node, that extends functionality of the Base node stores an instance of Base inside of them. For example, a Mesh node is a Base node plus some specific info (a list of surfaces, material, etc.). The \"hierarchy\" depth is unlimited - a Light node in the engine is an enumeration of three possible types of light source. Directional, Point, and Spot light sources both use BaseLight node, which in its turn contains Base node inside. Graphically it can be represented like so: `Point`\n|__ Point Light Properties (radius, etc.)\n|__`BaseLight` |__ Base Light Properties (color, etc.) |__`Base` |__ Base Node Properties (transform, children nodes, etc.) As you can see, this forms the nice tree (graph) that shows what the object contains. This is very natural way of describing scene nodes, it gives you the full power of building an object of any complexity.","breadcrumbs":"Introduction » Basic concepts » Nodes and Scene Graph","id":"27","title":"Nodes and Scene Graph"},"270":{"body":"Animation blending is a powerful feature that allows you to mix multiple animations into one. Each animation is mixed with a various weights which in sum gives 1.0 (100%). By having opposite coefficients (k1 = 0 -> 1, k2 = 1 -> 0) changing in time it is possible to create transition effect. Handling transitions with all the coefficients is a routine job, the engine can handle it for you giving you some nice features: Multiple states with smooth transitions between them Ability to blend multiple animations in one and use it as pose source for blending Ability to specify a set of variables that will be used as blending coefficients and transition rules. All these features consolidated in so-called animation blending state machine (ABSM). Machine is used to blend multiple animation as well as perform automatic \"smooth\" transition between states. In general, ABSM could be represented like this: ABSM Structure At the first look it may seem very complicated, but in reality it uses quite simple techniques. Let's start from the left side of the picture and go to the right. Yellow rectangle at the left depicts an animation player node that contains a bunch of animations, that will be used for blending. Two center blocks (layer 0 and layer 1) depicts separate layers (ABSM could have any number of layers in it). Each layer can contain an arbitrary nodes (green shapes), states (blue shapes), transitions (thick yellow arrows). Nodes serves as a source of poses, that can be blended in any desired way. States are the part of the inner state machine, only one state could be active at the same time. Transitions are used to specify state transition rules. At the \"exit\" of each layer there's a layer filter, it is responsible for filtering out values for specific scene nodes and could be used to prevent some scene nodes from being animated by a certain layer. Please note that despite the look of it, layer filter not necessarily be applied after all animations and states are blended - it could be done at any moment and drawn like so only for simplicity reasons. The last, but not the least, important thing on the picture is the parameters container on the right side of the picture. Parameter either a transition rule, blending weight, or sampling point. If you look closely at the transitions or animation blending nodes you'll see small text marks. This is the names of the respective parameters. In general, any state machine works like this - ABSM nodes are used to blend or fetch animations and their resulting poses are used by ABSM states. Active state provides final pose, which is then passes filtering and returned to you. After the last stage, you can apply the pose to a scene graph to make the resulting animation to have effect.","breadcrumbs":"Scene » Animation » Animation Blending » Animation Blending","id":"270","title":"Animation Blending"},"271":{"body":"As always, there are two major ways of creating things in Fyrox - from the editor or from code. Take your pick.","breadcrumbs":"Scene » Animation » Animation Blending » How to create","id":"271","title":"How to create"},"272":{"body":"Use ABSM Editor for to create animation blending state machines.","breadcrumbs":"Scene » Animation » Animation Blending » From editor","id":"272","title":"From editor"},"273":{"body":"You can always create an ABSM from code, a simple ABSM could be created like this: # extern crate fyrox;\nuse fyrox::{ animation::machine::{ Machine, State, Transition, PoseNode, node::blend::BlendPose, Parameter, PlayAnimation, PoseWeight, node::blend::BlendAnimations }, core::pool::Handle\n}; // Assume that these are correct handles.\nlet idle_animation = Handle::default();\nlet walk_animation = Handle::default();\nlet aim_animation = Handle::default(); let mut machine = Machine::new(); let root_layer = machine.layers_mut().first_mut().unwrap(); let aim = root_layer.add_node(PoseNode::PlayAnimation(PlayAnimation::new(aim_animation)));\nlet walk = root_layer.add_node(PoseNode::PlayAnimation(PlayAnimation::new(walk_animation))); // Blend two animations together\nlet blend_aim_walk = root_layer.add_node(PoseNode::BlendAnimations( BlendAnimations::new(vec![ BlendPose::new(PoseWeight::Constant(0.75), aim), BlendPose::new(PoseWeight::Constant(0.25), walk) ])\n)); let walk_state = root_layer.add_state(State::new(\"Walk\", blend_aim_walk)); let idle = root_layer.add_node(PoseNode::PlayAnimation(PlayAnimation::new(idle_animation)));\nlet idle_state = root_layer.add_state(State::new(\"Idle\", idle)); root_layer.add_transition(Transition::new(\"Walk->Idle\", walk_state, idle_state, 1.0, \"WalkToIdle\"));\nroot_layer.add_transition(Transition::new(\"Idle->Walk\", idle_state, walk_state, 1.0, \"IdleToWalk\")); Here we have Walk, Idle and Run states which use different sources of poses: Walk - is the most complicated here - it uses result of blending between Aim and Walk animations with different weights. This is useful if your character can only walk or can walk and aim at the same time. Desired pose determined by Walk Weight and Aim Weight parameters combination. Run and idle both directly use animation as pose source. There are four transitions between three states each with its own rule. Rule is just a boolean parameter that indicates that transition should be activated. Let's look at the code example of the above state graph: As you can see, everything is quite straightforward. Even such simple state machine requires quite a lot of code, which can be removed by using ABSM editor. Read the next chapter to learn about it.","breadcrumbs":"Scene » Animation » Animation Blending » From code","id":"273","title":"From code"},"274":{"body":"While it is possible to create and manage animation blending and state manually from code, it quickly becomes too annoying and hardly manageable. To help you create and manage blending machines in easy way, the engine offers an ABSM Editor tool. This chapter is an overview of the editor, it is quite complex, but the guide should help you to figure out which part is made for what. Next chapter will help you to create your first animation blending state machine. absm editor The editor has four main parts (panels): Toolbar - contains a set of tools to edit animation layers and enable/disable preview mode. See Toolbar section for more info. Parameters - allows you to edit various variables that are responsible for transitions, weight parameters for blending, etc. See Parameters section for more info. State Graph - allows you to create, delete, edit states and transition between them. See State Graph section for more info. State Viewer - allows you to edit pose source for a state. Pose source can be represented either by a single node that plays an animation, or a series of play animation nodes connected to blending nodes (which can be connected to other blending nodes, etc.). See State Viewer section for more info. The editor can be opened in two ways - using Utils -> ABSM Editor or by selecting an animation blending state machine node and clicking Open ABSM Editor... button: open1 open1 In both ways you still need to select an an animation blending state machine node for editing.","breadcrumbs":"Scene » Animation » ABSM Editor » Animation Blending State Machine (ABSM) Editor","id":"274","title":"Animation Blending State Machine (ABSM) Editor"},"275":{"body":"toolbar Preview Switch - enables or disables preview mode for the ABSM. See Preview Mode section for more info. Layer Name - name of the selected layer. Type a new name here to rename currently selected layer (hit enter or just click elsewhere to rename). Add Layer - adds a new layer with the name in the Layer Name text box to the ABSM. ABSM can have multiple layers with the same name, but it strongly advised to set unique names here. Remove Current Layer - removes currently selected layer. You can delete all layers, but in this case your ABSM won't have any effect. Layer Selector - allows you to select a layer for editing, default selection is none. Layer Mask - opens a Layer Mask Editor and helps you to edit the layer mask of the current layer. See Layer Mask section for more info.","breadcrumbs":"Scene » Animation » ABSM Editor » Toolbar","id":"275","title":"Toolbar"},"276":{"body":"Parameter is a named and typed variable that provides the animation system with some data required for it to work. There are only three type of parameters: Rule - boolean value that used as a trigger for transitions. When transition is using some rule, it checks the value of the parameter and if it is true transition starts. Weight - real number (f32) that is used a weight when you're blending multiple animations into one. Index - natural number (i32) that is used as an animation selector. parameters Add Parameters - adds a new parameter to the parameters' container. Remove a Parameter - removes selected parameter from the parameters' container. Parameter Name - allows you to set a parameter name. Parameter Type - allows you to select the type of the parameter. Parameter Value - allows you to set parameter value.","breadcrumbs":"Scene » Animation » ABSM Editor » Parameters","id":"276","title":"Parameters"},"277":{"body":"State Graph allows you to create states and transitions between them. state graph State - state is final animation for a set of scene nodes, only one state can be active at a time. Transition - is an ordered connection between two states, it defines how much time it needed to perform blending of two states. Root State - is an entry state of the current layer.","breadcrumbs":"Scene » Animation » ABSM Editor » State Graph","id":"277","title":"State Graph"},"278":{"body":"state context menu Create Transition - starts transition creation from the current state to some other. Remove - removes the state. Set As Entry State - marks the state as an entry state (this state will be active at beginning).","breadcrumbs":"Scene » Animation » ABSM Editor » State Context Menu","id":"278","title":"State Context Menu"},"279":{"body":"transition context menu Remove Transition - removes selected transition.","breadcrumbs":"Scene » Animation » ABSM Editor » Transition Context Menu","id":"279","title":"Transition Context Menu"},"28":{"body":"Plugin is a container for \"global\" game data and logic, its main usage is to provide scripts with some data and to manage global game state.","breadcrumbs":"Introduction » Basic concepts » Plugins","id":"28","title":"Plugins"},"280":{"body":"Select a State node to edit the following properties: state properties Position - is a location of the state on the canvas. Name - name of the state. Root - handle of the backing animation node inside the state.","breadcrumbs":"Scene » Animation » ABSM Editor » State Properties","id":"280","title":"State Properties"},"281":{"body":"Select a Transition node to edit the following properties: transition properties Name - name of the state. Transition Time - amount of time for blending between two states (in seconds). Elapsed Time - starting amount of blending time. Source - handle of a source state. Desc - handle of a destination state. Rule - a name of Rule type parameter that defines whether the transition can be activated or not. Invert Rule - defines whether to invert the value of Rule or not. Blend Factor - defines a percentage (in 0..1 range) of how much transition was active.","breadcrumbs":"Scene » Animation » ABSM Editor » Transition Properties","id":"281","title":"Transition Properties"},"282":{"body":"State Viewer allows you to edit contents of states. You can create animation blending chains of any complexity, the simplest content of a state is just a single Play Animation node. Currently, the engine supports just three animation blending nodes: Play Animation - takes animation pose directly from specified animation, does nothing to it. Blend Animations - takes multiple animation poses from respective animations and blends them together with respective blend weights. Blend Animations By Index - takes multiple animation poses from respective animations and switches between them with \"smooth\" transition using an index parameter. state viewer Node - is a source of animation for blending. Connection - defines how nodes are connected to each other. To create a new connection, click on a small dot on a node, hold the button and start dragging to a dot on some other node. Root Node - root node is marked green; root node is a final source of animation for the parent state.","breadcrumbs":"Scene » Animation » ABSM Editor » State Viewer","id":"282","title":"State Viewer"},"283":{"body":"Select a Play Animation node to edit the following properties: play animation properties Position - is a location of the node on the canvas. Animation - an animation to fetch the pose from.","breadcrumbs":"Scene » Animation » ABSM Editor » Play Animation Properties","id":"283","title":"Play Animation Properties"},"284":{"body":"Select a Blend Animations node to edit the following properties: blend animations properties Position - is a location of the node on the canvas. Pose Sources - a set of input poses. To add a pose either click on + or +Input on the node itself. Don't forget to connect some nodes to the new input poses. Weight - a weight of the pose; could be either a constant value or some parameter.","breadcrumbs":"Scene » Animation » ABSM Editor » Blend Animations Properties","id":"284","title":"Blend Animations Properties"},"285":{"body":"Select a Blend Animations By Index node to edit the following properties: blend animations by index properties Position - is a location of the node on the canvas. Index Parameter - a name of an indexing parameter (must be Index type). Inputs - a set of input poses. To add a pose either click on + or +Input on the node itself. Don't forget to connect some nodes to the new input poses. Blend Time - defines how much time is needed to transition to the pose.","breadcrumbs":"Scene » Animation » ABSM Editor » Blend Animations By Index Properties","id":"285","title":"Blend Animations By Index Properties"},"286":{"body":"Every connection has a context menu that can be shown by a right-click on a connection. connection context menu Remove Connection - removes the connection between parent nodes.","breadcrumbs":"Scene » Animation » ABSM Editor » Connection Context Menu","id":"286","title":"Connection Context Menu"},"287":{"body":"Every node has a context menu that can be shown by a right-click on a connection. node context menu Set As Root - sets the node as the final pose source of the parent state. Remove - removes the node from the state.","breadcrumbs":"Scene » Animation » ABSM Editor » Node Context Menu","id":"287","title":"Node Context Menu"},"288":{"body":"layer mask Layer mask editor allows you to select which nodes won't be animated by the current animation layer. Selected nodes are marked with dark color. To select multiple nodes at once, hold Ctrl and click on items. The text box at the top of the window allows you to search for a particular scene node. To save edited layer mask click OK.","breadcrumbs":"Scene » Animation » ABSM Editor » Layer Mask","id":"288","title":"Layer Mask"},"289":{"body":"Preview mode turns on the animation blending state machine and its animation player and allows you to see the result of the work of the machine. Any significant changes in the scene automatically disables the preview mode and any changes done by the machine is discarded. While the preview mode is active, you can freely change the values of the parameters to see how the machine will react to this. This helps you to debug your state machine, it is especially useful for complex state machines with lots of layers. Here's how the preview mode works: absm","breadcrumbs":"Scene » Animation » ABSM Editor » Preview Mode","id":"289","title":"Preview Mode"},"29":{"body":"Script - is a separate piece of data and logic, that can be attached to scene nodes. This is primary (but not single) way of adding custom game logic.","breadcrumbs":"Introduction » Basic concepts » Scripts","id":"29","title":"Scripts"},"290":{"body":"In some cases you may need to perform an action when at certain time of your animation. It could be a footstep sound, when foot touches ground, grenade tossing, etc. This could be done via animation signals. Animation signal is just a named marker that has time position at an animation timeline. It will be emitted when animation playback time passes it (left-to-right or right-to-left depending on the actual speed of your animation). All you need to do, is to catch these signals in your game code and do the desired actions.","breadcrumbs":"Scene » Animation » Signals » Signals","id":"290","title":"Signals"},"291":{"body":"As usual, there are two possible ways of adding animation signals - from the animation editor and from code.","breadcrumbs":"Scene » Animation » Signals » How to add","id":"291","title":"How to add"},"292":{"body":"To add a signal to some animation, select an animation player, open the animation editor, select some animation in it. Now all you need to do is to right-click on the timeline and press Add Signal. Add Signal After the signal is added, you can select it and edit its properties in the inspector. Also, you can drag it on the timeline to adjust its position. Edit Signal Set a meaningful name to the signal, and it is pretty much done - all you need to do next is to write signal handling code in your game. See the next section to learn how to do it.","breadcrumbs":"Scene » Animation » Signals » From animation editor","id":"292","title":"From animation editor"},"293":{"body":"A signal could also be added from code, this requires knowing a handle of your animation player and a name/handle of your animation. Please note the comment about signal's uuid in the code below. # extern crate fyrox;\n# use fyrox::{\n# animation::AnimationSignal,\n# core::{pool::Handle, uuid::uuid},\n# scene::{animation::AnimationPlayer, graph::Graph, node::Node},\n# };\n# fn add_signal( animation_player: Handle , animation_name: &str, signal_name: &str, graph: &mut Graph,\n) { if let Some(animation_player) = graph.try_get_mut_of_type:: (animation_player) { let animations = animation_player.animations_mut().get_value_mut_silent(); if let Some((_, animation)) = animations.find_by_name_mut(animation_name) { // This uuid should be unique, you could also use Uuid::new_v4() method, but it // will generate random uuid on every call. This uuid does not used by the engine, // it is used only for searching and useful when you have multiple signals with the // same name, but with different uuid. let uuid = uuid!(\"6d472c99-e1d3-44fd-81fd-5eb83bbafdf7\"); animation.add_signal(AnimationSignal::new(uuid, signal_name, 0.5)); } }\n}","breadcrumbs":"Scene » Animation » Signals » From code","id":"293","title":"From code"},"294":{"body":"When you have your signals ready for use, all you need to do is to react to the signals somehow. This is very simple: just borrow your animation from the animation player and pop animation event one-by-one from internal queue: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{animation::AnimationPlayer, graph::Graph, node::Node},\n# };\n# fn react_to_signal_events( animation_player: Handle , animation_name: &str, signal_name: &str, graph: &mut Graph,\n) { if let Some(animation_player) = graph.try_get_mut_of_type:: (animation_player) { let animations = animation_player.animations_mut().get_value_mut_silent(); // Ideally, animation fetching should be done via its handle (the first argument of the // tuple returned by find_by_name_mut/ref), but for the sake of simplicity we'll do // this by name. if let Some((_, animation)) = animations.find_by_name_mut(animation_name) { // Pop every event one-by-one and do something. while let Some(signal) = animation.pop_event() { // We're interested only in signals with specific name. if signal.name == signal_name { println!(\"Signal event {} has occurred!\", signal.name); } } } }\n} You can do pretty much anything when reacting to signals. For example, this could be a prefab instantiation to create smoke effect under the feet, playing a footstep sound, etc.","breadcrumbs":"Scene » Animation » Signals » Reacting to signal events","id":"294","title":"Reacting to signal events"},"295":{"body":"Animation blending state machines are able to collect events from the currently playing animations using different strategies. This ability prevents you from tedious manual animation events collection from a bunch of animations manually. # extern crate fyrox;\n# use fyrox::{\n# animation::machine::{\n# layer::LayerAnimationEventsCollection, node::AnimationEventCollectionStrategy,\n# },\n# core::pool::Handle,\n# scene::{\n# animation::{absm::AnimationBlendingStateMachine, AnimationPlayer},\n# node::Node,\n# },\n# script::ScriptContext,\n# };\n# fn collect_events_from_absm( absm: Handle , strategy: AnimationEventCollectionStrategy, ctx: &mut ScriptContext,\n) -> LayerAnimationEventsCollection { if let Some(absm) = ctx .scene .graph .try_get_of_type:: (absm) { if let Some(animation_player) = ctx .scene .graph .try_get_of_type:: (absm.animation_player()) { // Fetch a layer first, it could be any layer of the ABMS, but for simplicity // we'll use the first layer. if let Some(layer) = absm.machine().layers().first() { return layer.collect_active_animations_events( absm.machine().parameters(), animation_player.animations(), strategy, ); } } } Default::default()\n} This function collects all animation events from all active animations in the specified ABSM (in its first layer). The arguments to it are the following: absm - a handle to an animation blending state machine node. strategy - event collection strategy, which includes all events collection, max and min weight. The last two may be used if you're getting a lot of events and want to get events from the animations with max or min weights respectively. ctx - current script context, available in pretty much any script methods.","breadcrumbs":"Scene » Animation » Signals » Events from ABSM","id":"295","title":"Events from ABSM"},"296":{"body":"Root motion is a special technique that transfers motion from some node in a hierarchy to a physical capsule, which is then used to perform the actual motion. In action it looks like this: As you can see in the first part of the video, the movement of the character looks more like floating above the ground. This happens because the actual movement of the physical capsule is not synchronized with the movement of the character. Root motion fixes exactly this issue by taking the motion of some root node of the animated hierarchy (hips in case of this character) and transferring it to the physical capsule. This makes the actual movement to be fully synchronized with the movement \"baked\" in the animation. Root motion also have some nice effect - you can move your character solely by the movement from animation, and it will work perfectly in 99% of cases. Animations can also contain some rotations which can also be extracted and applied to the physical capsule. The next awesome property is that your character will never stand out of its physical capsule, which will prevent phasing it into walls when playing animations with large movements. In general, you should prefer root motion -driven movement for your characters whenever you can. Simply because it eliminates a lot of common problems with character movement. It can also be applied to 2D world and will work exactly the same.","breadcrumbs":"Scene » Animation » Root Motion » Root Motion","id":"296","title":"Root Motion"},"297":{"body":"You can enable/disable/setup it in the drop-down menu that opens by clicking RM button in the animation editor. Keep in mind, that root motion should be configured on per animation basis. Most of the animations does not need the root motion at all. root motion The most important part here is the Root handle, it should be set to a root node that moves by your animation, usually it is called like \"hips\" or similar: root node After that, you need to apply filters for axes - most of the locomotion animations \"works\" in oXZ plane, so Y axis should be ignored. Also, if you don't have any turns in your animation, you can also filter out the rotation part. Alternatively, you can do the same from code: # extern crate fyrox;\n# use fyrox::{\n# animation::{Animation, RootMotionSettings},\n# core::pool::Handle,\n# scene::{animation::AnimationPlayer, node::Node},\n# script::ScriptContext,\n# };\n# fn setup_root_motion( animation_player: Handle , animation: Handle , root_node: Handle , ctx: &mut ScriptContext,\n) { if let Some(animation_player) = ctx .scene .graph .try_get_mut_of_type:: (animation_player) { if let Some(animation) = animation_player.animations_mut().try_get_mut(animation) { animation.set_root_motion_settings(Some(RootMotionSettings { node: root_node, ignore_x_movement: false, ignore_y_movement: true, ignore_z_movement: false, ignore_rotations: true, })) } }\n} This code does pretty much the same as the editor on the screenshots above. The arguments of this function are the following: animation_player - a handle to the animation player in which all your animations are stored, animation - a handle of the animation in which you want to enable the root motion (you can obtain the handle by using AnimationContainer::find_by_name_ref method). root_node - a handle to a root node of your character's hierarchy, usually it is called something like \"Hips\" or \"Pelvis\". ctx - script context from your current script.","breadcrumbs":"Scene » Animation » Root Motion » How to enable","id":"297","title":"How to enable"},"298":{"body":"Direct root motion values extracted from animations are kind of useless by their own and in 99% of the cases you should get the average root motion values from a state machine that animates your character. This is because animation blending state machine properly blends the root motion from all active animation sources. In general, it could look something like this: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector3, pool::Handle},\n# scene::{animation::absm::AnimationBlendingStateMachine, node::Node, rigidbody::RigidBody},\n# script::ScriptContext,\n# };\n# fn fetch_and_apply_root_motion( absm: Handle , rigid_body: Handle , character_model: Handle , ctx: &mut ScriptContext,\n) { // Step 1. Fetch the velocity vector from the animation blending state machine. let transform = ctx.scene.graph[character_model].global_transform(); let mut velocity = Vector3::default(); if let Some(state_machine) = ctx .scene .graph .try_get(absm) .and_then(|node| node.query_component_ref:: ()) { if let Some(root_motion) = state_machine.machine().pose().root_motion() { velocity = transform .transform_vector(&root_motion.delta_position) .scale(1.0 / ctx.dt); } } // Step 2. Apply the velocity to the rigid body and lock rotations. if let Some(body) = ctx.scene.graph.try_get_mut_of_type:: (rigid_body) { body.set_ang_vel(Default::default()); body.set_lin_vel(Vector3::new(velocity.x, body.lin_vel().y, velocity.z)); }\n} This code extracts the current local-space offset for the current frame and then transforms the offset to world-space coordinates. Finally, it reduces the offset by the current delta time (1.0 / ctx.dt) to obtain the new velocity vector which is then applied to the rigid body (player's capsule). The arguments in this function are following: absm a handle to an instance of Animation Blending State Machine node rigid_body a handle to the rigid body that is used by your character model - a handle to the root node of your character's 3D model.","breadcrumbs":"Scene » Animation » Root Motion » How to use","id":"298","title":"How to use"},"299":{"body":"If for some reason you still need raw root motion values from animations, then you can extract them directly from the desired animation by using Animation::root_motion method.","breadcrumbs":"Scene » Animation » Root Motion » Raw root motion values","id":"299","title":"Raw root motion values"},"3":{"body":"The book is primarily focused on game development with Fyrox, not on its API. You can find API docs here .","breadcrumbs":"About the Book » API Documentation","id":"3","title":"API Documentation"},"30":{"body":"Let's talk a bit about design philosophy and goals of the engine. Development of the engine started in the beginning of 2019 as a hobby project to learn Rust, and it quickly showed that Rust can be a game changer in the game development industry. Initially, the engine was just a port of an engine that is written in C. At the beginning, it was very interesting to build such complex thing as game engine in such low level language without any safety guarantees. After a year of development it became annoying to fix memory related issues (memory corruption, leaks, etc.), luckily at that time Rust's popularity grew, and it showed on my radar. I ( @mrDIMAS ) was able to port the engine to it in less than a year. Stability has improved dramatically, no more random crashes, performance was at the same or better levels - time invested in learning new language was paid off. Development speed does not degrade over time as it was in C, it is very easy to manage growing project.","breadcrumbs":"Introduction » Design Philosophy and Goals » Design Philosophy and Goals","id":"30","title":"Design Philosophy and Goals"},"300":{"body":"Sometimes there's a need to combine root motion with some procedural motion (for example - inertia after jumping). This could be done pretty easily by adding two velocity vectors - one from the root motion, and one from the procedural motion.","breadcrumbs":"Scene » Animation » Root Motion » Combining root motion with procedural motion","id":"300","title":"Combining root motion with procedural motion"},"301":{"body":"This chapter explains how the input handling in the engine works. The input system based on various events, that comes to the window.","breadcrumbs":"Input Handling (WIP) » Input","id":"301","title":"Input"},"302":{"body":"","breadcrumbs":"Input Handling (WIP) » Keyboard (WIP) » Keyboard Input (WIP)","id":"302","title":"Keyboard Input (WIP)"},"303":{"body":"","breadcrumbs":"Input Handling (WIP) » Mouse (WIP) » Mouse Input (WIP)","id":"303","title":"Mouse Input (WIP)"},"304":{"body":"","breadcrumbs":"Input Handling (WIP) » Raw Text Input (WIP) » Raw Text Input (WIP)","id":"304","title":"Raw Text Input (WIP)"},"305":{"body":"","breadcrumbs":"Artificial Intelligence (WIP) » Artificial Intelligence (WIP)","id":"305","title":"Artificial Intelligence (WIP)"},"306":{"body":"","breadcrumbs":"Artificial Intelligence (WIP) » Behaviour Trees (WIP) » Behaviour Trees (WIP)","id":"306","title":"Behaviour Trees (WIP)"},"307":{"body":"Fyrox has built-in A* (A-star) algorithm for pathfinding. It can be used to find a path on arbitrary graph without cycles. It could be a simple grid where each point knows about its \"neighbours\", navigational mesh , or some other graph.","breadcrumbs":"Artificial Intelligence (WIP) » Path Finding » Path Finding","id":"307","title":"Path Finding"},"308":{"body":"The simplest examples could be a search of path on uniform grid. This could be useful for games with open worlds, strategies, and any other types of games that uses uniform grid for pathfinding. # extern crate fyrox;\nuse fyrox::{ core::algebra::Vector3, utils::astar::{PathFinder, PathVertex},\n}; fn astar_on_uniform_grid() { // Create vertices. let size = 40; let mut vertices = Vec::new(); for y in 0..size { for x in 0..size { vertices.push(PathVertex::new(Vector3::new(x as f32, y as f32, 0.0))); } } let mut pathfinder = PathFinder::new(); pathfinder.set_vertices(vertices); // Link vertices to form a uniform grid. for y in 0..(size - 1) { for x in 0..(size - 1) { pathfinder.link_bidirect(y * size + x, y * size + x + 1); pathfinder.link_bidirect(y * size + x, (y + 1) * size + x); } } // Build a path from vertex 0 to vertex 100. let mut path = Vec::new(); assert!(pathfinder.build(0, 100, &mut path).is_ok());\n} Keep in mind, that the returned path is always reversed (its first point corresponds to an end point). You need either to reverse the path, or (which is much faster) just iterate in reverse over its points.","breadcrumbs":"Artificial Intelligence (WIP) » Path Finding » Examples","id":"308","title":"Examples"},"309":{"body":"A* is very simple, yet powerful algorithm. However, it is not always suitable, because it searches only on graph vertices and cannot build paths that are lying on a surface of arbitrary meshes. Simple path finding on a uniform grid is ok for some games (strategies for instance), but in FPS games it will look awful. In this case you should use navigational meshes which can build path on a surface of arbitrary meshes.","breadcrumbs":"Artificial Intelligence (WIP) » Path Finding » What to use","id":"309","title":"What to use"},"31":{"body":"One of the main goals of in the development of the engine is to provide high level of safety. What does this mean? In short: protect from memory-unsafety bugs. This does not include any logic errors, but when your game is free of random crashes due to memory unsafety it is much easier to fix logic bugs, because you don't have to think about potentially corrupted memory. Safety is also dictates architecture design decisions of your game, typical callback hell, that is possible to do in many other languages, is very tedious to implement in Rust. It is possible, but it requires quite a lot of manual work and quickly tell you that you're doing it wrong.","breadcrumbs":"Introduction » Design Philosophy and Goals » Safety","id":"31","title":"Safety"},"310":{"body":"Current A* implementation is not ideal and may hurt performance if you need to calculate a lot of paths on large graphs. It will be optimized in the future (see tracking issue for info).","breadcrumbs":"Artificial Intelligence (WIP) » Path Finding » Performance","id":"310","title":"Performance"},"311":{"body":"navmesh Navigational mesh (navmesh for short) is a surface which can be used for path finding. Unlike A* Pathfinder , it can build arbitrary paths on a surface of large polygons, making a path from point A to point B linear (standard pathfinder builds path only from vertex to vertex). Navmeshes should be used when you have an arbitrary \"walkable\" surface, for example, a game level with rooms, hallways, multiple floors and so on. A* pathfinder should be used for strategies or any other types of games with uniform pathfinding grid.","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » Navigational Meshes","id":"311","title":"Navigational Meshes"},"312":{"body":"There are three major ways of creating navigational meshes: manual, automatic, from external data.","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » How to create","id":"312","title":"How to create"},"313":{"body":"Navigational meshes can be created and edited in the FyroxEd. At first, create a \"Navigational Mesh\" node, select it and switch to \"navmesh\" interaction mode: navmesh Now you can edit the navmesh. For now, editing capabilities are quite limited and the only way to edit the navmesh is to Shift+Drag one if its edges: navmesh edit You can also delete edges and vertices: select a vertex or an edge and press Delete key. If you need to create closed loops, use \"Connect Edges\" button in the \"Navmesh\" floating panel: navmesh connect","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » Using the editor","id":"313","title":"Using the editor"},"314":{"body":"Fyrox does not support automatic navigational mesh generation yet. You can help by adding such feature.","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » Using automatic generation","id":"314","title":"Using automatic generation"},"315":{"body":"It is possible to create a navigational mesh from an arbitrary mesh, which could be made somewhere else (in Blender, 3Ds Max, or even generated by a navmesh generator). If you have a Mesh scene node in your scene then you could do something like this to build a navmesh from it: # extern crate fyrox;\n# use fyrox::scene::Scene;\n# use fyrox::utils::navmesh::Navmesh;\nfn make_navmesh(scene: &Scene, navmesh_name: &str) -> Navmesh { // Find mesh node in existing scene and create navigation mesh from it. let navmesh_node_handle = scene.graph.find_by_name_from_root(navmesh_name).unwrap().0; Navmesh::from_mesh(scene.graph[navmesh_node_handle].as_mesh())\n} Alternatively, you can create a navmesh directly from code like so: # extern crate fyrox;\n# use fyrox::utils::navmesh::Navmesh;\nlet navmesh = Navmesh::new( vec![ TriangleDefinition([0, 1, 3]), TriangleDefinition([1, 2, 3]), TriangleDefinition([2, 5, 3]), TriangleDefinition([2, 4, 5]), TriangleDefinition([4, 7, 5]), TriangleDefinition([4, 6, 7]), ], vec![ Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0), Vector3::new(1.0, 0.0, 1.0), Vector3::new(1.0, 0.0, 0.0), Vector3::new(2.0, 0.0, 1.0), Vector3::new(2.0, 0.0, 0.0), Vector3::new(3.0, 0.0, 1.0), Vector3::new(3.0, 0.0, 0.0), ],\n); The Navmesh::new method accepts a list of triangles and vertices, where triangles is a set of three indices of vertices forming a triangle.","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » Using external data","id":"315","title":"Using external data"},"316":{"body":"Navigational mesh agent helps you to build paths along the surface of a navigational mesh and follow it. Agents can be used to drive the motion of your game characters. Every agent knows about its target and automatically rebuilds the path if the target has moved. Navmesh agents are able to move along the path, providing you with their current position, so you can use it to perform an actual motion of your game characters. Agents work together with navigational meshes, you need to update their state every frame, so they can recalculate path if needed. A simple example could something like this: # extern crate fyrox;\n# use fyrox::utils::navmesh::NavmeshAgent;\n# struct Foo { // Add this to your script\nagent: NavmeshAgent\n# } After that, you need to update the agent every frame to make sure it will follow the target: # extern crate fyrox;\n# use fyrox::{\n# core::algebra::Vector3, scene::navmesh::NavigationalMesh, utils::navmesh::NavmeshAgent,\n# };\nfn update_agent( agent: &mut NavmeshAgent, target: Vector3 , dt: f32, navmesh: &mut NavigationalMesh,\n) { // Set the target to follow and the speed. agent.set_target(target); agent.set_speed(1.0); // Update the agent. agent.update(dt, navmesh.navmesh_mut()).unwrap(); // Print its position - you can use this position as target point of your game character. println!(\"{}\", agent.position());\n} This method should be called in on_update of your script. It accepts four parameters: a reference to the agent, a target which it will follow, a time step (context.dt), and a reference to navigational mesh node. You can fetch navigational mesh from the scene graph by its name: # extern crate fyrox;\n# use fyrox::scene::{navmesh::NavigationalMesh, Scene};\nfn find_navmesh<'a>(scene: &'a mut Scene, name: &str) -> &'a mut NavigationalMesh { let handle = scene.graph.find_by_name_from_root(name).unwrap().0; scene.graph[handle].as_navigational_mesh_mut()\n}","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » Agents","id":"316","title":"Agents"},"317":{"body":"It is possible to specify a radius for navigation mesh agents, which could be used to walk around corners like so: agent radius In some cases this behaviour is preferable, because it makes produced paths to look more natural. You can set agent's radius using set_radius method. By default, it is set to 0.2 meters, which is an average radius that is suitable for most of the cases.","breadcrumbs":"Artificial Intelligence (WIP) » Navigational Meshes » Radius","id":"317","title":"Radius"},"318":{"body":"","breadcrumbs":"Rendering » Rendering (WIP)","id":"318","title":"Rendering (WIP)"},"319":{"body":"Shader is a set of programs that run directly on graphics adapter. Each program from the set is called sub-shader . Sub-shaders linked with render pass, each render pass defines \"where\" to draw an object. \"where\" means that you can set up your own render pass and the renderer will use the sub-shader with your render pass. For the ease of use there are a number of predefined render passes . Shaders have properties of various types that can be used together with materials to draw an object.","breadcrumbs":"Rendering » Shaders » Shaders","id":"319","title":"Shaders"},"32":{"body":"Game engines usually built using system-level programming languages, that provides peak performance levels. Fyrox is not an exception. One if its design goals is to provide high levels of performance by default, leaving an opportunity for adding custom solutions for performance-critical places.","breadcrumbs":"Introduction » Design Philosophy and Goals » Performance","id":"32","title":"Performance"},"320":{"body":"The engine uses GLSL shading language for every sub-shader. There are numerous GLSL guides over the internet, so there is no need to \"re-post\" the well documented info again. There are very few differences: No need to define a version of the shader. Every shader source will be pre-processed, and it will get correct version automatically. Preprocessing is needed because the same shader could run on OpenGL and WebGL (OpenGL ES) which have some differences. There is a \"standard\" library of useful methods which is automatically included in every shader source at preprocessing stage. The library source could be found here . It is well documented, and you may find some functions useful for you job.","breadcrumbs":"Rendering » Shaders » Shaders language","id":"320","title":"Shaders language"},"321":{"body":"Shader has rigid structure that could be described in this code snippet: ( // A set of properties, there could be any amount of properties. properties: [ ( // Each property must have a name. This name must match with respective // uniforms! That's is the whole point of having properties. name: \"diffuseTexture\", // Value has limited set of possible variants. value: Sampler(default: None, fallback: White) ) ], // A set of render passes (see next section for more info) passes: [ ( // Name must match with the name of either standard render pass (see below) or // one of your passes. name: \"Forward\", // A set of parameters that regulate renderer pipeline state. // This is mandatory field of each render pass. draw_parameters: DrawParameters( // A face to cull. Either Front or Back. cull_face: Some(Back), // Color mask. Defines which colors should be written to render target. color_write: ColorMask( red: true, green: true, blue: true, alpha: true, ), // Whether to modify depth buffer or not. depth_write: true, // Whether to use stencil test or not. stencil_test: None, // Whether to perform depth test when drawing. depth_test: true, // Blending options. blend: Some(BlendFunc( sfactor: SrcAlpha, dfactor: OneMinusSrcAlpha, )), // Stencil options. stencil_op: StencilOp( fail: Keep, zfail: Keep, zpass: Keep, write_mask: 0xFFFF_FFFF, ), ), // Vertex shader code. vertex_shader: r#\" layout(location = 0) in vec3 vertexPosition; layout(location = 1) in vec2 vertexTexCoord; uniform mat4 fyrox_worldViewProjection; out vec2 texCoord; void main() { texCoord = vertexTexCoord; gl_Position = fyrox_worldViewProjection * vertexPosition; } \"#; // Pixel shader code. pixel_shader: r#\" // Note that the name of this uniform match the name of the property up above. uniform sampler2D diffuseTexture; out vec4 FragColor; in vec2 texCoord; void main() { FragColor = diffuseColor * texture(diffuseTexture, texCoord); } \"#; ) ],\n) The engine can load such shaders if you save it in a file with .shader extension. After that, you can assign the shader to your material in the Material Editor: shader Alternatively, you can load the shader from code. To do this, you can use this code: # extern crate fyrox;\n# use fyrox::{\n# asset::manager::ResourceManager,\n# material::shader::{Shader, ShaderResource},\n# };\n# fn load_shader(resource_manager: &ResourceManager) -> ShaderResource { resource_manager.request:: (\"path/to/my/cool.shader\")\n} After that you can use the shader to build a material from it: # extern crate fyrox;\n# use fyrox::{\n# asset::manager::ResourceManager,\n# material::{shader::Shader, Material, SharedMaterial},\n# };\n# fn create_material(resource_manager: &ResourceManager) -> SharedMaterial { let shader = resource_manager.request:: (\"path/to/my/cool.shader\"); SharedMaterial::new(Material::from_shader( shader, Some(resource_manager.clone()), ))\n} This material instance can be used for rendering. For example, you can assign it a surface of some mesh.","breadcrumbs":"Rendering » Shaders » Structure","id":"321","title":"Structure"},"322":{"body":"Property is a named variable of some type. Properties are directly tied with the uniforms in the sub-shaders, for each you can have a property called time, and then you can define uniform float time; in your sub-shader and the engine will pass a property value to that uniform for you before drawing an object. Properties placed in a \"global namespace\", which means that every sub-shader has \"access\" to the properties.","breadcrumbs":"Rendering » Shaders » Properties","id":"322","title":"Properties"},"323":{"body":"There are number of built-in properties, that Fyrox will try to assign automatically if they're defined in your shader: Name Type Description fyrox_worldMatrix mat4 Local-to-world transformation. fyrox_worldViewProjection mat4 Local-to-clip-space transform. fyrox_boneMatrices sampler2D Array of bone matrices packed into a texture. Use S_FetchMatrix built-in method to fetch a matrix by its index. fyrox_useSkeletalAnimation bool Whether skinned meshes is rendering or not. fyrox_cameraPosition vec3 Position of the camera. fyrox_usePOM bool Whether to use parallax mapping or not. fyrox_lightPosition vec3 Light position. fyrox_blendShapesStorage sampler3D 3D texture of layered blend shape storage. Use S_FetchBlendShapeOffsets built-in method to fetch info. fyrox_blendShapesWeights float[128] Weights of all available blend shapes. fyrox_blendShapesCount int Total amount of blend shapes. To use any of the properties, just define a uniform with an appropriate name: uniform mat4 fyrox_worldMatrix;\nuniform vec3 fyrox_cameraPosition; This list will be extended in future releases.","breadcrumbs":"Rendering » Shaders » Built-in properties","id":"323","title":"Built-in properties"},"324":{"body":"Predefined render passes helps you to create your own shader without a need to create your own render pass and to quickly start writing your shaders. GBuffer - A pass that fills a set with render target sized textures with various data about each rendered object. These textures then are used for physically-based lighting. Use this pass when you want the standard lighting to work with your objects. Forward - A pass that draws an object directly in render target. This pass is very limiting, it does not support lighting, shadows, etc. It should be only used to render translucent objects. SpotShadow - A pass that emits depth values for an object, later this depth map will be used to render shadows. PointShadow - A pass that emits distance from a fragment to a point light, later this depth map will be used to render shadows.","breadcrumbs":"Rendering » Shaders » Predefined render passes","id":"324","title":"Predefined render passes"},"325":{"body":"Drawing parameters defines which GPU functions to use and at which state. For example, to render transparent objects you need to enable blending with specific blending rules. Or you need to disable culling to draw objects from both sides. This is when draw parameters come in handy. There are relatively large list of drawing parameters, and it could confuse a person who didn't get used to work with graphics. The following list should help you to use drawing parameters correctly. cull_face: Defines which side of polygon should be culled. Possible values: None, Some(CullFace::Back), Some(CullFace::Front) color_write: Defines which components of color should be written to a render target Possible values: ColorMask { .. } depth_write: Whether to modify depth buffer or not. Possible values: true/false stencil_test: Whether to use stencil test or not. Possible values: None Some(StencilFunc { .. }) depth_test: Whether to perform depth test when drawing. Possible values: true/false blend: Blending options. Possible values: None Some(BlendFunc { .. } ) stencil_op: Stencil options. Possible values: StencilOp { .. }","breadcrumbs":"Rendering » Shaders » Drawing parameters","id":"325","title":"Drawing parameters"},"326":{"body":"Vertex shader operates on single vertices, it must provide at least the position of the vertex in clipping space. In other words it has to do at least this: layout(location = 0) in vec3 vertexPosition; uniform mat4 fyrox_worldViewProjection; // Note the built-in variable. void main()\n{ gl_Position = fyrox_worldViewProjection * vertexPosition;\n} This is the simplest vertex shader, using vertex shaders you can create various graphical effects that affects vertices.","breadcrumbs":"Rendering » Shaders » Vertex shader","id":"326","title":"Vertex shader"},"327":{"body":"Pixel shader (or more precisely - fragment shader), operates on a small fragment of your render target. In general pixels shaders just writes some color to a render target (or multiple targets) using some program. out vec4 FragColor; void main()\n{ FragColor = vec4(1, 0, 0, 1);\n} This is the simplest pixel shader, it just fills the render target with red color.","breadcrumbs":"Rendering » Shaders » Pixel Shader","id":"327","title":"Pixel Shader"},"328":{"body":"Material defines a set of values for a shader. Materials usually contains textures (diffuse, normal, height, emission and other maps), numerical values (floats, integers), vectors, booleans, matrices and arrays of each type, except textures. Each parameter can be changed in runtime giving you the ability to create animated materials. However, in practice, most materials are static, this means that once it's created, it won't be changed anymore. Please keep in mind that the actual \"rules\" of drawing an entity are stored in the shader, material is only a storage for specific uses of the shader. Multiple materials can share the same shader, for example standard shader covers 95% of most common use cases, and it is shared across multiple materials. The only difference are property values, for example you can draw multiple cubes using the same shader, but with different textures. Material itself can be shared across multiple places as well as the shader. This gives you the ability to render multiple objects with the same material efficiently.","breadcrumbs":"Rendering » Materials » Materials","id":"328","title":"Materials"},"329":{"body":"It is very important re-use materials as much as possible, because the amount of materials used per frame significantly correlates with performance. The more unique materials you have per frame, the more work the renderer and video driver need in order to render a frame and more time the frame will require for rendering, thus lowering your FPS.","breadcrumbs":"Rendering » Materials » Performance","id":"329","title":"Performance"},"33":{"body":"Other very important part is that the engine should be friendly to newcomers. It should lower entry threshold, not make it worse. Fyrox uses well known and battle-tested concepts, thus making it easier to make games with it. On other hand, it still can be extended with anything you need - it tries to be as good for veterans of the game industry as for newcomers.","breadcrumbs":"Introduction » Design Philosophy and Goals » Ease of use","id":"33","title":"Ease of use"},"330":{"body":"The engine offers a standard PBR material, PBR stands for \"Physically-Based Rendering\" which gives you the quality of shading which is very close to materials in real world (to some extent of course). The standard material can cover 95% of use cases, and it is suitable for almost any kind of game, except maybe some cartoon-ish or stylized games. The standard material has quite a lot of properties that can be used to fully utilize the power of PBR rendering: diffuseColor - an RGBA color that will be used as a base color for you object. Caveat: the opacity value (alpha) will be used only with Forward render path! This means that you will need to switch render path on your mesh ( see below ) diffuseTexture - a 2D texture containing the unlit \"basic\" colors of your object, this is the most commonly used texture. For example, you can assign a brick wall texture to this property and your object will look like a brick wall. normalTexture - a 2D texture containing per-pixel normal vectors. metallicTexture - a 2D texture containing per-pixel metallic factor, where 0 - dielectric, 1 - metal. In simple words it defines whether your object reflects (1.0) the environment or not (0.0). roughnessTexture - a 2D texture containing per-pixel roughness factor, where 0 - completely flat, 1 - very rough. heightTexture - a 2D texture containing per-pixel displacement value, it is used with parallax mapping to crate an effect of volume on a flat surface. emissionTexture - a 2D texture containing per-pixel emission lighting. You could use this to create emissive surfaces like small lamps on wall of sci-fi ship, or to create glowing eyes for your monsters that will scare the player. lightmapTexture - a 2D texture containing per-pixel static lighting. It is used to apply precomputed light to your 3D models, and the most common use case is to lit a static object using a static light. Precomputed light is very cheap. The engine offers built-in lightmapper that can generate lightmaps for you. aoTexture - a 2D texture containing per-pixel shading values, allows you to \"bake\" shadows in for your 3D object. texCoordScale - a 2D vector that allows you to scale texture coordinates used to sample the textures mentioned above (except lightmaps, they're using separate texture coordinates) layerIndex - a natural number that is used for decals masking, a decal will only be applied to your mesh if and only if the decal has matching index. emissionStrength - a 3D vector that allows you to set the strength of emission per-channel (R, G, B) for your emissionTexture","breadcrumbs":"Rendering » Materials » Standard material","id":"330","title":"Standard material"},"331":{"body":"The standard material offers very basic transparency support, to use it you have to explicitly switch render path on your mesh object. It could be done in this way: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# scene::{mesh::RenderPath, node::Node, Scene},\n# };\n# # fn set_forward_render_path(scene: &mut Scene, mesh_handle: Handle ) { scene.graph[mesh_handle] .as_mesh_mut() .set_render_path(RenderPath::Forward);\n# } After this, your mesh will be rendered using a specialized render pass called Forward which supports alpha-blending and transparent objects. Caveat: Current forward renderer implementation does not support any kind of lighting, if you need lighting, you will need to use custom shader for that!","breadcrumbs":"Rendering » Materials » Transparency","id":"331","title":"Transparency"},"332":{"body":"When you're loading a 3D model in the engine, the engine tries to convert the materials stored inside to standard material. In most cases there is no way to create 100% matching material on the fly, instead the engine tries to do its best to make sure the material will be imported as closely as possible to the original one. Various 3D modelling tools use different material system, but all of them allow you to export your 3D model in one of the commonly used formats (such as FBX).","breadcrumbs":"Rendering » Materials » Material import","id":"332","title":"Material import"},"333":{"body":"When using Blender, make sure you are using Principled BSDF material, it is the closest material that can be converted to engine's standard material at almost 100% fidelity.","breadcrumbs":"Rendering » Materials » Blender","id":"333","title":"Blender"},"334":{"body":"It highly depends on the version of the 3Ds max, but in general the default material should work fine.","breadcrumbs":"Rendering » Materials » 3Ds max","id":"334","title":"3Ds max"},"335":{"body":"Fyrox supports light maps for static lighting, that allows you to pre-compute the lighting, store it into a texture and use this texture for rendering. This makes lighting very fast to render, but requires additional pre-processing step and slightly increases memory usage. Light maps are very useful for static lights and static level geometry; they do not work with dynamic objects and lights. Light maps could be used on mobile devices, to significantly increase performance. This is how \"baked\" light looks like: example light map This is light map for one of the curtains in the scene on screenshot below. As you can see, there are quite a lot of parts on this texture, this is because the engine generates second texture coordinates for the light map, and sometimes it cannot generate one big chunk, and it has to add seams. Despite the look of it, the light map is actually tightly packed, it contains a lot of black pixels because the ambient color is black and not all pixels on it are actually lit.","breadcrumbs":"Rendering » Light Maps » Light Maps","id":"335","title":"Light Maps"},"336":{"body":"There are two major ways of generating a light map: from the editor and from code. Usually, using the editor is preferable, because you can immediately see the results. Code-based approach could be used if you're making your own tool for light maps.","breadcrumbs":"Rendering » Light Maps » How to generate","id":"336","title":"How to generate"},"337":{"body":"You can generate a light map from the editor in just a few clicks, go to View -> Light Panel and the Light Panel should open: lightmap There's not many settings in this window, but all of them are very important. At first, choose a folder in which the editor will store the generated light map by clicking ... button. The last two parameters are the following: Texels per unit - it defines 'pixels density' per unit of area (square meters). The more the value, the more detailed the produced light map will be and vice versa. This value directly affects performance in quadratic manner, which means that if you change it from 32 to 64, the time needed to generate the light map won't double, but it will be 4 times more. Default value is 64 which is a good balance between quality and generation speed. Spacing - relative spacing between UV elements generated by the built-in UV mapper. The more the value, the more the distance between the UV elements will be. This parameter is used to prevent seams from occurring, when rendering meshes with bilinear filtration. Default value is 0.005, which is a good balance between size of the light maps and their quality (lack of seams). Usually the default values are fine for most cases, but you can tweak them and compare the results. Now you can click the Generate Light Map button and wait until the light map is fully generated. lightmap generation You can cancel the generation at any time, however in some cases there might be a small delay between cancel request and the actual generation cancellation. When the generation is done, you should immediately see the result: generated lightmap Now if you save the scene, it will remember the generated light map and will load it automatically for you.","breadcrumbs":"Rendering » Light Maps » From editor","id":"337","title":"From editor"},"338":{"body":"The following example creates a simple scene and generates a light map for it, which is then saved to disk: # use fyrox::{\n# asset::ResourceData,\n# core::algebra::{Matrix4, Vector3},\n# scene::{\n# base::BaseBuilder,\n# light::{point::PointLightBuilder, BaseLightBuilder},\n# mesh::{\n# surface::SurfaceSharedData,\n# surface::{SurfaceBuilder, SurfaceData},\n# MeshBuilder,\n# },\n# transform::TransformBuilder,\n# Scene,\n# },\n# utils::lightmap::{Lightmap, LightmapInputData},\n# };\n# use std::path::Path;\n# fn generate_lightmap() { // Create a test scene first. let mut scene = Scene::new(); let data = SurfaceData::make_cone( 16, 1.0, 1.0, &Matrix4::new_nonuniform_scaling(&Vector3::new(1.0, 1.1, 1.0)), ); MeshBuilder::new(BaseBuilder::new()) .with_surfaces(vec![ SurfaceBuilder::new(SurfaceSharedData::new(data)).build() ]) .build(&mut scene.graph); PointLightBuilder::new(BaseLightBuilder::new( BaseBuilder::new().with_local_transform( TransformBuilder::new() .with_local_position(Vector3::new(0.0, 2.0, 0.0)) .build(), ), )) .with_radius(4.0) .build(&mut scene.graph); // Prepare the data for generation using the scene. let data = LightmapInputData::from_scene( &scene, |_, _| true, Default::default(), Default::default(), ) .unwrap(); // Generate the lightmap. let lightmap = Lightmap::new(data, 64, 0.005, Default::default(), Default::default()).unwrap(); // Save each texture to disk. let mut counter = 0; for entry_set in lightmap.map.values() { for entry in entry_set { let mut data = entry.texture.as_ref().unwrap().data_ref(); data.save(Path::new(&format!(\"{}.png\", counter))).unwrap(); counter += 1; } }\n}","breadcrumbs":"Rendering » Light Maps » From code","id":"338","title":"From code"},"339":{"body":"You can ignore this section if you're generated a light map in the editor, because your scene already contains all required connections to the generated light map, and it will be loaded automatically with the scene. However, if you need to change the light maps on the fly, you can use the following code: # use fyrox::{asset::manager::ResourceManager, scene::Scene, utils::lightmap::Lightmap};\n# fn change_light_map(scene: &mut Scene, resource_manager: ResourceManager) { let light_map = fyrox::core::futures::executor::block_on(Lightmap::load( \"a/path/to/lightmap.lmp\", resource_manager, )) .unwrap(); scene.graph.set_lightmap(light_map).unwrap();\n}","breadcrumbs":"Rendering » Light Maps » Using the lightmap","id":"339","title":"Using the lightmap"},"34":{"body":"Fyrox has large projects built on it, that helps to understand real needs for general-purpose game engine. This helps in revealing weak spots in design and fix them.","breadcrumbs":"Introduction » Design Philosophy and Goals » Battle-tested","id":"34","title":"Battle-tested"},"340":{"body":"Fyrox uses CPU light map generator, which means that it is quite slow. Light sources that were baked into a light map will also light up any surface with light map on - this essentially means double lighting. To fix that you need to disable light sources that were baked into the light map explicitly.","breadcrumbs":"Rendering » Light Maps » Limitations","id":"340","title":"Limitations"},"341":{"body":"Renderer has a large set of settings, that allows you to tweak graphics quality to find optimal balance between rendering quality and performance. Quality settings are represented by the following structure: # extern crate fyrox;\n# use fyrox::renderer::{CsmSettings, ShadowMapPrecision};\nstruct QualitySettings { point_shadow_map_size: usize, point_soft_shadows: bool, point_shadows_enabled: bool, point_shadows_distance: f32, point_shadow_map_precision: ShadowMapPrecision, spot_shadow_map_size: usize, spot_soft_shadows: bool, spot_shadows_enabled: bool, spot_shadows_distance: f32, spot_shadow_map_precision: ShadowMapPrecision, csm_settings: CsmSettings, use_ssao: bool, ssao_radius: f32, light_scatter_enabled: bool, fxaa: bool, use_parallax_mapping: bool, use_bloom: bool,\n} point_shadow_map_size - size of a cube map face of shadow map texture (in pixels). The higher, the better quality, but lower performance. Typical values for medium GPU (GTX 1050) is 1024 pixels. point_soft_shadows - should the shadows from point lights be smooth (true) or blocky (false). The latter option has better performance, but lower quality. point_shadows_enabled - are the shadows from point lights enabled? point_shadows_distance - maximal distance from a camera to draw point light shadows. It is used to disable shadows on distant lights. The distance is given in meters. The lower the value, the better performance is. point_shadow_map_precision - defines bit-depth (u16 or u32) for shadow map pixels. Lower bit depth means better performance and lower quality. spot_shadow_map_size - size of a shadow map texture for spotlights. The higher, the better quality, but lower performance. Typical values for medium GPU (GTX 1050) is 1024 pixels. spot_soft_shadows - should the shadows from spotlights be smooth (true) or blocky (false). The latter option has better performance, but lower quality. spot_shadows_enabled - are the shadows from spotlights enabled? spot_shadows_distance - maximal distance from a camera to draw spotlight shadows. It is used to disable shadows on distant lights. The distance is given in meters. The lower the value, the better performance is. spot_shadow_map_precision - defines bit-depth (u16 or u32) for shadow map pixels. Lower bit depth means better performance and lower quality. csm_settings - settings for cascaded shadow maps for directional lights. enabled - whether cascaded shadow maps enabled or not. size - size of texture for each cascade. precision - defines bit-depth (u16 or u32) for shadow map pixels. Lower bit depth means better performance and lower quality. pcf - should the shadows from directional lights be smooth (true) or blocky (false). The latter option has better performance, but lower quality. use_ssao - defines whether the renderer should perform separate screen-space ambient occlusion pass. This option has relatively small performance impact. ssao_radius - radius of sampling hemisphere used in SSAO, it defines much ambient occlusion will be in your scene. has no performance impact. light_scatter_enabled - global switch to enable or disable light scattering. Each light have its own scatter switch, but this one is able to globally disable scatter. Light scattering has medium performance impact, it also depends on light count in your scene. fxaa - is full-screen anti-aliasing needed? This option has low performance impact. use_parallax_mapping - defines whether the renderer should use parallax mapping to simulate bumps and dents on flat surfaces using special textures. This option has low performance impact. use_bloom - defines whether the renderer should draw glowing pixels. This option has low performance impact.","breadcrumbs":"Rendering » Settings » Settings","id":"341","title":"Settings"},"342":{"body":"The renderer offers built-in presets for various graphics quality, use QualitySettings::ultra(), QualitySettings::high(), QualitySettings::medium() and QualitySettings::low() presets to quickly tune quality-performance balance.","breadcrumbs":"Rendering » Settings » Presets","id":"342","title":"Presets"},"343":{"body":"To apply the settings, use Renderer::set_quality_settings method: # extern crate fyrox;\n# use fyrox::{\n# core::log::Log, engine::GraphicsContext, plugin::PluginContext, renderer::QualitySettings,\n# };\n# fn set_quality_settings(context: &mut PluginContext) { // Keep in mind, that graphics context can be non-initialized. This could happen if you're trying to access it before // your game received `Event::Resumed` event. if let GraphicsContext::Initialized(ref mut graphics_context) = context.graphics_context { let mut settings = QualitySettings::high(); // Disable something. settings.use_ssao = false; settings.fxaa = false; // Apply. Log::verify(graphics_context.renderer.set_quality_settings(&settings)) }\n} Keep in mind, that graphics context can be non-initialized. This could happen if you're trying to access it before your game received Event::Resumed event. See the docs for Event::Resumed for more info. There is only one place, where graphics context is guaranteed to be initialized - Plugin::on_graphics_context_initialized method. Inside it, you can access the renderer by simple: context.graphics_context.as_initialized_mut().renderer, in other places you should always do a checked borrow.","breadcrumbs":"Rendering » Settings » How to apply","id":"343","title":"How to apply"},"344":{"body":"You can define your own render passes that extends the renderer, currently there are render passes only for scenes, so no custom post-effects (this is planned to be improved in Fyrox 0.28). Render pass has full access to graphics framework (which is a thin wrapper around OpenGL) so it can utilize full power of it to implement various graphical effects.","breadcrumbs":"Rendering » Render Pass » Render Pass","id":"344","title":"Render Pass"},"345":{"body":"Render pass is a complex thing, that requires relatively deep knowledge in computer graphics. It is intended to be used by experienced graphics programmers. Here's the simplest render pass that renders unit quad without any textures. # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Matrix4, pool::Handle, sstorage::ImmutableString},\n# renderer::{\n# framework::{\n# error::FrameworkError,\n# framebuffer::{DrawParameters},\n# geometry_buffer::{GeometryBuffer, ElementRange, GeometryBufferKind},\n# gpu_program::{GpuProgram, UniformLocation},\n# },\n# RenderPassStatistics, Renderer, SceneRenderPass, SceneRenderPassContext,\n# },\n# scene::{mesh::surface::SurfaceData, Scene},\n# };\n# use std::{cell::RefCell, rc::Rc};\n# struct MyRenderPass { enabled: bool, shader: GpuProgram, target_scene: Handle , quad: GeometryBuffer, world_view_proj: UniformLocation,\n} impl MyRenderPass { pub fn new( renderer: &mut Renderer, target_scene: Handle , ) -> Result { let vs = r#\" layout(location = 0) in vec3 vertexPosition; uniform mat4 c; void main() { gl_Position = worldViewProjectionMatrix * vertexPosition; } \"#; let fs = r#\" out vec4 FragColor; void main() { FragColor = vec4(1.0, 0.0, 0.0, 1.0); } \"#; let shader = GpuProgram::from_source(&mut renderer.state, \"MyShader\", vs, fs)?; Ok(Self { enabled: true, world_view_proj: shader.uniform_location( &renderer.state, &ImmutableString::new(\"worldViewProjectionMatrix\"), )?, target_scene, quad: GeometryBuffer::from_surface_data( &SurfaceData::make_quad(&Matrix4::identity()), GeometryBufferKind::StaticDraw, &mut renderer.state, ), shader, }) }\n} impl SceneRenderPass for MyRenderPass { fn on_ldr_render( &mut self, ctx: SceneRenderPassContext, ) -> Result { let mut stats = RenderPassStatistics::default(); // Make sure to render only to target scene. if self.enabled && ctx.scene_handle == self.target_scene { stats += ctx.framebuffer.draw( &self.quad, ctx.pipeline_state, ctx.viewport, &self.shader, &DrawParameters::default(), ElementRange::Full, |mut program| { program.set_matrix4(&self.world_view_proj, &Matrix4::identity()); }, )?; } Ok(stats) }\n} The code snippet shows how to create a shader, find its uniforms, and finally how to actually render something in target frame buffer.","breadcrumbs":"Rendering » Render Pass » Creating a render pass","id":"345","title":"Creating a render pass"},"346":{"body":"Every render pass must be registered in the renderer, otherwise it won't be used. You can register a render pass using add_render_pass method of the Renderer: # extern crate fyrox;\n# use fyrox::renderer::{Renderer, SceneRenderPass};\n# use std::{cell::RefCell, rc::Rc};\n# # struct MyRenderPass;\n# impl SceneRenderPass for MyRenderPass {}\n# fn usage_example(renderer: &mut Renderer, render_pass: MyRenderPass) { let shared_pass = Rc::new(RefCell::new(render_pass)); // You can share the pass across multiple places to be able to control it. renderer.add_render_pass(shared_pass);\n} Please notice that we've wrapped render pass in Rc >, this means that you can share it across multiple places and modify its data from the code of your game.","breadcrumbs":"Rendering » Render Pass » Registering a render pass","id":"346","title":"Registering a render pass"},"347":{"body":"This chapter explains how to use normal maps in the engine correctly and how to solve common issues with normal maps as well.","breadcrumbs":"Rendering » Normal Maps » Normal Maps","id":"347","title":"Normal Maps"},"348":{"body":"Fyrox uses so-called DirectX Y- normal maps, which means that it expects the origin of local coordinate system of a normal map to be at the left-top corner of the map, instead of OpenGL Y+ normal maps where the origin is at the left-bottom corner of the map. DirectX Y- normal maps are much more prevalent nowadays, especially when it comes to game-ready 3D models. Some software (like Substance Painter), by default has exporting settings to be set to DirectX Y- style normal maps. The difference between the two is quite obvious if you look at the lighting with both normal maps: difference The left one is DirectX Y- and the right one is OpenGL Y+. As you can see, the left one looks correctly - the screw head is convex as in reality and the lighting is also correct. On the other side, however, the screw head look to be concave and the lighting is opposite.","breadcrumbs":"Rendering » Normal Maps » Format","id":"348","title":"Format"},"349":{"body":"If you have these sort of issues in your project, all you need to do is to flip (G = 1 - G) green channel of you normal map. For now, this should be done manually in some pictures editor, future versions of the engine will have a switch to flip green channel for you automatically. A simple trick of how to understand which type of normal map you have: look at any obvious bump (convex part) on the normal map, if its top contains green-ish colors then you have OpenGL Y+ normal maps and its green channel should be flipped: y difference On the image above, the screw head (in the red circle) is an obviously convex and on the left side you can see, that the green-ish colors is at the bottom, while on the right side green-ish colors is at the top. You could also check the lighting results on your 3D model and see if they're correct (looks the same as in the 3D modelling software, for instance) while moving a light source around.","breadcrumbs":"Rendering » Normal Maps » Solving Issues","id":"349","title":"Solving Issues"},"35":{"body":"This chapter contains answers for frequently asked questions.","breadcrumbs":"Introduction » Frequently Asked Questions » Frequently Asked Questions","id":"35","title":"Frequently Asked Questions"},"350":{"body":"This chapter covers asset management in the engine. Asset management is performed by Asset Browser in the editor and by ResourceManager from API.","breadcrumbs":"Resource Management » Asset Management","id":"350","title":"Asset Management"},"351":{"body":"Assets loading is asynchronous, it is possible to load multiple assets in parallel or load until a specific asset is loaded.","breadcrumbs":"Resource Management » General Info","id":"351","title":"General Info"},"352":{"body":"It is strongly advised to specify all resources used by your game entities inside your scripts, instead of requesting resources directly from the resource manager on demand. This approach solves two common issues: It allows you to set resources directly from the editor by a simple drag'n'drop from the Asset Browser. The engine will be able to wait until all resources used by a scene are fully loaded. This is especially important, because this way can guarantee, that scene loading will be \"seamless\" and if the scene was loaded, it means that all resources used by it are loaded too. This can be achieved by adding a respective field in your script. For example, you may a have a weapon script that shoots some projectiles. In this case all you need to add a projectile: Option field in your script, assign it to some prefab in the editor and then instantiate it from code when shooting. Storing resource handle directly in your script helps the engine to gather all resources used by parent scene and preload them too while loading the scene itself. Such approach prevent lags when doing actual shooting, which is especially important if you're targeting a WebAssembly platform. On WebAssembly all the files accessed over network API which could work with unstable connection. In any case, even on PC it helps a lot. Requesting resources on demand could be useful in limited situations: You're loading a new game level - in this case it is perfectly fine to request the resource manually. You're doing some background work (level streaming for instance).","breadcrumbs":"Resource Management » Best Practices","id":"352","title":"Best Practices"},"353":{"body":"Asset browser allows you to preview your assets and edit their import properties. It looks something like this (keep in mind that the screenshot could be outdated). Asset Browser There are three main areas in it: Left directory tree - shows all directories starting from project root. It does not show any files, this is for what the center section is. Center asset previewer - shows all assets from selected directory. The path at the top of the section shows asset path. Right asset import options inspector - it shows import properties of selected asset. Typical workflow could look like this: Select desired directory from the left tree Select desired asset in the center previewer Edit import properties of selected asset and click \"Apply\" button to save import options and re-load the asset with new options. Alternatively, you can just type in the name of some resource you're looking for in the search bar at the top of the Asset Browser. Check next chapters to learn how to manage specific asset types and what their import does what.","breadcrumbs":"Resource Management » Asset Browser","id":"353","title":"Asset Browser"},"354":{"body":"Please read API docs here","breadcrumbs":"Resource Management » API Docs","id":"354","title":"API Docs"},"355":{"body":"","breadcrumbs":"Resource Management » Models » Model resources","id":"355","title":"Model resources"},"356":{"body":"Fyrox supports these file formats for 3D models: FBX - standard game development industry 3D model exchange format RGS - native scenes format produced by Fyroxed (the editor) The list could be extended in the future.","breadcrumbs":"Resource Management » Models » Supported formats","id":"356","title":"Supported formats"},"357":{"body":"Model must be instantiated in your scene, there is no other way of using it. To do this, you can either use drag'n'drop from Asset Browser in the editor or instantiate the model dynamically from code: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::model::{Model, ModelResourceExtension},\n# scene::{node::Node, Scene},\n# };\n# use std::path::Path;\nasync fn instantiate_model( path: &Path, resource_manager: ResourceManager, scene: &mut Scene,\n) -> Handle { // Load model first. Alternatively, you can store resource handle somewhere and use it for // instantiation. let model = resource_manager.request:: (path).await.unwrap(); model.instantiate(scene)\n}","breadcrumbs":"Resource Management » Models » Instantiation","id":"357","title":"Instantiation"},"358":{"body":"The engine tries to import materials as close as possible to originals in the model, however it is not always possible because some 3D modelling software could use different shading models. By default, the engine tries to convert everything to PBR materials, so if you have a 3D model with a special material made for cartoon shading, the engine will still import it as PBR material (with lots of missing textures of course). You should take this into account when working with something other than PBR materials. In cases when your 3D model have some weird materials, you should create appropriate materials and shaders manually , the engine is not a magic tool, it has some defaults that do not cover all possible cases. It is also possible to specify how to resolve textures while loading a 3D model, select your model in the Asset Browser and there will be import options right below the model preview: model import It is also possible to specify such options manually. To do that, you need to create import options file with the following content near your 3D model (this is what the editor does for you): ( material_search_options: RecursiveUp\n) The file must have the .options additional extension. For example, if you have a foo.fbx model, the options file should have foo.fbx.options name. Even if it is possible to modify it by hand, it is strongly advised to use the editor to edit import options, because it reduces the chance of messing up.","breadcrumbs":"Resource Management » Models » Material import","id":"358","title":"Material import"},"359":{"body":"Blender's FBX exporter has exporting scale properties usually set to 100%, this may lead to incorrect scale of your model in the engine. It will have (100.0, 100.0, 100.0) scale which is very huge. To fix that, set the scale in the exporter to 0.01.","breadcrumbs":"Resource Management » Models » Tips for Blender","id":"359","title":"Tips for Blender"},"36":{"body":"Fyrox uses OpenGL 3.3 on PC and OpenGL ES 3.0 on WebAssembly. Why? Mainly due to historical reasons. Back in the day (Q4 of 2018), there weren't any good alternatives to it with a wide range of supported platforms. For example, wgpu didn't even exist , as its first version was released in January 2019. Other crates were taking their first baby steps and weren't ready for production.","breadcrumbs":"Introduction » Frequently Asked Questions » Which graphics API does the engine use?","id":"36","title":"Which graphics API does the engine use?"},"360":{"body":"Latest versions of 3Ds max have node-based material editor which creates some \"junk\" nodes which may mess up material import. To prevent any issues with that, you should clean all assignments to material slots to use maps directly.","breadcrumbs":"Resource Management » Models » Tips for 3Ds Max","id":"360","title":"Tips for 3Ds Max"},"361":{"body":"Texture is an image that is used to fill faces to add details to them. In most cases textures are just 2D images, however there are some exclusions to that - for example cube maps, that may be used for environment mapping. Fyrox supports 1D, 2D, 3D and Cube textures.","breadcrumbs":"Resource Management » Textures » Textures","id":"361","title":"Textures"},"362":{"body":"To load images and decode them, Fyrox uses image and ddsfile crates. Here is the list of supported formats: png, tga, bmp, dds, jpg, gif, tiff, dds.","breadcrumbs":"Resource Management » Textures » Supported formats","id":"362","title":"Supported formats"},"363":{"body":"Fyrox supports most commonly used formats of compressed textures: DXT1, DXT3, DXT5. Such textures can be loaded only from DDS files. You can specify on-demand texture compression in import options (see below), it works for every texture format except DDS. It is meant to be used when you don't want to bother with DDS format, there are two compression methods: Quality - has 4:1 compression ratio, supports full 8-bit alpha channel. Textures with gradients will most likely suffer from noticeable banding. Speed - has lower quality compared to Quality mode, but it has 8:1 compression ratio for texture without alpha channel and 6:1 with alpha channel. Keep in mind, that alpha channel in this mode supports only 1 bit - it is either enabled or not. Compressed textures usually does not support color gradient very well, if you have a texture with a lot of colors and gradients, then you'll most likely get compressed texture with lots of graphical artifacts such as banding. It is also worth mentioning, that you should never use compression with normal maps, it can significantly distort normals because normal maps usually have lots of color gradients.","breadcrumbs":"Resource Management » Textures » Compressed textures","id":"363","title":"Compressed textures"},"364":{"body":"It is possible to define custom import options. Using import options you could set desired compression quality, filtering, wrapping, etc. Import options should be defined using Asset Browser in the editor: texture import It is also possible to define import options manually in a separate file with the same name as the source texture, but with additional extension options, this is what the editor does for you. For example, you have a foo.jpg texture, a file with import options should be called foo.jpg.options. Its content may look something like this: ( minification_filter: Linear, magnification_filter: Linear, s_wrap_mode: Repeat, t_wrap_mode: ClampToEdge, anisotropy: 8.0, compression: NoCompression, ) Even if it is possible to modify it by hand, it is strongly advised to use the editor to edit import options, because it reduces chances of messing up.","breadcrumbs":"Resource Management » Textures » Import options","id":"364","title":"Import options"},"365":{"body":"Texture can be used as a render target to render a scene in it. To do this you should use new_render_target method and pass its result to scene's render target property. Renderer will automatically provide you info about metrics of texture, but it won't give you access to pixels of render target.","breadcrumbs":"Resource Management » Textures » Render target","id":"365","title":"Render target"},"366":{"body":"","breadcrumbs":"Resource Management » Sound Buffers (WIP) » Sound (WIP)","id":"366","title":"Sound (WIP)"},"367":{"body":"","breadcrumbs":"Resource Management » Curves (WIP) » Curve (WIP)","id":"367","title":"Curve (WIP)"},"368":{"body":"In Fyrox, you can create your own, custom resource type that can be embedded in the standard resource management pipeline. It could be useful to access specific data using engine's resource manager. Custom resources has a few major advantages over manual resource management via direct files access: Since Fyrox resource system is asynchronous, your resource can be loaded in separate worker thread which speeds up loading (since it may run on a separate CPU core). You can access your resources from the Asset Browser and assign their handles to scripts directly from the editor. File access for resource management has an abstraction, that unifies the access over all supported platforms. This means that you don't need to use fetch API directly, if you're targeting WebAssembly platform, or use AssetManager on Android. To create a custom resource, you need to do three major steps: Define your resource structure with all required traits implemented. Add a custom resource loader, that will be used by the resource manager to load your custom resource. Register the resource loader in the resource manager. See the code snippet in the next section as a guide.","breadcrumbs":"Resource Management » Custom Resource » Custom Resources","id":"368","title":"Custom Resources"},"369":{"body":"Custom resource is just an ordinary struct with some data. It must implement Debug, Reflect, Visit, ResourceData traits. Also, it must contain at least path to external file with the content. Here's the simplest custom resource, that contains some string data. # extern crate fyrox;\n# use fyrox::{\n# asset::{\n# event::ResourceEventBroadcaster,\n# loader::{BoxedLoaderFuture, ResourceLoader},\n# untyped::UntypedResource,\n# ResourceData,\n# },\n# core::{\n# io::{self},\n# reflect::prelude::*,\n# uuid::{uuid, Uuid},\n# visitor::prelude::*,\n# TypeUuidProvider,\n# },\n# };\n# use std::{\n# any::Any,\n# borrow::Cow,\n# path::{Path, PathBuf},\n# };\n# #[derive(Debug, Visit, Reflect)]\nstruct CustomResource { // You resource must store the path. path: PathBuf, some_data: String,\n} impl TypeUuidProvider for CustomResource { // Every resource must provide a unique identifier, that is used for dynamic type // casting, serialization, etc. fn type_uuid() -> Uuid { uuid!(\"15551157-651b-4f1d-a5fb-6874fbfe8637\") }\n} impl ResourceData for CustomResource { fn path(&self) -> Cow { Cow::Borrowed(&self.path) } fn set_path(&mut self, path: PathBuf) { self.path = path; } fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn type_uuid(&self) -> Uuid { ::type_uuid() } fn is_procedural(&self) -> bool { false }\n} struct CustomResourceLoader; impl ResourceLoader for CustomResourceLoader { fn extensions(&self) -> &[&str] { // An array of extensitions, supported by this loader. There could be any number of extensions // since sometimes multiple extensions map to a single resource (for instance, jpg, png, bmp, are // all images). &[\"my_resource\"] } fn data_type_uuid(&self) -> Uuid { ::type_uuid() } fn load( &self, resource: UntypedResource, event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { Box::pin(async move { let path = resource.path(); match io::load_file(&path).await { Ok(content) => { let my_resource = CustomResource { path, some_data: String::from_utf8(content).unwrap(), }; resource.commit_ok(my_resource); // Notify potential subscribers that the resource was loader. event_broadcaster.broadcast_loaded_or_reloaded(resource, reload); } Err(err) => { resource.commit_error(path, err); } } }) }\n} Keep in mind, that you must provide unique UUID for every resource type that you're creating. Otherwise, using existing id multiple times will cause incorrect serialization and type casting. The next step is to register the new resource in the resource manager. This can be done by adding the following code to the register method for impl PluginConstructor for GameConstructor: # extern crate fyrox;\n# use fyrox::{\n# asset::{\n# event::ResourceEventBroadcaster,\n# loader::{BoxedLoaderFuture, ResourceLoader},\n# untyped::UntypedResource,\n# },\n# core::{pool::Handle, uuid::Uuid},\n# plugin::{Plugin, PluginConstructor, PluginContext, PluginRegistrationContext},\n# scene::Scene,\n# };\n# # struct CustomResourceLoader;\n# # impl ResourceLoader for CustomResourceLoader {\n# fn extensions(&self) -> &[&str] {\n# todo!()\n# }\n# # fn data_type_uuid(&self) -> Uuid {\n# todo!()\n# }\n# # fn load(\n# &self,\n# resource: UntypedResource,\n# event_broadcaster: ResourceEventBroadcaster,\n# reload: bool,\n# ) -> BoxedLoaderFuture {\n# todo!()\n# }\n# }\n# # pub struct GameConstructor;\n# impl PluginConstructor for GameConstructor { fn register(&self, context: PluginRegistrationContext) { context .resource_manager .state() .loaders .set(CustomResourceLoader); // ... }\n# # fn create_instance(\n# &self,\n# scene_path: Option<&str>,\n# context: PluginContext,\n# ) -> Box {\n# todo!()\n# }\n} After doing so, any attempt to load a resource with my_resource extension will call the load method of your resource loader.","breadcrumbs":"Resource Management » Custom Resource » Example","id":"369","title":"Example"},"37":{"body":"There is no need for it. The current implementation works and is more than good enough. So instead of focusing on replacing something that works for little to no benefit, the current focus is on adding features that are missing as well as improving existing features when needed.","breadcrumbs":"Introduction » Frequently Asked Questions » Why not use alternatives now?","id":"37","title":"Why not use alternatives now?"},"370":{"body":"There's one more step before your custom resource is fully usable - you need to register a property editor for it, so any fields in your scripts that has my_resource: Option > fields can be editable in the editor. Otherwise, you'll see an error message in the Inspector instead of resource selector field. To register a property editor, add the following lines to editor/src/main.rs file, somewhere after the editor instance is created: editor.inspector.property_editors.insert( ResourceFieldPropertyEditorDefinition:: ::new( Rc::new(|resource_manager, path| { resource_manager .try_request:: (path) .map(|r| block_on(r)) }), editor.message_sender.clone(), ),\n); After this, the editor will create this property editor for my_resource field and will allow you to set its value by drag'n'dropping an asset from the Asset Browser.","breadcrumbs":"Resource Management » Custom Resource » Editor Support","id":"370","title":"Editor Support"},"371":{"body":"","breadcrumbs":"Resource Management » Hot Reloading (WIP) » Hot Reloading (WIP)","id":"371","title":"Hot Reloading (WIP)"},"372":{"body":"Fyrox features an extremely powerful and flexible node-based user interface system. Power and flexibility comes with a certain price: it has a steep learning curve. This chapter will cover user interface usage in the engine, explain basic concepts, provide information about most commonly used widgets, and so on.","breadcrumbs":"User Interface » User Interface","id":"372","title":"User Interface"},"373":{"body":"You can explore UI system capabilities in this web demo . Keep in mind, that it was designed to run on PC and wasn't tested on mobile devices.","breadcrumbs":"User Interface » Web Demo","id":"373","title":"Web Demo"},"374":{"body":"This chapter should help you understand basic concepts lying in the foundation of the GUI in the engine.","breadcrumbs":"User Interface » Basic concepts » Basic concepts","id":"374","title":"Basic concepts"},"375":{"body":"Stateful UI means that we can create and destroy widgets when we need to, it is the opposite approach of immediate-mode or stateless UIs when you don't have long-lasting state for your widgets (usually stateless UI hold its state only for one or two frames). Stateful UI is much more powerful and flexible, it allows you to have complex layout system without having to create hacks to create complex layout as you'd do in immediate-mode UIs. It is also much faster in terms of performance. Stateful UI is a must for complex user interfaces that requires rich layout and high performance. I'm not telling that you can't do it in immediate mode UI, you can, but using tons of hacks. See Layout section for more info.","breadcrumbs":"User Interface » Basic concepts » Stateful","id":"375","title":"Stateful"},"376":{"body":"The UI system is designed to be used in a classic model-view-controller MVC approach. Model in this case is your game state, view is the UI system, controller is your event handlers. In other words - the UI shows what happens in your game and does not store any game-related information. This is quite old, yet powerful mechanism that decouples UI code from game code very efficiently and allows you to change game code and UI code independently.","breadcrumbs":"User Interface » Basic concepts » Model-View-Controller","id":"376","title":"Model-View-Controller"},"377":{"body":"Every user interface could be represented as a set of small blocks that have hierarchical bonding between each other. For example a button could be represented using two parts: a background and a foreground. Usually the background is just a simple rectangle (either a vector or bitmap), and a foreground is a text. The text (the foreground widget) is a child object of the rectangle (the background widget). These two widgets forms another, more complex widget that we call button. Graphically it will look like this: Button On the right side of the image we can see the generic button and on the left side, we can see its hierarchical structure. Such approach allows us to modify the look of the button as we wish, we can create a button with image background, or with any vector image, or even other widgets. The foreground can be anything too, it can also contain its own complex hierarchy, like a pair of an icon with a text and so on.","breadcrumbs":"User Interface » Basic concepts » Node-based architecture","id":"377","title":"Node-based architecture"},"378":{"body":"Every widget in the engine uses composition to build more complex widgets. All widgets (and respective builders) contains Widget instance inside, it provides basic functionality the widget such as layout information, hierarchy, default foreground and background brushes (their usage depends on derived widget), render and layout transform and so on.","breadcrumbs":"User Interface » Basic concepts » Composition","id":"378","title":"Composition"},"379":{"body":"Many widgets provide component querying functionality - you can get an immutable reference to inner component by its type. It is used instead of type casting in many places. Component querying is much more flexible compared to direct type casting. For example, you may want to build a custom Tree widget, you want your CustomTree to inherit all the functionality from the Tree, but add something new. The Tree widget can manage its children subtrees, but it needs to somehow get required data from subtree. Direct type casting would fail in this case, because now you have something like this: # extern crate fyrox;\n# use fyrox::gui::tree::Tree;\nstruct CustomTree { tree: Tree, my_data: u32\n} On other hand, component querying will work fine, because you can query inner component (Tree in our case). Please note that this has nothing similar with ECS and stuff, it is made to circumvent Rust's lack of inheritance.","breadcrumbs":"User Interface » Basic concepts » Component Querying","id":"379","title":"Component Querying"},"38":{"body":"No, the engine uses a mixed composition-based, object-oriented design with message passing and other different approaches that fit the most for a particular task. Why not use ECS for everything, though? Pragmatism. Use the right tool for the job. Don't use a microscope to hammer nails.","breadcrumbs":"Introduction » Frequently Asked Questions » Is the engine based on ECS?","id":"38","title":"Is the engine based on ECS?"},"380":{"body":"The engine uses message passing mechanism for any UI logic. What does that mean? Let's see at the button from the previous section and imagine we want to change its text. To do that we need to explicitly \"tell\" the button's text widget to change its content to something new. This is done by sending a message to the widget. There is no classic callbacks to handle various types of messages, which may come from widgets. Instead, you should write your own message dispatcher where you'll handle all messages. Why so? At first - decoupling, in this case business logic is decoupled from the UI. You just receive messages one-by-one and do specific logic. The next reason is that any callback would require context capturing which could be somewhat restrictive - since you need to share context with the UI, it would force you to wrap it in Rc >/Arc >. Message dispatcher is very easy to write, all you need to do is to handle UI messages in Plugin::on_ui_message method: # extern crate fyrox;\n# use fyrox::{\n# core::{pool::Handle},\n# event_loop::ControlFlow,\n# gui::{button::ButtonMessage, message::UiMessage, UiNode},\n# plugin::{Plugin, PluginContext},\n# };\n# struct MyPlugin { button: Handle ,\n} impl Plugin for MyPlugin { fn on_ui_message( &mut self, _context: &mut PluginContext, message: &UiMessage, _control_flow: &mut ControlFlow, ) { if let Some(ButtonMessage::Click) = message.data() { if message.destination() == self.button { println!(\"The button was clicked!\"); } } }\n} As you can see, all you need to do is to check type of incoming message and message destination, which is a node handle from which a message was come from. Then you do any actions you want.","breadcrumbs":"User Interface » Basic concepts » Message passing","id":"380","title":"Message passing"},"381":{"body":"Message passing mechanism works in pair with various routing strategies that allows you to define how the message will \"travel\" across the tree of nodes. Bubble - a message starts its way from a widget and goes up on hierarchy until it reaches root node of hierarchy. Nodes that lies outside that path won't receive the message. This is the most important message routing strategy, that is used for every node by default. Direct - a message passed directly to every node that are capable to handle it. There is actual routing in this case. Direct routing is used in rare cases when you need to catch a message outside its normal \"bubble\" route. Bubble message routing is used to handle complex hierarchies of widgets with ease. Let's take a look at the button example above - it has text widget as a content and when, for instance, you hover a mouse over the text widget the UI system creates a \"mouse moved\" message and sends it to the text. Once it was processed by the text, it \"floats\" one level of hierarchy up - to the button widget itself. This way the button widget can process mouse events as well.","breadcrumbs":"User Interface » Basic concepts » Message routing strategies","id":"381","title":"Message routing strategies"},"382":{"body":"The UI systems uses complex, yet powerful layout system that allows you to build complex user interfaces with complex layout. Layout pass has two recursive sub-passes: Measurement - the sub-pass is used to fetch the desired size of each widget in hierarchy. Each widget in the hierarchy \"asked\" for its desired size with the constraint from a parent widget. This step is recursive - to know a desired size of a widget root of some hierarchy you need to recursively fetch the desired sizes of every descendant. Arrangement - the sub-pass is used to set final position and size of each widget in hierarchy. It uses desired size of every widget from the previous step to set the final size and relative position. This step is recursive. Such separation in two passes is required because we need to know desired size of each node in hierarchy before we can actually do an arrangement.","breadcrumbs":"User Interface » Basic concepts » Layout","id":"382","title":"Layout"},"383":{"body":"The UI system supports both ways of making a UI: Code-first approach is used when your user interface is procedural and its appearance is heavily depends on your game logic. In this case you need to use various widget builder to create UIs. Editor-first approach is used when you have relatively static (animations does not count) user interface, that almost does not change in time. In this case you can use built-in WYSIWYG (what-you-see-is-what-you-get) editor. See Editor chapter for more info. In case of code-first approach you should prefer so-called fluent syntax : this means that you can create your widget in series of nested call of other widget builders. In code, it looks something like this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle, resource::texture::Texture,\n# asset::manager::ResourceManager,\n# gui::{\n# button::ButtonBuilder, image::ImageBuilder, widget::WidgetBuilder, UiNode,\n# UserInterface,\n# },\n# utils::into_gui_texture,\n# };\n# fn create_fancy_button(ui: &mut UserInterface, resource_manager: ResourceManager) -> Handle {\n# let ctx = &mut ui.build_ctx();\nButtonBuilder::new(WidgetBuilder::new()) .with_back( ImageBuilder::new(WidgetBuilder::new()) .with_texture(into_gui_texture( resource_manager.request:: (\"path/to/your/texture\"), )) .build(ctx), ) .with_text(\"Click me!\") .build(ctx)\n# } This code snippet creates a button with an image and a text. Actually it creates three widgets, that forms complex hierarchy. The topmost widget in hierarchy is the Button widget itself, it has two children widgets: background image and a text. Background image is set explicitly by calling image widget builder with specific texture. The text is created implicitly, the button builder creates Text widget for you and attaches it to the button. The structure of the button can contain any amount of nodes, for example you can create a button that contains text with some icon. To do that, replace .with_text(\"My Button\") with this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::texture::Texture,\n# gui::{\n# button::ButtonBuilder,\n# grid::{Column, GridBuilder, Row},\n# image::ImageBuilder,\n# text::TextBuilder,\n# widget::WidgetBuilder,\n# UiNode, UserInterface,\n# },\n# utils::into_gui_texture,\n# };\n# # fn create_fancy_button(\n# ui: &mut UserInterface,\n# resource_manager: ResourceManager,\n# ) -> Handle {\n# let ctx = &mut ui.build_ctx();\n# # ButtonBuilder::new(WidgetBuilder::new()) .with_content( GridBuilder::new( WidgetBuilder::new() .with_child( ImageBuilder::new(WidgetBuilder::new().on_column(0)) .with_texture(into_gui_texture( resource_manager.request:: (\"your_icon\"), )) .build(ctx), ) .with_child( TextBuilder::new(WidgetBuilder::new().on_column(1)) .with_text(\"My Button\") .build(ctx), ), ) .add_row(Row::stretch()) .add_column(Column::auto()) .add_column(Column::stretch()) .build(ctx), )\n# .build(ctx)\n# } Quite often you need to store a handle to a widget in a variable, there is one neat trick to do that preserving the fluent syntax: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::texture::Texture,\n# gui::{\n# button::ButtonBuilder, image::ImageBuilder, widget::WidgetBuilder, UiNode,\n# UserInterface,\n# },\n# utils::into_gui_texture,\n# };\n# fn create_fancy_button(ui: &mut UserInterface, resource_manager: ResourceManager) -> Handle {\n# let ctx = &mut ui.build_ctx();\nlet image;\nButtonBuilder::new(WidgetBuilder::new()) .with_back({ image = ImageBuilder::new(WidgetBuilder::new()) .with_texture(into_gui_texture( resource_manager.request:: (\"path/to/your/texture\"), )) .build(ctx); image }) .with_text(\"Click me!\") .build(ctx)\n# }\n// image now contains a handle of the Image widget","breadcrumbs":"User Interface » Basic concepts » Code-first and Editor-first approaches","id":"383","title":"Code-first and Editor-first approaches"},"384":{"body":"UI system uses completely different kind of scenes - UI scenes, which are fully decoupled from game scenes. This means that you can't incorporate UI widgets in a game scene. As a consequence, you don't have an ability to attach scripts to widgets - their logic is strictly defined in their backing code. This limitation is intentional, and it is here only for one reason - decoupling of UI code from game logic. Currently, there's only one right approach to make UIs - to create widgets in your game plugin and sync the state of the widgets with game entities manually.","breadcrumbs":"User Interface » Basic concepts » Limitations","id":"384","title":"Limitations"},"385":{"body":"UI Editor User interface (UI) editor is used to create and edit UI scenes. Unlike many other game engines, UI scenes are completely different kind of scenes - their content cannot be mixed with standard game scenes. This is made on purpose - such separation enables a variety of optimizations and greatly improves flexibility.","breadcrumbs":"User Interface » Editor » UI Editor","id":"385","title":"UI Editor"},"386":{"body":"UI editor looks pretty much the same as the editor for game scenes, with the only difference that it is 2D only. On the left side it contains the world viewer, which is used to manipulate the tree of widgets. On the right side it has the inspector, which is used to edit properties of widgets. It also has just a few tools in the toolbar - selection and move tool.","breadcrumbs":"User Interface » Editor » Appearance","id":"386","title":"Appearance"},"387":{"body":"To start making a UI scene all you need to do is to create a new UI scene. This could be done from File -> New UI Scene menu. After this you'll see an example scene with a button and a text. You can either delete these widgets, or use them as you want. You can create any kinds of widgets in the UI scene, even custom-made ones. All you need to do is to click Create -> UI and select a desired widget, or you can also right-click on a widget in the world viewer and create a widget from the context menu. The widget created from the context menu will become a child widget of the widget from which you've opened the context menu. Widgets can form an arbitrary hierarchy, which could be changed by dragging a widget and dropping it on some other widget in the world viewer. Keep in mind, that some widgets may contain handles to other widgets, and you need to set them too. For example, the Button widget contains a handle of its content which is used to delete a current content when changing it to some other. If you'll leave button's content as unassigned handle, then your button may behave weirdly. Some of the widgets (like layout panels) works directly with their children widgets and do not have \"external\" handles.","breadcrumbs":"User Interface » Editor » Introduction","id":"387","title":"Introduction"},"388":{"body":"See a respective chapter for each widget to learn how it can be created and tweaked. Keep in mind, that UI editor is a new feature of the engine and the book's chapters could lack some information about the editor.","breadcrumbs":"User Interface » Editor » Widgets","id":"388","title":"Widgets"},"389":{"body":"The following video shows how to create a simple main menu for a 2D platformer.","breadcrumbs":"User Interface » Editor » Video","id":"389","title":"Video"},"39":{"body":"Pretty much any kind of games, except maybe games with vast open-worlds (since there's no built-in world streaming). In general, it depends on your game development experience.","breadcrumbs":"Introduction » Frequently Asked Questions » What kinds of games can I make using Fyrox?","id":"39","title":"What kinds of games can I make using Fyrox?"},"390":{"body":"User interface is usually rendered directly on screen, and in most cases this is enough. However, there are some specific cases when you need to incorporate user interface in your game scene as an interactive screen (holographic, for example) or to render some scene inside some UI element (to create some sort of in-game CCTV, for example). This chapter covers these specific cases and rendering nuances in general.","breadcrumbs":"User Interface » Rendering » Rendering","id":"390","title":"Rendering"},"391":{"body":"Offscreen rendering is used to render a UI into a texture, so it can be later used in your game scene. Here's a simple example - holographic inventory in sci-fi games: offscreen ui Default engine's user interface instance (accessible in context.user_interface from plugin methods) can't be rendered offscreen due to engine design. However, you can create a new user interface instance, populate it with widgets, and then render it to a texture. This could be done like so: # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector2, log::Log, pool::Handle, sstorage::ImmutableString},\n# engine::GraphicsContext,\n# event::Event,\n# event_loop::ControlFlow,\n# gui::{button::ButtonBuilder, widget::WidgetBuilder, UserInterface},\n# material::{Material, PropertyValue},\n# plugin::{Plugin, PluginContext},\n# resource::texture::{TextureResource, TextureResourceExtension},\n# scene::Scene,\n# utils,\n# };\n# struct Game { // Add these fields to your game. my_ui: UserInterface, render_target: TextureResource, screen_size: Vector2 ,\n} impl Game { pub fn new(override_scene: Handle , context: PluginContext) -> Self { // Add this code to your Game::new // Define the desired render target size. let width = 128; let height = 128; // Create render target and user interface. let render_target = TextureResource::new_render_target(width, height); let screen_size = Vector2::new(width as f32, height as f32); let mut my_ui = UserInterface::new(screen_size); // Create some widgets as usual. ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"Click Me!\") .build(&mut my_ui.build_ctx()); // Use render_target as an ordinary texture - it could be applied to any material like so: let mut material = Material::standard(); Log::verify(material.set_property( &ImmutableString::new(\"diffuseTexture\"), PropertyValue::Sampler { value: Some(render_target.clone()), fallback: Default::default(), }, )); // This material **must** be assigned to some mesh in your scene! Self { my_ui, render_target, screen_size, } }\n} impl Plugin for Game { fn on_os_event( &mut self, event: &Event<()>, context: PluginContext, control_flow: &mut ControlFlow, ) { // This is the tricky part. Event OS event handling will be different depending on the use case. // In cases if your UI just shows some information, this method can be fully removed. In case when // you need to interact with the UI, there are two different ways. // 1) If your UI will be incorporated in 2D scene, you need to patch mouse events - mostly positions // of the cursor so it will be in local coordinates. // 2) In 3D, it is much more complex - you need to patch mouse events as well, but use mouse OS events // to produce a picking ray and do an intersection test with a quad that will serve as a canvas for your // UI to obtain the local mouse coordinates. if let Event::WindowEvent { event, .. } = event { if let Some(event) = utils::translate_event(event) { self.my_ui.process_os_event(&event); } } } fn update(&mut self, context: &mut PluginContext, control_flow: &mut ControlFlow) { // It is very important to update the UI every frame and process all events that // comes from it. self.my_ui.update(self.screen_size, context.dt); while let Some(message) = self.my_ui.poll_message() { // Do something with the events coming from the custom UI. } } fn before_rendering(&mut self, context: PluginContext, control_flow: &mut ControlFlow) { // Render the UI before every other rendering operation, this way the texture will be ready for use immediately. if let GraphicsContext::Initialized(ref mut graphics_context) = context.graphics_context { Log::verify( graphics_context .renderer .render_ui_to_texture(self.render_target.clone(), &mut self.my_ui), ); } }\n} There's quite a lot of code, but it is quite simple and the comments should help you to understand which part does what. It uses standard plugin structure and contents of each method should be placed in the according methods in your game. This code creates a new user interface, a render target of appropriate size and renders the UI into the render target. The render target then could be used as a diffuse texture in one of your materials, which in its turn, can be assigned to pretty much any mesh in your game.","breadcrumbs":"User Interface » Rendering » Offscreen Rendering","id":"391","title":"Offscreen Rendering"},"392":{"body":"It is possible to \"embed\" a scene into arbitrary user interface. This could be useful if you need to create some sort of CCTV, or to show 3D graphics in 2D user interface and so on. To do so, you need to specify a render target for your scene and then use the texture (render target) in an Image widget. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{image::ImageBuilder, widget::WidgetBuilder, UiNode},\n# plugin::PluginContext,\n# resource::texture::{TextureResource, TextureResourceExtension},\n# scene::Scene,\n# utils,\n# };\n# fn reroute_scene_rendering( width: u32, height: u32, scene: &mut Scene, context: &mut PluginContext,\n) -> Handle { // Create render target first. let render_target = TextureResource::new_render_target(width, height); // Specify render target for the scene. scene.rendering_options.render_target = Some(render_target.clone()); // The scene will be drawn in this image widget. ImageBuilder::new( WidgetBuilder::new() .with_width(width as f32) .with_height(height as f32), ) .with_texture(utils::into_gui_texture(render_target.clone())) .build(&mut context.user_interface.build_ctx())\n} This function could be used as-is to re-route rendering of a scene to an Image widget. It creates a new render target first, then it assigns it to the scene, and also it creates a new Image widget with the render target as a texture. A simple example of what this code does is the scene previewer in the editor: rerouting If you set width and height to match your screen size, you'll create a simple \"overlay\" that will allow you to render scene entities on top of the UI. In this case, however, you also need to configure scene camera accordingly, and probably use orthographic projection so the coordinates would match.","breadcrumbs":"User Interface » Rendering » Embedding Scene into UI","id":"392","title":"Embedding Scene into UI"},"393":{"body":"Font is used to store graphical representation of unicode characters. The engine supports TTF and OTF fonts, you can load pretty much any font from the internet and use it as is.","breadcrumbs":"User Interface » Fonts » Font","id":"393","title":"Font"},"394":{"body":"There are two ways to create font instance - either load font from file, or load it directly from memory.","breadcrumbs":"User Interface » Fonts » Create New Font","id":"394","title":"Create New Font"},"395":{"body":"Since every font in the engine is a resource, you can load fonts using standard resource manager like so: # extern crate fyrox;\n# use fyrox::{asset::Resource, gui::font::Font, plugin::PluginContext};\n# fn load_font_from_file(ctx: &PluginContext) -> Resource { ctx.resource_manager.request::(\"path/to/my/font\")\n}","breadcrumbs":"User Interface » Fonts » Loading Font From File","id":"395","title":"Loading Font From File"},"396":{"body":"This option could be useful, if you already have your font loaded into memory. Loading font from data buffer is very simple: # extern crate fyrox;\n# use fyrox::{asset::untyped::ResourceKind, asset::Resource, gui::font::Font};\n# fn load_font_from_memory(data: Vec ) -> Resource { Resource::new_ok( ResourceKind::Embedded, Font::from_memory(data, 1024).unwrap(), )\n} data input parameter could be a buffer that contains any valid TTF/OTF font. For example, you can load TTF file into a data buffer and create font using the data buffer: # extern crate fyrox;\n# use fyrox::{asset::untyped::ResourceKind, asset::Resource, gui::font::Font};\n# use std::{fs::File, io::Read};\n# fn load_font_from_memory() -> Resource { let mut data = Vec::new(); File::open(\"path/to/your/font.ttf\") .unwrap() .read_to_end(&mut data) .unwrap(); Resource::new_ok( ResourceKind::Embedded, Font::from_memory(data, 1024).unwrap(), )\n}","breadcrumbs":"User Interface » Fonts » Creating Font From Memory","id":"396","title":"Creating Font From Memory"},"397":{"body":"User interface provides its own font of fixed size, it is enough to cover most of the use cases, but the default font includes only ASCII characters, if you need extended character set, you can replace default font using the following code snippet: # extern crate fyrox;\n# use fyrox::gui::{ttf::Font, UserInterface};\nasync fn set_default_font(ui: &mut UserInterface) { // Select character set. let character_set = Font::korean_char_set(); // Load font. let new_font = Font::from_file(\"path/to/your/font.ttf\", 20.0, character_set) .await .unwrap(); // Set as default font. ui.default_font.set(new_font)\n}","breadcrumbs":"User Interface » Fonts » Default Font","id":"397","title":"Default Font"},"398":{"body":"All you need to do is to set font size in your Text or TextBox widgets like so: use fyrox::{ core::pool::Handle, gui::{text::TextBuilder, widget::WidgetBuilder, BuildContext, UiNode},\n}; fn text(ctx: &mut BuildContext) -> Handle { TextBuilder::new(WidgetBuilder::new()) .with_text(\"Some text\") .with_font_size(30.0) // Sets the desired font size. .build(ctx)\n} You can also change the font size at runtime using TextMessage::FontSize message like so: # use fyrox::{\n# core::pool::Handle,\n# gui::{message::MessageDirection, text::TextMessage, UiNode, UserInterface},\n# };\n# fn set_font_size(text: Handle , ui: &UserInterface, new_font_size: f32) { ui.send_message(TextMessage::font_size( text, MessageDirection::ToWidget, new_font_size, ))\n}","breadcrumbs":"User Interface » Fonts » How to Change Font Size","id":"398","title":"How to Change Font Size"},"399":{"body":"Internally, font may use a number of texture atlases to pack all glyphs into a single texture. Font system creates a new atlas for every glyph size. Each atlas could be split into multiple pages, which essentially just a texture of a fixed size. Such paging is needed, because there's a hardware limit of maximum texture size on video cards and instead of praying that everything fits into a single page, the engine automatically adds a new page if none of the previous cannot fit a new character. Keep in mind, that when you create a font, its atlases are empty. They're filled on demand when you try to use a character that wasn't used previously.","breadcrumbs":"User Interface » Fonts » Important notes","id":"399","title":"Important notes"},"4":{"body":"We're expecting that you know basics of Rust programming language, its package manager Cargo. It is also necessary to know the basics of the game development, linear algebra, principles of software development and patterns, otherwise the book will probably be very hard for you.","breadcrumbs":"About the Book » Required knowledge","id":"4","title":"Required knowledge"},"40":{"body":"This section of the book will guide you through the basics of the engine. You will learn how to create a project, use plugins, scripts, assets, and the editor. Fyrox is a modern game engine with its own scene editor, that helps you to edit game worlds, manage assets, and many more. At the end of reading this section, you'll also learn how to manage game and engine entities, how they're structured and what are the basics of data management in the engine. Next chapter will guide you through major setup of the engine - creating a game project using special project generator tool.","breadcrumbs":"Getting started » Getting Started","id":"40","title":"Getting Started"},"400":{"body":"The engine has an ability to customize the appearance of widgets, however it is not centralized, and has to be done per widget. Check Style section of each widget (keep in mind that some of the widgets does not support custom styles, mainly because they were made in a hurry). In general, styling of widgets could be performed by replacing parts of a widget with your own. For example, a button by default uses Decorator widget as its background, which in its turn uses simple set of brushes to control internal Border widget's parameters, such as background and foreground colors. This is ok if you're creating some tools, where you don't need bells and whistles. However, buttons in games could be of any shape, color, have any kind of animations and so on. For this reason, Button widget allows you to change background widget with your own. So, imagine that you have a button template with two images that represents its state - Pressed and Normal. In this case you could create a custom widget, that will render different images depending on pressed state and use this widget as a background widget of your button. The same applies to pretty much every other widget, for example CheckBox allows you to change check marks for every of three states as well as a widget that is used as a background.","breadcrumbs":"User Interface » Style » Styles","id":"400","title":"Styles"},"401":{"body":"The subsections of this chapter explains how to use every widget built into Fyrox. The widgets in the table of contents to the left are ordered in alphebetical order. However, below we will order them by primary function to help introduce them to new users.","breadcrumbs":"User Interface » Widgets » Widgets","id":"401","title":"Widgets"},"402":{"body":"The Container widgets primary purpose is to contain other widgets. They are mostly used as a tool to layout the UI in visually different ways. Stack panel : The Stack Panel arranges widgets in a linear fashion, either vertically or horizontally depending on how it's setup. Wrap Panel : The Wrap Panel arranges widgets in a linear fashion but if it overflows the widgets are continued adjacent to the first line. Can arrange widgets either vertically or horizontally depending on how it's setup. Grid : The Grid arranges widgets into rows and columns with given size constraints. Canvas : The Canvas arranges widgets with pixel perfect precision. Window : The Window holds other widgets in a panel that can be configured at setup to be move-able, expanded and contracted via user input, exited, and have a displayed label. The window has a title bar to assist with these features. Message Box : The Message Box is a Window that has been streamlined to show standard confirmation/information dialogues, for example, closing a document with unsaved changes. It has a title, some text, and a fixed set of buttons (Yes, No, Cancel in different combinations). Menu : The Menu is a root container for Menu Items, an example could be a menu strip with File, Edit, View, etc items. Popup : The Popup is a panel that locks input to its content while it is open. A simple example of it could be a context menu. Scroll Viewer : The ScrollViewer is a wrapper for Scroll Panel that adds two scroll bars to it. Scroll Panel : The Scroll Panel is a panel that allows you apply some offset to children widgets. It is used to create \"scrollable\" area in conjunction with the Scroll Viewer. Expander : The Expander handles hiding and showing multiple panels of widgets in an accordian style UI element. Multiple panels can be shown or hidden at any time based on user input. Tab Control : The Tab Control handles hiding several panels of widgets, only showing the one that the user has selected. Docking Manager : The Docking manager allows you to dock windows and hold them in-place. Tree : The Tree allows you to create views for hierarchical data.","breadcrumbs":"User Interface » Widgets » Containers","id":"402","title":"Containers"},"403":{"body":"The Visual widgets primary purpose is to provide the user feedback generally without the user directly interacting with them. Text : The Text widget is used to display a string to the user. Image : The Image widget is used to display a pixel image to the user. Vector Image : The Vector Image is used to render vector instructions as a graphical element. Rect : The Rect allows you to specify numeric values for X, Y, Width, and Height of a rectangle. Progress Bar : The Progress Bar shows a bar whoes fill state can be adjusted to indicate visually how full something is, for example how close to 100% is a loading process. Decorator : The Decorator is used to style any widget. It has support for different styles depending on various events like mouse hover or click. Border : The Border widget is used in conjunction with the Decorator widget to provide configurable boarders to any widget for styling purposes.","breadcrumbs":"User Interface » Widgets » Visual","id":"403","title":"Visual"},"404":{"body":"Control widgets primary purpose is to provide users with intractable UI elements to control some aspect of the program. Button : The Button provides a press-able control that can contain other UI elements, for example a Text or Image Widget. Check Box : The Check Box is a toggle-able control that can contain other UI elements, for example a Text or Image Widget. Text Box : The Text Box is a control that allows the editing of text. Scroll Bar : The Scroll Bar provides a scroll bar like control that can be used on it's own as a data input or with certain other widgets to provide content scrolling capabilities. Numeric Field : The Numeric Field provides the ability to adjust a number via increment and decrement buttons or direct input. The number can be constrained to remain inside a specific range or have a specific step. Range : The Range allows the user to edit a numeric range - specify its begin and end values. List View : The List View provides a control where users can select from a list of items. Dropdown List : The Drop-down List is a control which shows the currently selected item and provides a drop-down list to select an item. File Browser : The File Browser is a tree view of the file system allowing the user to select a file or folder. Curve Editor : The CurveEditor allows editing parametric curves - adding points, and setting up transitions (constant, linear, cubic) between them. Inspector : The Inspector automatically creates and handles the input of UI elements based on a populated Inspector Context given to it allowing the user to adjust values of a variety of models without manually creating UI's for each type.","breadcrumbs":"User Interface » Widgets » Controls","id":"404","title":"Controls"},"405":{"body":"It is possible to create your own widgets, that could solve a specific task that couldn't be solved easily with the widgets that the engine provides. Let's say, for instance, that we need to have a custom button with specific visual effects. It will have a border and a text, and it will also react to mouse events: custom widget A \"skeleton\" of such widget could be something like this (for now it does nothing): # extern crate fyrox;\n# use fyrox::{\n# core::{pool::Handle, reflect::prelude::*, visitor::prelude::*},\n# gui::{\n# message::UiMessage, widget::Widget, widget::WidgetMessage, Control, UiNode,\n# UserInterface, define_widget_deref\n# },\n# };\n# use std::{\n# any::{Any, TypeId},\n# ops::{Deref, DerefMut},\n# };\n# #[derive(Clone, Debug, Reflect, Visit)]\nstruct MyButton { widget: Widget, border: Handle , text: Handle ,\n} define_widget_deref!(MyButton); impl Control for MyButton { fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> { if type_id == TypeId::of:: () { Some(self) } else { None } } fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) { // Pass another message to the base widget first. self.widget.handle_routed_message(ui, message); }\n} Every widget in the engine must have an instance of Widget (widget: Widget field) type in them and implement the Control trait with two required methods. query_component is used for dynamic component fetching and could be used to support behavior mix-ins and support derived widgets, that based on some other widgets. There's a lot more of available methods in the Control trait, however for simplicity we won't use it in this chapter. handle_routed_message is used to react to various messages, but only in the child -> parent -> parent_of_parent -> ... chain. For example, if some of the child widget of our button will receive a message, it will also be passed to our button, then to a parent of our button (if any), etc. This routing strategy is called \"bubble\" routing (it acts like a bubble of air in the water; it always goes up). See this section for more info.","breadcrumbs":"User Interface » Widgets » Custom widget » Custom Widget","id":"405","title":"Custom Widget"},"406":{"body":"Now let's add some logic to the button, that will handle various mouse events. The full version of our button widget's logic will be like so: # extern crate fyrox;\n# use fyrox::{\n# core::{\n# color::{Color, Hsv},\n# pool::Handle,\n# reflect::prelude::*,\n# visitor::prelude::*,\n# },\n# gui::{\n# border::BorderBuilder,\n# brush::Brush,\n# define_constructor, define_widget_deref,\n# message::{MessageDirection, UiMessage},\n# text::TextBuilder,\n# widget::{Widget, WidgetBuilder, WidgetMessage},\n# BuildContext, Control, HorizontalAlignment, Thickness, UiNode, UserInterface,\n# VerticalAlignment,\n# },\n# };\n# use std::{\n# any::{Any, TypeId},\n# ops::{Deref, DerefMut},\n# };\n# #[derive(Debug, Clone, PartialEq)]\npub enum MyButtonMessage { // A message, that will be emitted when our button is clicked. Click,\n} impl MyButtonMessage { // A constructor for `Click` message. define_constructor!( MyButtonMessage:Click => fn click(), layout: false );\n} #[derive(Clone, Debug, Reflect, Visit)]\nstruct MyButton { widget: Widget, border: Handle , text: Handle ,\n} define_widget_deref!(MyButton); impl MyButton { fn set_colors(&self, ui: &UserInterface, text_color: Color, border_color: Color) { for (handle, color) in [(self.border, border_color), (self.text, text_color)] { ui.send_message(WidgetMessage::foreground( handle, MessageDirection::ToWidget, Brush::Solid(color), )); } // Make the fill brush of the border slightly dimmer than the input value. let mut border_color = Hsv::from(border_color); border_color.set_brightness(border_color.brightness() - 20.0); ui.send_message(WidgetMessage::background( self.border, MessageDirection::ToWidget, Brush::Solid(border_color.into()), )); }\n} impl Control for MyButton { fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> { if type_id == TypeId::of:: () { Some(self) } else { None } } fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) { // Pass another message to the base widget first. self.widget.handle_routed_message(ui, message); // Then process it in our widget. if let Some(msg) = message.data:: () { if message.destination() == self.handle() || self.has_descendant(message.destination(), ui) { match msg { WidgetMessage::MouseUp { .. } => { // Send the message to outside world, saying that the button was clicked. ui.send_message(MyButtonMessage::click( self.handle(), MessageDirection::FromWidget, )); ui.release_mouse_capture(); } WidgetMessage::MouseDown { .. } => { ui.capture_mouse(message.destination()); } WidgetMessage::MouseEnter => { // Make both the border and text brighter when the mouse enter the bounds of our button. self.set_colors( ui, Color::opaque(220, 220, 220), Color::opaque(140, 140, 140), ); } WidgetMessage::MouseLeave => { // Make both the border and text dimmer when the mouse leaves the bounds of our button. self.set_colors( ui, Color::opaque(120, 120, 120), Color::opaque(100, 100, 100), ); } _ => (), } } } }\n} As you can see, the most of the code was placed in handle_routed_message, we using it to respond for four messages: MouseDown + MouseUp, MouseEnter + MouseLeave. Let's look closely at each pair of the messages. The first two messages is used to handle clicks and send appropriate message to the outside world if a click has happened. When you're sending a message, it is not immediately processed, instead it is put in the common message queue and will be processed by the engine later. You can react to such messages to perform a desired action, see the section below for more info. The last two events are used to alter the visual appearance of the button by changing the colors of both the border and the text. The source code above is very simple and straightforward, despite the look of it.","breadcrumbs":"User Interface » Widgets » Custom widget » Custom Logic","id":"406","title":"Custom Logic"},"407":{"body":"Did you notice, that we didn't assign anything to border and text handles in our button widget? It is because, we haven't made a respective builder for our button. Builder is a separate structure, that collects all the info from the outside world and \"compiles\" it into a finished widget. Usually, widgets contains a bunch of children widgets, which in their turn could have their own children and so on. In our case, the button will have two child widgets: a border and a text. # extern crate fyrox;\n# use fyrox::{\n# core::{color::Color, pool::Handle, reflect::prelude::*, visitor::prelude::*},\n# gui::{\n# border::BorderBuilder,\n# brush::Brush,\n# define_constructor, define_widget_deref,\n# message::{MessageDirection, UiMessage},\n# text::TextBuilder,\n# widget::{Widget, WidgetBuilder, WidgetMessage},\n# BuildContext, Control, HorizontalAlignment, UiNode, UserInterface, VerticalAlignment,\n# },\n# };\n# use std::{\n# any::{Any, TypeId},\n# ops::{Deref, DerefMut},\n# };\n# # #[derive(Clone, Debug, Reflect, Visit)]\n# struct MyButton {\n# widget: Widget,\n# border: Handle ,\n# text: Handle ,\n# }\n# # define_widget_deref!(MyButton);\n# # impl Control for MyButton {\n# fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {\n# unimplemented!()\n# }\n# # fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {\n# unimplemented!()\n# }\n# }\n#\npub struct MyButtonBuilder { widget_builder: WidgetBuilder, // Some text of our button. text: String,\n} impl MyButtonBuilder { pub fn new(widget_builder: WidgetBuilder) -> Self { Self { widget_builder, text: Default::default(), } } pub fn with_text(mut self, text: String) -> Self { self.text = text; self } pub fn build(self, ctx: &mut BuildContext) -> Handle { let text = TextBuilder::new( WidgetBuilder::new() .with_vertical_alignment(VerticalAlignment::Center) .with_horizontal_alignment(HorizontalAlignment::Center), ) .with_text(self.text) .build(ctx); let border = BorderBuilder::new(WidgetBuilder::new().with_child(text)) .with_stroke_thickness(Thickness::uniform(2.0)) .build(ctx); let button = MyButton { widget: self.widget_builder.with_child(border).build(), border, text, }; ctx.add_node(UiNode::new(button)) }\n} This is how a button is created, at first we're creating a border widget instance with a text widget as a child of it. Text widget uses the actual text string from our builder, and also it sets the desired alignment in parent border's bounds. Finally, we're initializing an instance of MyButton with the handles of the widget we've just made and as the last step we're adding the widget to the user interface.","breadcrumbs":"User Interface » Widgets » Custom widget » Builder","id":"407","title":"Builder"},"408":{"body":"The widget could be created using the builder we've just made like so: MyButtonBuilder::new( WidgetBuilder::new() .with_width(200.0) .with_height(32.0)\n)\n.with_text(\"Click Me!\".to_string())\n.build(ctx);","breadcrumbs":"User Interface » Widgets » Custom widget » Using the Builder","id":"408","title":"Using the Builder"},"409":{"body":"Our button sends a Click message every time when it was pressed, and we can use this message to perform some actions in an application. All you need to do is to catch MyButtonMessage::Click in Plugin::on_ui_message and do something in response: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{message::UiMessage, UiNode},\n# plugin::{Plugin, PluginContext},\n# };\n# # #[derive(Debug, PartialEq)]\n# enum MyButtonMessage {\n# Click,\n# }\n# struct MyPlugin { my_button: Handle ,\n} impl Plugin for MyPlugin { fn on_ui_message(&mut self, context: &mut PluginContext, message: &UiMessage) { if message.destination() == self.my_button { if let Some(MyButtonMessage::Click) = message.data() { // Do something. } } }\n}","breadcrumbs":"User Interface » Widgets » Custom widget » Reacting to Click Messages","id":"409","title":"Reacting to Click Messages"},"41":{"body":"Every Fyrox game is just a plugin for both the engine and the editor, such approach allows you to run your game from the editor and to be able to edit the game entities in it. Your game can define any number of scripts, which can be assigned to scene objects to run custom game logic on them. In this chapter you'll learn the basics: how to install the engine with its platform-specific dependencies, how to use the plugins and scripting system, how to run the editor.","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Editor, Plugins and Scripts","id":"41","title":"Editor, Plugins and Scripts"},"410":{"body":"When do you need a custom widget? The answer depends on the use case, but the general rules here is quite simple: If your widget exist in a single instance, then there is no need to create a custom widget for it. If you need to create multiple instances of your widget, and each widget will carry some specific data, then you definitely need a custom widget. Custom widgets have some limitations that could be limiting, one of them is that custom widgets do not have access to your code, since they're \"living\" inside UI and know nothing about the \"environment\" where they're being used.","breadcrumbs":"User Interface » Widgets » Custom widget » Custom widget or composition of widgets.","id":"410","title":"Custom widget or composition of widgets."},"411":{"body":"Full source code for this chapter can be found here , and you can also run web demo to see it in action.","breadcrumbs":"User Interface » Widgets » Custom widget » Source Code and Web Demo","id":"411","title":"Source Code and Web Demo"},"412":{"body":"buttons","breadcrumbs":"User Interface » Widgets » Button » Button","id":"412","title":"Button"},"413":{"body":"To create a simple button with text you should do something like this: fn create_button(ui: &mut UserInterface) -> Handle { ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"Click me!\") .build(&mut ui.build_ctx())\n} How to create a button using custom dimensions (100x100) and custom text alignment (Vertical centered and Horizontal right aligned): fn create_button_custom(ui: &mut UserInterface) -> Handle { ButtonBuilder::new(WidgetBuilder::new().with_width(100.0).with_height(100.0)) .with_content( TextBuilder::new(WidgetBuilder::new()) .with_text(\"Click me!\") .with_horizontal_text_alignment(HorizontalAlignment::Right) .with_vertical_text_alignment(VerticalAlignment::Center) .build(&mut ui.build_ctx()), ) .build(&mut ui.build_ctx())\n}","breadcrumbs":"User Interface » Widgets » Button » Simple button with text","id":"413","title":"Simple button with text"},"414":{"body":"More fancy-looking button with an image as a background could be created using this code snippet: fn create_fancy_button( ui: &mut UserInterface, resource_manager: ResourceManager,\n) -> Handle { let ctx = &mut ui.build_ctx(); ButtonBuilder::new(WidgetBuilder::new()) .with_back( ImageBuilder::new(WidgetBuilder::new()) .with_texture( resource_manager .request:: (\"path/to/your/texture\") .into(), ) .build(ctx), ) .with_text(\"Click me!\") .build(ctx)\n}","breadcrumbs":"User Interface » Widgets » Button » A button with image","id":"414","title":"A button with image"},"415":{"body":"When clicked, a button sends a ButtonMessage::Click message, you can catch it in your code and do something useful: struct MyGame { button: Handle ,\n} impl Plugin for MyGame { fn on_ui_message(&mut self, context: &mut PluginContext, message: &UiMessage) { if let Some(ButtonMessage::Click) = message.data() { if message.destination() == self.button { // // Insert your code clicking handling code here. // } } }\n}","breadcrumbs":"User Interface » Widgets » Button » Message handling","id":"415","title":"Message handling"},"416":{"body":"This example shows how to create a button that will close your game. struct Game { quit_button_handle: Handle ,\n} fn create_quit_button(ui: &mut UserInterface) -> Handle { ButtonBuilder::new(WidgetBuilder::new()) .with_content( TextBuilder::new(WidgetBuilder::new()) .with_text(\"Quit\") .build(&mut ui.build_ctx()), ) .build(&mut ui.build_ctx())\n} impl Game { fn new(ctx: PluginContext) -> Self { Self { quit_button_handle: create_quit_button(ctx.user_interface), } }\n} impl Plugin for Game { fn on_ui_message(&mut self, context: &mut PluginContext, message: &UiMessage) { if let Some(ButtonMessage::Click) = message.data() { if message.destination() == self.quit_button_handle { if let Some(window_target) = context.window_target { window_target.exit(); } } } }\n}","breadcrumbs":"User Interface » Widgets » Button » Using a button to exit the game","id":"416","title":"Using a button to exit the game"},"417":{"body":"The Border widget provides a stylized, static border around its child widget. Below is an example of creating a 1 pixel thick border around a button widget: fn create_border_with_button(ui: &mut UserInterface) -> Handle { BorderBuilder::new( WidgetBuilder::new().with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"I'm boxed in!\") .build(&mut ui.build_ctx()), ), ) //You can also use Thickness::uniform(1.0) .with_stroke_thickness(Thickness { left: 1.0, right: 1.0, top: 1.0, bottom: 1.0, }) .build(&mut ui.build_ctx())\n} As with other UI elements, we create the border using the BorderBuilder helper struct. The widget that should have a border around it is added as a child of the base WidgetBuilder, and the border thickness can be set by providing a Thickness struct to the BorderBuilder's with_stroke_thickness function. This means you can set different thicknesses for each edge of the border. You can style the border by creating a Brush and setting the border's base WidgetBuilder's foreground or background. The foreground will set the style of the boarder itself, while setting the background will color the whole area within the border. Below is an example of a blue border and a red background with white text inside. fn create_blue_border_with_red_background(ui: &mut UserInterface) -> Handle { BorderBuilder::new( WidgetBuilder::new() .with_foreground(Brush::Solid(Color::opaque(0, 0, 200))) .with_background(Brush::Solid(Color::opaque(200, 0, 0))) .with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"I'm boxed in Blue and backed in Red!\") .build(&mut ui.build_ctx()), ), ) .with_stroke_thickness(Thickness { left: 2.0, right: 2.0, top: 2.0, bottom: 2.0, }) .build(&mut ui.build_ctx())\n}","breadcrumbs":"User Interface » Widgets » Border » Border","id":"417","title":"Border"},"418":{"body":"canvas Canvas is a panel widget that allows you to explicitly set coordinates for children widgets. It is useful when you need to manually control position of children widgets (like potions on the image above). As any other panel widget, it does not have its own graphical representation, so the image above shows only its positioning capabilities. Root UI node is also canvas, so any widgets that are not attached to any other widgets can have explicit position.","breadcrumbs":"User Interface » Widgets » Canvas » Canvas","id":"418","title":"Canvas"},"419":{"body":"Use CanvasBuilder to create Canvas instance: fn create_canvas(ctx: &mut BuildContext) -> Handle { CanvasBuilder::new(WidgetBuilder::new()).build(ctx)\n} Canvas does not have any specific options, so its creation is probably simplest of all widgets.","breadcrumbs":"User Interface » Widgets » Canvas » How to create","id":"419","title":"How to create"},"42":{"body":"Run the following commands to start using the engine as quick as possible. Read the next chapters if you want to know more or if you have any issues with this. cargo install fyrox-template\nfyrox-template init --name fyrox_test --style 2d\ncd fyrox_test\ncargo run --package editor --release","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Quick Start","id":"42","title":"Quick Start"},"420":{"body":"Use .with_desired_position on children widgets to set specific position: fn create_canvas_with_children_widgets(ctx: &mut BuildContext) -> Handle { CanvasBuilder::new( WidgetBuilder::new() .with_child( TextBuilder::new( WidgetBuilder::new().with_desired_position(Vector2::new(100.0, 200.0)), ) .with_text(\"Simple Text at (100.0, 200.0)\") .build(ctx), ) .with_child( ButtonBuilder::new( WidgetBuilder::new().with_desired_position(Vector2::new(200.0, 100.0)), ) .with_text(\"Simple Button at (200.0, 100.0)\") .build(ctx), ), ) .build(ctx)\n} The code snippet will create a canvas with a text widget located at (100.0, 200.0) relative to top-left corner of the canvas and a button located at (200.0, 100.0).","breadcrumbs":"User Interface » Widgets » Canvas » How to position children nodes","id":"420","title":"How to position children nodes"},"421":{"body":"Canvas provides infinite bounds for children widgets, this means that children nodes will not be stretched, instead they'll shrink to fit their content. For example, a button with a text will take slightly bigger rectangle than the text bounds.","breadcrumbs":"User Interface » Widgets » Canvas » Tips","id":"421","title":"Tips"},"422":{"body":"Checkbox is a UI widget that have three states - Checked, Unchecked and Undefined. In most cases it is used only with two values which fits in bool type. Third, undefined, state is used for specific situations when your data have such state.","breadcrumbs":"User Interface » Widgets » Check box » Check box","id":"422","title":"Check box"},"423":{"body":"Checkbox in Checked state: Checked Checkbox in Unchecked state: Unchecked","breadcrumbs":"User Interface » Widgets » Check box » How it looks","id":"423","title":"How it looks"},"424":{"body":"To create a checkbox you should do something like this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{check_box::CheckBoxBuilder, widget::WidgetBuilder, UiNode, UserInterface},\n# };\nfn create_checkbox(ui: &mut UserInterface) -> Handle { CheckBoxBuilder::new(WidgetBuilder::new()) // A custom value can be set during initialization. .checked(Some(true)) .build(&mut ui.build_ctx())\n} The above code will create a checkbox without any textual info, but usually checkboxes have some useful info near them. To create such checkbox, you could use .with_content(..) method which accepts any widget handle. For checkbox with text, you could use TextBuilder to create textual content, for checkbox with text - use ImageBuilder. As already said, you're free to use any widget handle there. Here's an example of checkbox with textual content. # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{\n# check_box::CheckBoxBuilder, text::TextBuilder, widget::WidgetBuilder, UiNode,\n# UserInterface,\n# },\n# };\nfn create_checkbox(ui: &mut UserInterface) -> Handle { let ctx = &mut ui.build_ctx(); CheckBoxBuilder::new(WidgetBuilder::new()) // A custom value can be set during initialization. .checked(Some(true)) .with_content( TextBuilder::new(WidgetBuilder::new()) .with_text(\"This is a checkbox\") .build(ctx), ) .build(ctx)\n}","breadcrumbs":"User Interface » Widgets » Check box » How to create","id":"424","title":"How to create"},"425":{"body":"Checkboxes are not static widget and have multiple states. To handle a message from a checkbox, you need to handle a CheckBoxMessage::Check message. To do so, you can do something like this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# engine::Engine,\n# event_loop::ControlFlow,\n# gui::{check_box::CheckBoxMessage, message::UiMessage, UiNode},\n# plugin::PluginContext,\n# };\n# # struct Foo {\n# checkbox: Handle ,\n# }\n# # impl Foo {\nfn on_ui_message( &mut self, context: &mut PluginContext, message: &UiMessage, control_flow: &mut ControlFlow,\n) { if let Some(CheckBoxMessage::Check(value)) = message.data() { if message.destination() == self.checkbox { // // Insert your clicking handling code here. // } }\n}\n# } Keep in mind that checkbox (as any other widget) generates WidgetMessage instances. You can catch them too and do a custom handling if you need.","breadcrumbs":"User Interface » Widgets » Check box » Message handling","id":"425","title":"Message handling"},"426":{"body":"Checkbox can be fully customized to have any look you want, there are few methods that will help you with customization: .with_content(..) - sets the content that will be shown near the checkbox. .with_check_mark(..) - sets the widget that will be used as checked icon. .with_uncheck_mark(..) - sets the widget that will be used as unchecked icon. .with_undefined_mark(..) - sets the widget that will be used as undefined icon.","breadcrumbs":"User Interface » Widgets » Check box » Theme","id":"426","title":"Theme"},"427":{"body":"curve editor","breadcrumbs":"User Interface » Widgets » Curve editor (WIP) » Curve editor (WIP)","id":"427","title":"Curve editor (WIP)"},"428":{"body":"A visual element that changes its appearance by listening specific events. It can have \"pressed\", \"hover\", \"selected\" or normal appearance: Pressed - enables on mouse down message. Selected - whether decorator selected or not. Hovered - mouse is over the decorator. Normal - not selected, pressed or hovered. This element is widely used to provide some generic visual behaviour for various widgets. For example, it used in buttons, tree items, dropdown list items, etc.; in other words - everywhere where a widget needs to give visual feedback the user.","breadcrumbs":"User Interface » Widgets » Decorator (WIP) » Decorator","id":"428","title":"Decorator"},"429":{"body":"docking manager Docking manager allows you to dock windows and hold them in-place. Docking manager can hold any types of UI elements, but dragging works only for windows.","breadcrumbs":"User Interface » Widgets » Docking manager (WIP) » Docking manager (WIP)","id":"429","title":"Docking manager (WIP)"},"43":{"body":"Before you start using the engine, make sure you have all required platform-specific development dependencies installed, otherwise you'll get compilation errors. If you're on Windows or macOS, you don't need to install anything specific - all you need to have is the latest Rust installed with appropriate toolchain for your platform.","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Platform-specific Dependencies","id":"43","title":"Platform-specific Dependencies"},"430":{"body":"dropdown list Drop-down list. This is control which shows currently selected item and provides drop-down list to select its current item. It is build using composition with standard list view.","breadcrumbs":"User Interface » Widgets » Dropdown list (WIP) » Dropdown list (WIP)","id":"430","title":"Dropdown list (WIP)"},"431":{"body":"expander Expander is a collapsible container for child widgets with a field that describes a content.","breadcrumbs":"User Interface » Widgets » Expander (WIP) » Expander (WIP)","id":"431","title":"Expander (WIP)"},"432":{"body":"file browser FileBrowser widget is a simple file system tree, FileSelector is a window with FileBrowser and few buttons.","breadcrumbs":"User Interface » Widgets » File browser (WIP) » File browser (WIP)","id":"432","title":"File browser (WIP)"},"433":{"body":"Grids are one of several methods to position multiple widgets in relation to each other. A Grid Widget, as the name implies, is able to position children widgets into a grid of specifically sized rows and columns. Here is a simple example that positions several text widgets into a 2 by 2 grid: # extern crate fyrox;\n# use fyrox::gui::{\n# UiNode,\n# BuildContext,\n# widget::WidgetBuilder,\n# text::TextBuilder,\n# grid::{GridBuilder, GridDimension},\n# }; fn create_text_grid(ctx: &mut BuildContext) -> fyrox::core::pool::Handle { GridBuilder::new( WidgetBuilder::new() .with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"top left \") .build(ctx) ) .with_child( TextBuilder::new( WidgetBuilder::new() .on_column(1) ) .with_text(\" top right\") .build(ctx) ) .with_child( TextBuilder::new( WidgetBuilder::new() .on_row(1) ) .with_text(\"bottom left \") .build(ctx) ) .with_child( TextBuilder::new( WidgetBuilder::new() .on_row(1) .on_column(1) ) .with_text(\" bottom right\") .build(ctx) ) ) .add_row(GridDimension::auto()) .add_row(GridDimension::auto()) .add_column(GridDimension::auto()) .add_column(GridDimension::auto()) .build(ctx)\n} As with other UI widgets, Grids are created via the GridBuilder struct. Each widget whose position should be controlled by the Grid should be added as a child of the GridBuilder's base widget. You then need to tell each child what row and column it belongs to via the on_column and on_row functions of their base widget. By default, all children will be placed into row 0, column 0. After that you need to provide sizing constraints for each row and column to the GridBuilder by using the add_row and add_column functions while providing a GridDimension instance to the call. GridDimension can be constructed with the following functions: GridDimension::auto() - Sizes the row or column so it's just large enough to fit the largest child's size. GridDimension::stretch() - Stretches the row or column to fill the parent's available space, if multiple rows or columns have this option the size is evenly distributed between them. GridDimension::strict(f32) - Sets the row or column to be exactly the given value of pixels long. So a row will only be the given number of pixels wide, while a column will be that many pixels tall. You can add any number of rows and columns to a grid widget, and each grid cell does not need to have a UI widget in it to be valid. For example you can add a column and set it to a specific size via strict to provide spacing between two other columns.","breadcrumbs":"User Interface » Widgets » Grid » Grid","id":"433","title":"Grid"},"434":{"body":"image Image widget is a rectangle with a texture, it is used draw custom bitmaps. The UI in the engine is vector-based, Image widget is the only way to draw a bitmap. Usage of the Image is very simple:","breadcrumbs":"User Interface » Widgets » Image » Image","id":"434","title":"Image"},"435":{"body":"# extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::texture::Texture,\n# gui::{image::ImageBuilder, widget::WidgetBuilder, BuildContext, UiNode},\n# utils::into_gui_texture,\n# }; fn create_image(ctx: &mut BuildContext, resource_manager: ResourceManager) -> Handle { // You must explicitly set width and height of the image, otherwise it will collapse to a // point and you won't see anything. let width = 100.0; let height = 100.0; ImageBuilder::new(WidgetBuilder::new().with_width(width).with_height(height)) .with_texture(into_gui_texture( // Ask resource manager to load a texture. resource_manager.request:: (\"path/to/your/texture.png\"), )) .build(ctx)\n} There are one common pitfall when using Image widget - you must explicitly set width and height of the image if it is not placed to some panel, that will stretch it automatically. In other words if you created an image with undefined width and height, then putting it to some container like Grid' cell will stretch the image to fit cell bounds.","breadcrumbs":"User Interface » Widgets » Image » Usage","id":"435","title":"Usage"},"436":{"body":"Sometimes you need your image to have equal size with the texture it uses, in this case you should fetch texture bounds first and then create an Image width these bounds: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::texture::Texture,\n# gui::{image::ImageBuilder, widget::WidgetBuilder, BuildContext, UiNode},\n# resource::texture::TextureKind,\n# utils::into_gui_texture,\n# }; async fn create_image( ctx: &mut BuildContext<'_>, resource_manager: ResourceManager,\n) -> Handle { // Ask resource manager to load the texture and wait while it loads using `.await`. if let Ok(texture) = resource_manager .request:: (\"path/to/your/texture.png\") .await { // A texture can be not only rectangular, so we must check that. let texture_kind = texture.data_ref().kind(); if let TextureKind::Rectangle { width, height } = texture_kind { return ImageBuilder::new( WidgetBuilder::new() .with_width(width as f32) .with_height(height as f32), ) .with_texture(into_gui_texture(texture)) .build(ctx); } } // Image wasn't created. Handle::NONE\n} This function can be used as-is whenever you need to create an Image that have same size as the source file. It is marked as async because resource loading (texture is a resource) happens in separate thread and to get actual texture data we must wait it. If you don't want to use async, then use any executor to block current thread and execute the promise immediately: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager,\n# gui::{BuildContext, UiNode},\n# };\n# # async fn create_image(\n# ctx: &mut BuildContext<'_>,\n# resource_manager: ResourceManager,\n# ) -> Handle {\n# Handle::NONE\n# }\nfn create_image_sync( ctx: &mut BuildContext<'_>, resource_manager: ResourceManager,\n) -> Handle { fyrox::core::futures::executor::block_on(create_image(ctx, resource_manager))\n}","breadcrumbs":"User Interface » Widgets » Image » Equal Size to Source","id":"436","title":"Equal Size to Source"},"437":{"body":"In some rare cases you need to flip your source image before showing it, there is .with_flip option for that: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# asset::manager::ResourceManager, resource::texture::Texture,\n# gui::{image::ImageBuilder, widget::WidgetBuilder, BuildContext, UiNode},\n# utils::into_gui_texture,\n# }; fn create_image(ctx: &mut BuildContext, resource_manager: ResourceManager) -> Handle { ImageBuilder::new(WidgetBuilder::new().with_width(100.0).with_height(100.0)) .with_flip(true) // Flips an image vertically .with_texture(into_gui_texture( resource_manager.request:: (\"path/to/your/texture.png\"), )) .build(ctx)\n} There are few places where it can be helpful: You're using render target as a source texture for your Image instance, render targets are vertically flipped due to mismatch of coordinates of UI and graphics API. The UI has origin at left top corner, the graphics API - bottom left. Your source image is vertically mirrored.","breadcrumbs":"User Interface » Widgets » Image » Vertical Flip","id":"437","title":"Vertical Flip"},"438":{"body":"inspector A widget that allows you to generate visual representation for arbitrary structures, that implement Reflect trait.","breadcrumbs":"User Interface » Widgets » Inspector (WIP) » Inspector (WIP)","id":"438","title":"Inspector (WIP)"},"439":{"body":"list view","breadcrumbs":"User Interface » Widgets » List view (WIP) » List view (WIP)","id":"439","title":"List view (WIP)"},"44":{"body":"On Linux Fyrox needs the development files for the following libraries: libxcb-shape0, libxcb-xfixes0, libxcb1, libxkbcommon, libasound2. For Debian based distros like Ubuntu, they can be installed like below: sudo apt install libxcb-shape0-dev libxcb-xfixes0-dev libxcb1-dev libxkbcommon-dev libasound2-dev","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Linux","id":"44","title":"Linux"},"440":{"body":"","breadcrumbs":"User Interface » Widgets » Menu (WIP) » Menu (WIP)","id":"440","title":"Menu (WIP)"},"441":{"body":"message box","breadcrumbs":"User Interface » Widgets » Message box (WIP) » Message box (WIP)","id":"441","title":"Message box (WIP)"},"442":{"body":"numeric up down A widget that handles numbers of any machine type. Use this widget if you need to provide input field for a numeric type.","breadcrumbs":"User Interface » Widgets » Numeric field » NumericUpDown Widget","id":"442","title":"NumericUpDown Widget"},"443":{"body":"Use NumericUpDownBuilder to create a new instance of the NumericUpDown widget: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle, gui::{numeric::NumericUpDownBuilder, widget::WidgetBuilder, BuildContext, UiNode}\n# };\nfn create_numeric_widget(ctx: &mut BuildContext) -> Handle { NumericUpDownBuilder::new(WidgetBuilder::new()) .with_value(123.0f32) .build(ctx)\n} Keep in mind, that this widget is generic and can work with any numeric types. Sometimes you might get an \"unknown type\" error message from the compiler (especially if your use 123.0 ambiguous numeric literals), in this case you need to specify the type explicitly (NumericUpDownBuilder:: ::new...).","breadcrumbs":"User Interface » Widgets » Numeric field » How to create","id":"443","title":"How to create"},"444":{"body":"This widget supports lower and upper limits for the values. It can be specified by NumericUpDownBuilder::with_min_value and NumericUpDownBuilder::with_max_value (or changed at runtime using NumericUpDownMessage::MinValue and NumericUpDownMessage::MaxValue messages): # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle, gui::{numeric::NumericUpDownBuilder, widget::WidgetBuilder, BuildContext, UiNode}\n# };\nfn create_numeric_widget(ctx: &mut BuildContext) -> Handle { NumericUpDownBuilder::new(WidgetBuilder::new()) .with_value(123.0f32) .with_min_value(42.0) .with_max_value(666.0) .build(ctx)\n} The default limits for min and max are NumericType::min_value and NumericType::max_value respectively.","breadcrumbs":"User Interface » Widgets » Numeric field » Limits","id":"444","title":"Limits"},"445":{"body":"Since the value of the widget can be changed via up/down arrow buttons (also by dragging the cursor up or down on them), the widget provides a way to set the step of the value (for increment and decrement at the same time): # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle, gui::{numeric::NumericUpDownBuilder, widget::WidgetBuilder, BuildContext, UiNode}\n# };\nfn create_numeric_widget(ctx: &mut BuildContext) -> Handle { NumericUpDownBuilder::new(WidgetBuilder::new()) .with_value(125.0f32) .with_step(5.0) .build(ctx)\n} The default value of the step is NumericType::one.","breadcrumbs":"User Interface » Widgets » Numeric field » Step","id":"445","title":"Step"},"446":{"body":"It is possible to specify visual rounding of the value up to desired decimal place (it does not change the way how the actual value is rounded). For example, in some cases you might get irrational values such as 1/3 ~= 0.33333333, but you interested in only first two decimal places. In this case you can set the precision to 2: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle, gui::{numeric::NumericUpDownBuilder, widget::WidgetBuilder, BuildContext, UiNode}\n# };\nfn create_numeric_widget(ctx: &mut BuildContext) -> Handle { NumericUpDownBuilder::new(WidgetBuilder::new()) .with_value(0.3333333f32) .with_precision(2) .build(ctx)\n}","breadcrumbs":"User Interface » Widgets » Numeric field » Precision","id":"446","title":"Precision"},"447":{"body":"","breadcrumbs":"User Interface » Widgets » Popup (WIP) » Popup (WIP)","id":"447","title":"Popup (WIP)"},"448":{"body":"progress bar","breadcrumbs":"User Interface » Widgets » Progress bar (WIP) » Progress bar (WIP)","id":"448","title":"Progress bar (WIP)"},"449":{"body":"range editor","breadcrumbs":"User Interface » Widgets » Range (WIP) » Range (WIP)","id":"449","title":"Range (WIP)"},"45":{"body":"Fyrox plugins are static, this means that you must re-compile your game or editor if the source code of your game changes, such architecture requires some boilerplate code for any game. Fyrox offers a special tiny tool - fyrox-template - that helps you generate all this boilerplate with a single command. Install it by running the following command: cargo install fyrox-template Note for Linux: This installs it in $user/.cargo/bin. If you get errors about the fyrox-template command not found then you need to add this folder to your $PATH still. Navigate to the folder where you want the project to be created and run the following command: fyrox-template init --name my_game --style 3d Note that unlike cargo init, this will create a new folder with the given name. The tool accepts two arguments - a project name (--name) and a style (--style), which defines the contents of the default scene. Once you initialize your project, go to game/src/lib.rs - this is where your game logic is located, as you can see, the fyrox-template generated quite a bit of code for you. There are comments explaining what each place is for. For more info about each method, please refer to the docs . Once the project is generated, you should memorize the two commands that will help you to run your game in different modes: cargo run --package editor --release - launches the editor with your game attached. The editor allows you to run your game from it and edit its game entities. It is intended to be used only for development. cargo run --package executor --release - creates and runs the production binary of your game, which can be shipped (for example - to a store). Navigate to your project's directory and run cargo run --package editor --release, after some time you should see the editor: editor In the editor you can start building your game scene. Important note: your scene must have at least one camera, otherwise you won't see a thing. Read the next chapter to learn how to use the editor.","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Project Generator","id":"45","title":"Project Generator"},"450":{"body":"rect","breadcrumbs":"User Interface » Widgets » Rect (WIP) » Rect editor (WIP)","id":"450","title":"Rect editor (WIP)"},"451":{"body":"scroll bar Scroll bar is used to represent a value on a finite range. It has a thumb that shows the current value on on the bar. Usually it is used in pair with ScrollPanel to create something like ScrollViewer widget. However, it could also be used to create sliders to show some value that lies within some range.","breadcrumbs":"User Interface » Widgets » Scroll bar » Scroll bar","id":"451","title":"Scroll bar"},"452":{"body":"A simple example of how to create a new ScrollBar could be something like this: # extern crate fyrox;\n# use fyrox::gui::{\n# core::pool::Handle, scroll_bar::ScrollBarBuilder, widget::WidgetBuilder, BuildContext,\n# UiNode,\n# };\nfn create_scroll_bar(ctx: &mut BuildContext) -> Handle { ScrollBarBuilder::new(WidgetBuilder::new()) .with_min(0.0) .with_max(200.0) .with_value(123.0) .build(ctx)\n} It creates a horizontal scroll bar with 123.0 value and a range of [0.0..200.0]. To fetch the new value of the scroll bar, use ScrollBarMessage::Value message: # extern crate fyrox;\n# use fyrox::gui::{\n# core::pool::Handle,\n# message::{MessageDirection, UiMessage},\n# scroll_bar::ScrollBarMessage,\n# UiNode,\n# };\n# fn foo(scroll_bar: Handle , message: &mut UiMessage) {\nif message.destination() == scroll_bar && message.direction() == MessageDirection::FromWidget\n{ if let Some(ScrollBarMessage::Value(value)) = message.data() { println!(\"{}\", value); }\n}\n# } Please note, that you need to explicitly filter messages by MessageDirection::FromWidget, because it's the only direction that is used as an \"indicator\" that the value was accepted by the scroll bar.","breadcrumbs":"User Interface » Widgets » Scroll bar » Example","id":"452","title":"Example"},"453":{"body":"Scroll bar could be either horizontal (default) or vertical. You can select the orientation when building a scroll bar using ScrollBarBuilder::with_orientation method and provide a desired value from Orientation enum there.","breadcrumbs":"User Interface » Widgets » Scroll bar » Orientation","id":"453","title":"Orientation"},"454":{"body":"By default, scroll bar does not show its actual value, you can turn it on using ScrollBarBuilder::show_value method with true as the first argument. To change rounding of the value, use ScrollBarBuilder::with_value_precision and provide the desired amount of decimal places there.","breadcrumbs":"User Interface » Widgets » Scroll bar » Show values","id":"454","title":"Show values"},"455":{"body":"Scroll bar provides arrows to change the current value using a fixed step value. You can change it using ScrollBarBuilder::with_step method.","breadcrumbs":"User Interface » Widgets » Scroll bar » Step","id":"455","title":"Step"},"456":{"body":"Scroll panel widget does the same as Scroll Viewer widget, but it does not have any additional widgets and does not have any graphics. It is a panel widget that provides basic scrolling functionality and Scroll Viewer is built on top of it. Strictly speaking, scroll panel widget is used to arrange its children widgets, so they can be offset by a certain amount of units from top-left corner. It is used to provide basic scrolling functionality.","breadcrumbs":"User Interface » Widgets » Scroll panel » Scroll panel","id":"456","title":"Scroll panel"},"457":{"body":"# extern crate fyrox;\n# use fyrox_ui::{\n# button::ButtonBuilder,\n# core::{algebra::Vector2, pool::Handle},\n# grid::{Column, GridBuilder, Row},\n# scroll_panel::ScrollPanelBuilder,\n# widget::WidgetBuilder,\n# BuildContext, UiNode,\n# };\n#\nfn create_scroll_panel(ctx: &mut BuildContext) -> Handle { ScrollPanelBuilder::new( WidgetBuilder::new().with_child( GridBuilder::new( WidgetBuilder::new() .with_child( ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"Some Button\") .build(ctx), ) .with_child( ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"Some Other Button\") .build(ctx), ), ) .add_row(Row::auto()) .add_row(Row::auto()) .add_column(Column::stretch()) .build(ctx), ), ) .with_scroll_value(Vector2::new(100.0, 200.0)) .with_vertical_scroll_allowed(true) .with_horizontal_scroll_allowed(true) .build(ctx)\n}","breadcrumbs":"User Interface » Widgets » Scroll panel » Examples","id":"457","title":"Examples"},"458":{"body":"Scrolling value for both axes can be set via ScrollPanelMessage::VerticalScroll and ScrollPanelMessage::HorizontalScroll: use fyrox_ui::{ core::pool::Handle, message::MessageDirection, scroll_panel::ScrollPanelMessage, UiNode, UserInterface,\n};\nfn set_scrolling_value( scroll_panel: Handle , horizontal: f32, vertical: f32, ui: &UserInterface,\n) { ui.send_message(ScrollPanelMessage::horizontal_scroll( scroll_panel, MessageDirection::ToWidget, horizontal, )); ui.send_message(ScrollPanelMessage::vertical_scroll( scroll_panel, MessageDirection::ToWidget, vertical, ));\n}","breadcrumbs":"User Interface » Widgets » Scroll panel » Scrolling","id":"458","title":"Scrolling"},"459":{"body":"Calculates the scroll values to bring a desired child into view, it can be used for automatic navigation: # use fyrox_ui::{\n# core::pool::Handle, message::MessageDirection, scroll_panel::ScrollPanelMessage, UiNode,\n# UserInterface,\n# };\nfn bring_child_into_view( scroll_panel: Handle , child: Handle , ui: &UserInterface,\n) { ui.send_message(ScrollPanelMessage::bring_into_view( scroll_panel, MessageDirection::ToWidget, child, ))\n}","breadcrumbs":"User Interface » Widgets » Scroll panel » Bringing child into view","id":"459","title":"Bringing child into view"},"46":{"body":"Due to the nature of the software development, some bugs will inevitably sneak into the major releases, due to this, you may want to use the latest engine version from the repository on GitHub, since it is the most likely to have bugs fixed (you can also contribute by fixing any bugs you find or at least, by filing an issue ).","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Using the Latest Engine Version","id":"46","title":"Using the Latest Engine Version"},"460":{"body":"scroll viewer Scroll viewer is a scrollable region with two scroll bars for each axis. It is used to wrap a content of unknown size to ensure that all of it will be accessible in its parent widget bounds. For example, it could be used in a Window widget to allow a content of the window to be accessible, even if the window is smaller than the content.","breadcrumbs":"User Interface » Widgets » Scroll viewer » Scroll viewer","id":"460","title":"Scroll viewer"},"461":{"body":"A scroll viewer widget could be created using ScrollViewerBuilder: # extern crate fyrox;\n# use fyrox::gui::{\n# button::ButtonBuilder, core::pool::Handle, scroll_viewer::ScrollViewerBuilder,\n# stack_panel::StackPanelBuilder, text::TextBuilder, widget::WidgetBuilder, BuildContext,\n# UiNode,\n# };\n#\nfn create_scroll_viewer(ctx: &mut BuildContext) -> Handle { ScrollViewerBuilder::new(WidgetBuilder::new()) .with_content( StackPanelBuilder::new( WidgetBuilder::new() .with_child( ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"Click Me!\") .build(ctx), ) .with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"Some\\nlong\\ntext\") .build(ctx), ), ) .build(ctx), ) .build(ctx)\n} Keep in mind, that you can change the content of a scroll viewer at runtime using ScrollViewerMessage::Content message.","breadcrumbs":"User Interface » Widgets » Scroll viewer » Example","id":"461","title":"Example"},"462":{"body":"Scroll viewer can have an arbitrary scrolling speed for each axis. Scrolling is performed via mouse wheel and by default it scrolls vertical axis, which can be changed by holding Shift key. Scrolling speed can be set during the build phase: # extern crate fyrox;\n# use fyrox::gui::{\n# core::pool::Handle, scroll_viewer::ScrollViewerBuilder, widget::WidgetBuilder,\n# BuildContext, UiNode,\n# };\n#\nfn create_scroll_viewer(ctx: &mut BuildContext) -> Handle { ScrollViewerBuilder::new(WidgetBuilder::new()) // Set vertical scrolling speed twice as fast as default scrolling speed. .with_v_scroll_speed(60.0) // Set horizontal scrolling speed slightly lower than the default value (30.0). .with_h_scroll_speed(20.0) .build(ctx)\n} Also, it could be set using ScrollViewerMessage::HScrollSpeed or ScrollViewerMessage::VScrollSpeed messages.","breadcrumbs":"User Interface » Widgets » Scroll viewer » Scrolling Speed and Controls","id":"462","title":"Scrolling Speed and Controls"},"463":{"body":"Calculates the scroll values to bring a desired child into view, it can be used for automatic navigation: # extern crate fyrox;\n# use fyrox::gui::{\n# core::pool::Handle, message::MessageDirection, scroll_viewer::ScrollViewerMessage, UiNode,\n# UserInterface,\n# };\nfn bring_child_into_view( scroll_viewer: Handle , child: Handle , ui: &UserInterface,\n) { ui.send_message(ScrollViewerMessage::bring_into_view( scroll_viewer, MessageDirection::ToWidget, child, ))\n}","breadcrumbs":"User Interface » Widgets » Scroll viewer » Bringing a child into view","id":"463","title":"Bringing a child into view"},"464":{"body":"screen widget Screen is a widget that always has the size of the screen of the UI in which it is used. It is main use case is to provide automatic layout functionality, that will always provide screen size to its children widgets. This is needed, because the root node of any UI is Canvas which provides infinite bounds as a layout constraint, thus making it impossible for automatic fitting to the current screen size. For example, Screen widget could be used as a root node for Grid widget - in this case the grid instance will always have the size of the screen and will automatically shrink or expand when the screen size changes. It is ideal choice if you want to have some widgets always centered on screen (for example - crosshair, main menu of your game, etc.).","breadcrumbs":"User Interface » Widgets » Screen » Screen","id":"464","title":"Screen"},"465":{"body":"There are two major ways to create a Screen widget - using the editor or by code.","breadcrumbs":"User Interface » Widgets » Screen » How To Create","id":"465","title":"How To Create"},"466":{"body":"Go to Create -> UI menu and find Screen widget there, make sure it is a direct child of the root node of the hierarchy. Alternatively, you can right-click on the root node in the hierarchy and click Create Child -> Screen. After that you can add any number of children nodes to it. Screen widget does not have any special properties, so you do not need to tweak it at all.","breadcrumbs":"User Interface » Widgets » Screen » Using the Editor","id":"466","title":"Using the Editor"},"467":{"body":"The following example creates a simple main menu of a game with just two buttons. The buttons will always be centered in the current screen bounds. It creates something similar to the gif above, but not so fancy. # extern crate fyrox;\n# use fyrox::gui::{\n# core::pool::Handle,\n# button::ButtonBuilder,\n# grid::{Column, GridBuilder, Row},\n# screen::ScreenBuilder,\n# stack_panel::StackPanelBuilder,\n# widget::WidgetBuilder,\n# BuildContext, UiNode,\n# };\n# fn create_always_centered_game_menu(ctx: &mut BuildContext) -> Handle { // Screen widget will provide current screen size to its Grid widget as a layout constraint, // thus making it fit to the current screen bounds. ScreenBuilder::new( WidgetBuilder::new().with_child( GridBuilder::new( WidgetBuilder::new() .with_width(300.0) .with_height(400.0) .with_child( // Buttons will be stacked one on top of another. StackPanelBuilder::new( WidgetBuilder::new() .on_row(1) .on_column(1) .with_child( ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"New Game\") .build(ctx), ) .with_child( ButtonBuilder::new(WidgetBuilder::new()) .with_text(\"Exit\") .build(ctx), ), ) .build(ctx), ), ) // Split the grid into 3 rows and 3 columns. The center cell contain the stack panel // instance, that basically stacks main menu buttons one on top of another. The center // cell will also be always centered in screen bounds. .add_row(Row::stretch()) .add_row(Row::auto()) .add_row(Row::stretch()) .add_column(Column::stretch()) .add_column(Column::auto()) .add_column(Column::stretch()) .build(ctx), ), ) .build(ctx)\n}","breadcrumbs":"User Interface » Widgets » Screen » From Code","id":"467","title":"From Code"},"468":{"body":"Stack Panels are one of several methods to position multiple widgets in relation to each other. A Stack Panel Widget orders it's children widgets linerarly, aka in a stack of widgets, based on the order the widgets were added as children. So the first widget added will be at the top or left most position, while each additional widget will decend from top to bottom or continue from left most to right most. The below example code places 3 text widgets into a vertical stack: # extern crate fyrox;\n# use fyrox::gui::{\n# UiNode,\n# BuildContext,\n# widget::WidgetBuilder,\n# text::TextBuilder,\n# stack_panel::StackPanelBuilder,\n# }; fn create_stack_panel(ctx: &mut BuildContext) -> fyrox::core::pool::Handle { StackPanelBuilder::new( WidgetBuilder::new() .with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"Top\") .build(ctx) ) .with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"Middle\") .build(ctx) ) .with_child( TextBuilder::new(WidgetBuilder::new()) .with_text(\"Bottom\") .build(ctx) ) ) .build(ctx) } As you can see from the example, creating a Stack Panel uses the standard method for creating widgets. Create a new StackPanelBuilder and provide it with a new WidgetBuilder. Adding widgets to the stack is done by adding childeren to the StackBuilder's WidgetBuilder.","breadcrumbs":"User Interface » Widgets » Stack panel » Stack Panel","id":"468","title":"Stack Panel"},"469":{"body":"As has been indicated, Stack Panels can be oriented to order it's children either Vertical, from top to bottom, or Horizontal, Left most to right most. This is done using the StackPanelBuilder's with_orientation function providing it with a gui::Orientation enum value. By default all Stack Panel's are Vertical. # extern crate fyrox;\n# use fyrox::gui::{\n# Orientation,\n# BuildContext,\n# widget::WidgetBuilder,\n# stack_panel::StackPanelBuilder,\n# }; # fn build(ctx: &mut BuildContext) {\nStackPanelBuilder::new( WidgetBuilder::new()\n) .with_orientation(Orientation::Horizontal) .build(ctx);\n# }","breadcrumbs":"User Interface » Widgets » Stack panel » Stack Panel Orientation","id":"469","title":"Stack Panel Orientation"},"47":{"body":"⚠️ fyrox-template has special sub-command - upgrade to quickly upgrade to desired engine version. To upgrade to the latest version (nightly) you should execute fyrox-template upgrade --version nightly command in your game's directory. There are three main variants for --version switch: nightly - uses latest nightly version of the engine from GitHub directly. This is the preferable version if you want to use the latest changes and bug fixes as they release. latest - uses latest stable version of the engine. major.minor.patch - uses specific stable version from crates.io (0.30.0 for example).","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Automatic","id":"47","title":"Automatic"},"470":{"body":"The Tab Control handles the visibility of several tabs, only showing a single tab that the user has selected via the tab header buttons. Each tab is defined via a Tab Definition struct which takes two widgets, one representing the tab header and the other representing the tab's contents. The following example makes a 2 tab, Tab Control containing some simple text widgets: # extern crate fyrox;\n# use fyrox::gui::{\n# tab_control::{TabControlBuilder, TabDefinition},\n# text::TextBuilder,\n# widget::WidgetBuilder,\n# BuildContext,\n# };\n# fn create_tab_control(ctx: &mut BuildContext) { TabControlBuilder::new(WidgetBuilder::new()) .with_tab(TabDefinition { header: TextBuilder::new(WidgetBuilder::new()) .with_text(\"First\") .build(ctx), content: TextBuilder::new(WidgetBuilder::new()) .with_text(\"First tab's contents!\") .build(ctx), can_be_closed: true, user_data: None, }) .with_tab(TabDefinition { header: TextBuilder::new(WidgetBuilder::new()) .with_text(\"Second\") .build(ctx), content: TextBuilder::new(WidgetBuilder::new()) .with_text(\"Second tab's contents!\") .build(ctx), can_be_closed: true, user_data: None, }) .build(ctx);\n} As usual, we create the widget via the builder TabControlBuilder. Tabs are added via the with_tab function in the order you want them to appear, passing each call to the function a directly constructed TabDefinition struct. Tab headers will appear from left to right at the top with tab contents shown directly below the tabs. As usual, if no constraints are given to the base WidgetBuilder of the TabControlBuilder, then the tab content area will resize to fit whatever is in the current tab. Each tab's content is made up of one widget, so to be useful you will want to use one of the container widgets to help arrange additional widgets within the tab.","breadcrumbs":"User Interface » Widgets » Tab control » Tab Control","id":"470","title":"Tab Control"},"471":{"body":"Notice that you can put any widget into the tab header, so if you want images to denote each tab you can add an Image widget to each header, and if you want an image and some text you can insert a stack panel with an image on top and text below it. You will also likely want to style whatever widgets you add. As can be seen when running the code example above, the tab headers are scrunched when there are no margins provided to your text widgets. Simply add something like the below code example and you will get a decent look: # extern crate fyrox;\n# use fyrox::gui::{\n# BuildContext,\n# widget::WidgetBuilder,\n# text::TextBuilder,\n# Thickness, # tab_control::{TabDefinition},\n# }; # fn build(ctx: &mut BuildContext) {\n# TabDefinition{\nheader: TextBuilder::new( WidgetBuilder::new() .with_margin(Thickness::uniform(4.0)) ) .with_text(\"First\") .build(ctx),\n# content: Default::default(),\n# can_be_closed: true,\n# user_data: None,\n# };\n# }","breadcrumbs":"User Interface » Widgets » Tab control » Tab Header Styling","id":"471","title":"Tab Header Styling"},"472":{"body":"Text is a simple widget that allows you to print text on screen. It has various options like word wrapping, text alignment, and so on.","breadcrumbs":"User Interface » Widgets » Text » Text","id":"472","title":"Text"},"473":{"body":"An instance of the Text widget could be created like so: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{text::TextBuilder, widget::WidgetBuilder, UiNode, UserInterface},\n# };\nfn create_text(ui: &mut UserInterface, text: &str) -> Handle { TextBuilder::new(WidgetBuilder::new()) .with_text(text) .build(&mut ui.build_ctx())\n}","breadcrumbs":"User Interface » Widgets » Text » How to create","id":"473","title":"How to create"},"474":{"body":"There are various text alignment options for both vertical and horizontal axes. Typical alignment values are: Left, Center, Right for horizontal axis, and Top, Center, Bottom for vertical axis. An instance of centered text could be created like so: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{\n# text::TextBuilder, widget::WidgetBuilder, HorizontalAlignment, UiNode, UserInterface,\n# VerticalAlignment,\n# },\n# };\nfn create_centered_text(ui: &mut UserInterface, text: &str) -> Handle { TextBuilder::new(WidgetBuilder::new()) .with_horizontal_text_alignment(HorizontalAlignment::Center) .with_vertical_text_alignment(VerticalAlignment::Center) .with_text(text) .build(&mut ui.build_ctx())\n} Long text is usually needs to wrap on available bounds, there are three possible options for word wrapping: NoWrap, Letter, Word. An instance of text with word-based wrapping could be created like so: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{\n# formatted_text::WrapMode, text::TextBuilder, widget::WidgetBuilder, UiNode,\n# UserInterface,\n# },\n# };\nfn create_text_with_word_wrap(ui: &mut UserInterface, text: &str) -> Handle { TextBuilder::new(WidgetBuilder::new()) .with_wrap(WrapMode::Word) .with_text(text) .build(&mut ui.build_ctx())\n}","breadcrumbs":"User Interface » Widgets » Text » Text alignment and word wrapping","id":"474","title":"Text alignment and word wrapping"},"475":{"body":"If you need to have a text with some background, you should use Border widget as a parent widget of your text. Caveat: Widget::background is ignored for Text widget! # extern crate fyrox;\n# use fyrox::{\n# core::{color::Color, pool::Handle},\n# gui::{\n# border::BorderBuilder, brush::Brush, text::TextBuilder, widget::WidgetBuilder, UiNode,\n# UserInterface,\n# },\n# };\n# fn create_text_with_background(ui: &mut UserInterface, text: &str) -> Handle { let text_widget = TextBuilder::new(WidgetBuilder::new().with_foreground(Brush::Solid(Color::RED))) .with_text(text) .build(&mut ui.build_ctx()); BorderBuilder::new( WidgetBuilder::new() .with_child(text_widget) // <-- Text is now a child of the border .with_background(Brush::Solid(Color::opaque(50, 50, 50))), ) .build(&mut ui.build_ctx())\n} Keep in mind that now the text widget is a child widget of the border, so if you need to position the text, you should position the border, not the text.","breadcrumbs":"User Interface » Widgets » Text » Background","id":"475","title":"Background"},"476":{"body":"To set a color of the text just use .with_foreground(..) of the WidgetBuilder while building the text instance: # extern crate fyrox;\n# use fyrox::{\n# core::{color::Color, pool::Handle},\n# gui::{brush::Brush, text::TextBuilder, widget::WidgetBuilder, UiNode, UserInterface},\n# };\nfn create_text(ui: &mut UserInterface, text: &str) -> Handle { // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv TextBuilder::new(WidgetBuilder::new().with_foreground(Brush::Solid(Color::RED))) .with_text(text) .build(&mut ui.build_ctx())\n} By default, text is created with default font, however it is possible to set any custom font: # extern crate fyrox;\n# use fyrox::{\n# core::{futures::executor::block_on, pool::Handle},\n# gui::{\n# text::TextBuilder,\n# ttf::{Font, SharedFont},\n# widget::WidgetBuilder,\n# UiNode, UserInterface,\n# },\n# }; fn load_font() -> SharedFont { // Choose desired character set, default is Basic Latin + Latin Supplement. // Character set is a set of ranges with Unicode code points. let character_set = Font::default_char_set(); // Normally `block_on` should be avoided. let font = block_on(Font::from_file( \"path/to/your/font.ttf\", 24.0, character_set, )) .unwrap(); SharedFont::new(font)\n} fn create_text(ui: &mut UserInterface, text: &str) -> Handle { TextBuilder::new(WidgetBuilder::new()) .with_font(load_font()) .with_text(text) .build(&mut ui.build_ctx())\n} Please refer to Font chapter to learn more about fonts.","breadcrumbs":"User Interface » Widgets » Text » Fonts and colors","id":"476","title":"Fonts and colors"},"477":{"body":"There is no way to change font size without changing the entire font used by Text, it is known issue and there is tracking issue for that. Check Font chapter to learn how to create fonts.","breadcrumbs":"User Interface » Widgets » Text » Font size","id":"477","title":"Font size"},"478":{"body":"Text widget supports shadows effect to add contrast to your text, which could be useful to make text readable independent on the background colors. This effect could be used for subtitles. Shadows are pretty easy to add, all you need to do is to enable them, setup desired thickness, offset and brush (solid color or gradient). # extern crate fyrox;\n# use fyrox::{\n# core::{algebra::Vector2, color::Color, pool::Handle},\n# gui::{brush::Brush, text::TextBuilder, widget::WidgetBuilder, UiNode, UserInterface},\n# };\n# fn create_red_text_with_black_shadows(ui: &mut UserInterface, text: &str) -> Handle { TextBuilder::new(WidgetBuilder::new().with_foreground(Brush::Solid(Color::RED))) .with_text(text) // Enable shadows. .with_shadow(true) // Black shadows. .with_shadow_brush(Brush::Solid(Color::BLACK)) // 1px thick. .with_shadow_dilation(1.0) // Offset the shadow slightly to the right-bottom. .with_shadow_offset(Vector2::new(1.0, 1.0)) .build(&mut ui.build_ctx())\n}","breadcrumbs":"User Interface » Widgets » Text » Shadows","id":"478","title":"Shadows"},"479":{"body":"Text widget can accept the following list of messages at runtime (respective constructors are name with small letter - TextMessage::Text -> TextMessage::text(widget_handle, direction, text)): TextMessage::Text - sets new text for a Text widget. TextMessage::Wrap - sets new wrapping mode . TextMessage::Font - sets new font TextMessage::VerticalAlignment and TextMessage::HorizontalAlignment sets vertical and horizontal text alignment respectively. TextMessage::Shadow - enables or disables shadow casting TextMessage::ShadowDilation - sets \"thickness\" of the shadows under the tex. TextMessage::ShadowBrush - sets shadow brush (allows you to change color and even make shadow with color gradients). TextMessage::ShadowOffset - sets offset of the shadows. An example of changing text at runtime could be something like this: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{\n# message::{MessageDirection},\n# UiNode, UserInterface,\n# text::TextMessage\n# },\n# };\nfn request_change_text(ui: &UserInterface, text_widget_handle: Handle , text: &str) { ui.send_message(TextMessage::text( text_widget_handle, MessageDirection::ToWidget, text.to_owned(), ))\n} Please keep in mind, that like any other situation when you \"changing\" something via messages, you should remember that the change is not immediate. The change will be applied on ui.poll_message(..) call somewhere in your code (or will be done automatically if you're using scripts or Framework (obsolete)).","breadcrumbs":"User Interface » Widgets » Text » Messages","id":"479","title":"Messages"},"48":{"body":"Engine version can also be updated manually. The first step you need to take is to install the latest fyrox-template, this can be done with a single cargo command: cargo install fyrox-template --force --git https://github.com/FyroxEngine/Fyrox This will ensure you're using the latest project/script template generator, which is important, since old versions of the template generator will most likely generate outdated code, no longer be compatible with the engine. To switch existing projects to the latest version of the engine, you need to specify paths pointing to the remote repository for the fyrox and fyroxed_base dependencies. You need to do this in the game, executor, and editor projects. First, open game/Cargo.toml and change the fyrox dependency to the following: [dependencies]\nfyrox = { git = \"https://github.com/FyroxEngine/Fyrox\" } Do the same for executor/Cargo.toml. The editor has two dependencies we need to change: fyrox and fyroxed_base. Open the editor/Cargo.toml and set both dependencies to the following: [dependencies]\nfyrox = { git = \"https://github.com/FyroxEngine/Fyrox\" }\nfyroxed_base = { git = \"https://github.com/FyroxEngine/Fyrox\" } Now your game will use the latest engine and editor, but beware - new commits could bring some API breaks. You can avoid these by specifying a particular commit, just add rev = \"desired_commit_hash\" to every dependency like so: [dependencies]\nfyrox = { git = \"https://github.com/FyroxEngine/Fyrox\", rev = \"0195666b30562c1961a9808be38b5e5715da43af\" }\nfyroxed_base = { git = \"https://github.com/FyroxEngine/Fyrox\", rev = \"0195666b30562c1961a9808be38b5e5715da43af\" } To bring a local git repository of the engine to being up-to-date, just call cargo update at the root of the project's workspace. This will pull the latest changes from the remote, unless there is no rev specified. Learn more about dependency paths on the official cargo documentation, here .","breadcrumbs":"Getting started » Editor, Plugins and Scripts » Manual","id":"48","title":"Manual"},"480":{"body":"TextBox is a text widget that allows you to edit text and create specialized input fields. It has various options like word wrapping, text alignment, and so on.","breadcrumbs":"User Interface » Widgets » Text box » Text Box","id":"480","title":"Text Box"},"481":{"body":"An instance of the TextBox widget could be created like so: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{text_box::TextBoxBuilder, widget::WidgetBuilder, UiNode, UserInterface},\n# };\nfn create_text_box(ui: &mut UserInterface, text: &str) -> Handle { TextBoxBuilder::new(WidgetBuilder::new()) .with_text(text) .build(&mut ui.build_ctx())\n}","breadcrumbs":"User Interface » Widgets » Text box » How to create","id":"481","title":"How to create"},"482":{"body":"There are various text alignment options for both vertical and horizontal axes. Typical alignment values are: Left, Center, Right for horizontal axis, and Top, Center, Bottom for vertical axis. An instance of centered text could be created like so: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{\n# text_box::TextBoxBuilder, widget::WidgetBuilder, HorizontalAlignment, UiNode, UserInterface,\n# VerticalAlignment,\n# },\n# };\nfn create_centered_text(ui: &mut UserInterface, text: &str) -> Handle { TextBoxBuilder::new(WidgetBuilder::new()) .with_horizontal_text_alignment(HorizontalAlignment::Center) .with_vertical_text_alignment(VerticalAlignment::Center) .with_text(text) .build(&mut ui.build_ctx())\n} Long text is usually needs to wrap on available bounds, there are three possible options for word wrapping: NoWrap, Letter, Word. An instance of text with word-based wrapping could be created like so: # extern crate fyrox;\n# use fyrox::{\n# core::pool::Handle,\n# gui::{\n# formatted_text::WrapMode, text_box::TextBoxBuilder, widget::WidgetBuilder, UiNode,\n# UserInterface,\n# },\n# };\nfn create_text_with_word_wrap(ui: &mut UserInterface, text: &str) -> Handle