diff --git a/Cargo.lock b/Cargo.lock index c492871ae..dd10aae4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,114 +18,58 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" -[[package]] -name = "accesskit" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf780eb737f2d4a49ffbd512324d53ad089070f813f7be7f99dbd5123a7f448" - [[package]] name = "accesskit" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4700bdc115b306d6c43381c344dc307f03b7f0460c304e4892c309930322bd7" -[[package]] -name = "accesskit_consumer" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdfa1638ddd6eb9c752def95568df8b3ad832df252e9156d2eb783b201ca8a9" -dependencies = [ - "accesskit 0.14.0", - "immutable-chunkmap", -] - [[package]] name = "accesskit_consumer" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe3a07a32ab5837ad83db3230ac490c8504c2cd5b90ac8c00db6535f6ed65d0b" dependencies = [ - "accesskit 0.16.0", + "accesskit", "immutable-chunkmap", ] -[[package]] -name = "accesskit_macos" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c236a84ff1111defc280cee755eaa953d0b24398786851b9d28322c6d3bb1ebd" -dependencies = [ - "accesskit 0.14.0", - "accesskit_consumer 0.22.0", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "once_cell", -] - [[package]] name = "accesskit_macos" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a189d159c153ae0fce5f9eefdcfec4a27885f453ce5ef0ccf078f72a73c39d34" dependencies = [ - "accesskit 0.16.0", - "accesskit_consumer 0.24.0", + "accesskit", + "accesskit_consumer", "objc2", "objc2-app-kit", "objc2-foundation", "once_cell", ] -[[package]] -name = "accesskit_windows" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7f43d24b16b3e76bef248124fbfd2493c3a9860edb5aae1010c890e826de5e" -dependencies = [ - "accesskit 0.14.0", - "accesskit_consumer 0.22.0", - "paste", - "static_assertions", - "windows 0.54.0", -] - [[package]] name = "accesskit_windows" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "682d8c4fb425606f97408e7577793f32e96310b646fa77662eb4216293eddc7f" dependencies = [ - "accesskit 0.16.0", - "accesskit_consumer 0.24.0", + "accesskit", + "accesskit_consumer", "paste", "static_assertions", "windows 0.54.0", ] -[[package]] -name = "accesskit_winit" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755535e6bf711a42dac28b888b884b10fc00ff4010d9d3bd871c5f5beae5aa78" -dependencies = [ - "accesskit 0.14.0", - "accesskit_macos 0.15.0", - "accesskit_windows 0.20.0", - "raw-window-handle", - "winit", -] - [[package]] name = "accesskit_winit" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afbd6d598b7c035639ad2b664aa0edc94c93dc1fc3ebb4b40d8a95fcd43ffac" dependencies = [ - "accesskit 0.16.0", - "accesskit_macos 0.17.0", - "accesskit_windows 0.22.0", + "accesskit", + "accesskit_macos", + "accesskit_windows", "raw-window-handle", "winit", ] @@ -293,9 +237,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "approx" @@ -323,9 +267,9 @@ checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "arboard" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" +checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" dependencies = [ "clipboard-win", "core-graphics", @@ -352,9 +296,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -407,7 +351,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb111d0b927c4b860b42a8bd869b94f5f973b0395ed5892f1ab2be2dc6831ea8" dependencies = [ - "bindgen", + "bindgen 0.69.4", "cc", "num-derive 0.4.2", "num-traits", @@ -606,21 +550,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bevy" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043c9ad4b6fc4ca52d779873a8ca792a4e37842d07fce95363c9e17e36a1d8a0" -dependencies = [ - "bevy_internal 0.14.2", -] - [[package]] name = "bevy" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_internal 0.15.0-dev", + "bevy_internal", ] [[package]] @@ -629,23 +564,23 @@ version = "0.25.2" source = "git+https://github.com/tychedelia/bevy-inspector-egui?branch=main#1a4e9a31cb5f4a242697a19a2f03765e463edb66" dependencies = [ "bevy-inspector-egui-derive", - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", - "bevy_core 0.15.0-dev", + "bevy_core", "bevy_core_pipeline", - "bevy_ecs 0.15.0-dev", + "bevy_ecs", "bevy_egui 0.29.0 (git+https://github.com/tychedelia/bevy_egui)", - "bevy_hierarchy 0.15.0-dev", - "bevy_log 0.15.0-dev", - "bevy_math 0.15.0-dev", + "bevy_hierarchy", + "bevy_log", + "bevy_math", "bevy_pbr", - "bevy_reflect 0.15.0-dev", + "bevy_reflect", "bevy_render", "bevy_state", - "bevy_time 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_time", + "bevy_utils", + "bevy_window", "bytemuck", "egui", "fuzzy-matcher", @@ -665,49 +600,37 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "bevy_a11y" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a976cb539d6a5a3ff579cdb78187a6bcfbffa7e8224ea28f23d8b983d9389" -dependencies = [ - "accesskit 0.14.0", - "bevy_app 0.14.2", - "bevy_derive 0.14.2", - "bevy_ecs 0.14.2", -] - [[package]] name = "bevy_a11y" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "accesskit 0.16.0", - "bevy_app 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "accesskit", + "bevy_app", + "bevy_derive", + "bevy_ecs", + "bevy_reflect", ] [[package]] name = "bevy_animation" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", - "bevy_core 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_log 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_core", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_log", + "bevy_math", + "bevy_reflect", "bevy_render", - "bevy_time 0.15.0-dev", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_time", + "bevy_transform", + "bevy_utils", "blake3", "fixedbitset 0.5.7", "petgraph 0.6.5", @@ -718,34 +641,16 @@ dependencies = [ "uuid 1.10.0", ] -[[package]] -name = "bevy_app" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5361d0f8a8677a5d0102cfe7321a7ecd2a8b9a4f887ce0dde1059311cf9cd42" -dependencies = [ - "bevy_derive 0.14.2", - "bevy_ecs 0.14.2", - "bevy_reflect 0.14.2", - "bevy_tasks 0.14.2", - "bevy_utils 0.14.2", - "console_error_panic_hook", - "downcast-rs", - "thiserror", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "bevy_app" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_tasks 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_derive", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", "console_error_panic_hook", "ctrlc", "downcast-rs", @@ -754,54 +659,22 @@ dependencies = [ "web-sys", ] -[[package]] -name = "bevy_asset" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60ec5ea257e1ebd3d411f669e29acf60beb715bebc7e1f374c17f49cd3aad46c" -dependencies = [ - "async-broadcast", - "async-fs", - "async-lock", - "bevy_app 0.14.2", - "bevy_asset_macros 0.14.2", - "bevy_ecs 0.14.2", - "bevy_reflect 0.14.2", - "bevy_tasks 0.14.2", - "bevy_utils 0.14.2", - "bevy_winit 0.14.2", - "blake3", - "crossbeam-channel", - "downcast-rs", - "futures-io", - "futures-lite", - "js-sys", - "parking_lot 0.12.3", - "ron", - "serde", - "thiserror", - "uuid 1.10.0", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "bevy_asset" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "async-broadcast", "async-fs", "async-lock", "atomicow", - "bevy_app 0.15.0-dev", - "bevy_asset_macros 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_tasks 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_winit 0.15.0-dev", + "bevy_app", + "bevy_asset_macros", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_winit", "blake3", "crossbeam-channel", "downcast-rs", @@ -820,24 +693,12 @@ dependencies = [ "web-sys", ] -[[package]] -name = "bevy_asset_macros" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9eb05ce838d282f09d83380b4d6432aec7519d421dee8c75cc20e6148237e6e" -dependencies = [ - "bevy_macro_utils 0.14.2", - "proc-macro2 1.0.86", - "quote 1.0.37", - "syn 2.0.77", -] - [[package]] name = "bevy_asset_macros" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", @@ -846,17 +707,17 @@ dependencies = [ [[package]] name = "bevy_audio" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" -dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_math", + "bevy_reflect", + "bevy_transform", + "bevy_utils", "cpal 0.15.3", "rodio", ] @@ -864,10 +725,10 @@ dependencies = [ [[package]] name = "bevy_color" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_math", + "bevy_reflect", "bytemuck", "encase", "serde", @@ -878,60 +739,45 @@ dependencies = [ [[package]] name = "bevy_common_assets" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f631ce2863f3b6f1af3fa24143363b9fbd21c44218caad495a17b83e6968256" +source = "git+https://github.com/tychedelia/bevy_common_assets?branch=main#b0c00d083851dae813645b16df8561b3ce66e0e7" dependencies = [ "anyhow", - "bevy 0.14.2", + "bevy", "serde", "serde_json", "thiserror", "toml 0.8.19", ] -[[package]] -name = "bevy_core" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de706862871a1fe99ea619bff2f99d73e43ad82f19ef866a9e19a14c957c8537" -dependencies = [ - "bevy_app 0.14.2", - "bevy_ecs 0.14.2", - "bevy_reflect 0.14.2", - "bevy_tasks 0.14.2", - "bevy_utils 0.14.2", - "uuid 1.10.0", -] - [[package]] name = "bevy_core" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_tasks 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", "uuid 1.10.0", ] [[package]] name = "bevy_core_pipeline" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", - "bevy_core 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_core", + "bevy_derive", + "bevy_ecs", + "bevy_math", + "bevy_reflect", "bevy_render", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_transform", + "bevy_utils", "bitflags 2.6.0", "nonmax", "radsort", @@ -940,88 +786,42 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bevy_derive" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbfc33a4c6b80760bb8bf850a2cc65a1e031da62fd3ca8b552189104dc98514" -dependencies = [ - "bevy_macro_utils 0.14.2", - "quote 1.0.37", - "syn 2.0.77", -] - [[package]] name = "bevy_derive" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "quote 1.0.37", "syn 2.0.77", ] -[[package]] -name = "bevy_diagnostic" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebb154e0cc78e3bbfbfdb42fb502b14c1cd47e72f16e6d4228dfe6233ba6cbd" -dependencies = [ - "bevy_app 0.14.2", - "bevy_core 0.14.2", - "bevy_ecs 0.14.2", - "bevy_tasks 0.14.2", - "bevy_time 0.14.2", - "bevy_utils 0.14.2", - "const-fnv1a-hash", -] - [[package]] name = "bevy_diagnostic" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" -dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_core 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_tasks 0.15.0-dev", - "bevy_time 0.15.0-dev", - "bevy_utils 0.15.0-dev", +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" +dependencies = [ + "bevy_app", + "bevy_core", + "bevy_ecs", + "bevy_tasks", + "bevy_time", + "bevy_utils", "const-fnv1a-hash", "sysinfo", ] -[[package]] -name = "bevy_ecs" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee4222406637f3c8e3991a99788cfcde76097bf997c311f1b6297364057483f" -dependencies = [ - "bevy_ecs_macros 0.14.2", - "bevy_ptr 0.14.2", - "bevy_reflect 0.14.2", - "bevy_tasks 0.14.2", - "bevy_utils 0.14.2", - "bitflags 2.6.0", - "concurrent-queue", - "fixedbitset 0.5.7", - "nonmax", - "petgraph 0.6.5", - "serde", - "thiserror", -] - [[package]] name = "bevy_ecs" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "arrayvec 0.7.6", - "bevy_ecs_macros 0.15.0-dev", - "bevy_ptr 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_tasks 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_ecs_macros", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", "bitflags 2.6.0", "concurrent-queue", "fixedbitset 0.5.7", @@ -1031,24 +831,12 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bevy_ecs_macros" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b573430b67aff7bde8292257494f39343401379bfbda64035ba4918bba7b20" -dependencies = [ - "bevy_macro_utils 0.14.2", - "proc-macro2 1.0.86", - "quote 1.0.37", - "syn 2.0.77", -] - [[package]] name = "bevy_ecs_macros" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", @@ -1060,7 +848,7 @@ version = "0.29.0" source = "git+https://github.com/tychedelia/bevy_egui?branch=main#5c7095cab99df8009dd41b3126acadab9f6099c9" dependencies = [ "arboard", - "bevy 0.15.0-dev", + "bevy", "bytemuck", "console_log", "crossbeam-channel", @@ -1082,7 +870,7 @@ version = "0.29.0" source = "git+https://github.com/tychedelia/bevy_egui#5c7095cab99df8009dd41b3126acadab9f6099c9" dependencies = [ "arboard", - "bevy 0.15.0-dev", + "bevy", "bytemuck", "console_log", "crossbeam-channel", @@ -1101,22 +889,22 @@ dependencies = [ [[package]] name = "bevy_encase_derive" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "encase_derive_impl 0.8.0", ] [[package]] name = "bevy_gilrs" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_input 0.15.0-dev", - "bevy_time 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_time", + "bevy_utils", "gilrs", "thiserror", ] @@ -1124,31 +912,31 @@ dependencies = [ [[package]] name = "bevy_gizmos" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", "bevy_core_pipeline", - "bevy_ecs 0.15.0-dev", + "bevy_ecs", "bevy_gizmos_macros", - "bevy_math 0.15.0-dev", + "bevy_math", "bevy_pbr", - "bevy_reflect 0.15.0-dev", + "bevy_reflect", "bevy_render", "bevy_sprite", - "bevy_time 0.15.0-dev", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_time", + "bevy_transform", + "bevy_utils", "bytemuck", ] [[package]] name = "bevy_gizmos_macros" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", @@ -1157,25 +945,25 @@ dependencies = [ [[package]] name = "bevy_gltf" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "base64 0.22.1", "bevy_animation", - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", - "bevy_core 0.15.0-dev", + "bevy_core", "bevy_core_pipeline", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_math 0.15.0-dev", + "bevy_ecs", + "bevy_hierarchy", + "bevy_math", "bevy_pbr", - "bevy_reflect 0.15.0-dev", + "bevy_reflect", "bevy_render", "bevy_scene", - "bevy_tasks 0.15.0-dev", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_tasks", + "bevy_transform", + "bevy_utils", "gltf", "percent-encoding 2.3.1", "serde", @@ -1184,174 +972,92 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bevy_hierarchy" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88b912b37e1bc4dbb2aa40723199f74c8b06c4fbb6da0bb4585131df28ef66e" -dependencies = [ - "bevy_app 0.14.2", - "bevy_core 0.14.2", - "bevy_ecs 0.14.2", - "bevy_reflect 0.14.2", - "bevy_utils 0.14.2", - "smallvec 1.13.2", -] - [[package]] name = "bevy_hierarchy" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_core 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_app", + "bevy_core", + "bevy_ecs", + "bevy_reflect", + "bevy_utils", "smallvec 1.13.2", ] -[[package]] -name = "bevy_input" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd3a54e67cc3ba17971de7b1a7e64eda84493c1e7bb6bfa11c6cf8ac124377b" -dependencies = [ - "bevy_app 0.14.2", - "bevy_ecs 0.14.2", - "bevy_math 0.14.2", - "bevy_reflect 0.14.2", - "bevy_utils 0.14.2", - "smol_str", - "thiserror", -] - [[package]] name = "bevy_input" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_utils", "smol_str", "thiserror", ] -[[package]] -name = "bevy_internal" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d435cac77c568f3aef65f786a5fee0e53c81950c5258182dd2c1d6cd6c4fec" -dependencies = [ - "bevy_a11y 0.14.2", - "bevy_app 0.14.2", - "bevy_asset 0.14.2", - "bevy_core 0.14.2", - "bevy_derive 0.14.2", - "bevy_diagnostic 0.14.2", - "bevy_ecs 0.14.2", - "bevy_hierarchy 0.14.2", - "bevy_input 0.14.2", - "bevy_log 0.14.2", - "bevy_math 0.14.2", - "bevy_ptr 0.14.2", - "bevy_reflect 0.14.2", - "bevy_tasks 0.14.2", - "bevy_time 0.14.2", - "bevy_transform 0.14.2", - "bevy_utils 0.14.2", - "bevy_window 0.14.2", -] - [[package]] name = "bevy_internal" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_a11y 0.15.0-dev", + "bevy_a11y", "bevy_animation", - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_audio", "bevy_color", - "bevy_core 0.15.0-dev", + "bevy_core", "bevy_core_pipeline", - "bevy_derive 0.15.0-dev", - "bevy_diagnostic 0.15.0-dev", - "bevy_ecs 0.15.0-dev", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", "bevy_gilrs", "bevy_gizmos", "bevy_gltf", - "bevy_hierarchy 0.15.0-dev", - "bevy_input 0.15.0-dev", - "bevy_log 0.15.0-dev", - "bevy_math 0.15.0-dev", + "bevy_hierarchy", + "bevy_input", + "bevy_log", + "bevy_math", "bevy_pbr", "bevy_picking", - "bevy_ptr 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_ptr", + "bevy_reflect", "bevy_render", "bevy_scene", "bevy_sprite", "bevy_state", - "bevy_tasks 0.15.0-dev", + "bevy_tasks", "bevy_text", - "bevy_time 0.15.0-dev", - "bevy_transform 0.15.0-dev", + "bevy_time", + "bevy_transform", "bevy_ui", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", - "bevy_winit 0.15.0-dev", -] - -[[package]] -name = "bevy_log" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67240c7596c8f0653e50fce35a60196516817449235193246599facba9002e02" -dependencies = [ - "android_log-sys", - "bevy_app 0.14.2", - "bevy_ecs 0.14.2", - "bevy_utils 0.14.2", - "tracing-log", - "tracing-subscriber", - "tracing-wasm", + "bevy_utils", + "bevy_window", + "bevy_winit", ] [[package]] name = "bevy_log" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "android_log-sys", - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_utils", "tracing-log", "tracing-subscriber", "tracing-wasm", ] -[[package]] -name = "bevy_macro_utils" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc65e570012e64a21f3546df68591aaede8349e6174fb500071677f54f06630" -dependencies = [ - "proc-macro2 1.0.86", - "quote 1.0.37", - "syn 2.0.77", - "toml_edit 0.22.20", -] - [[package]] name = "bevy_macro_utils" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", @@ -1359,27 +1065,13 @@ dependencies = [ "toml_edit 0.22.20", ] -[[package]] -name = "bevy_math" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5421792749dda753ab3718e77d27bfce38443daf1850b836b97530b6245a4581" -dependencies = [ - "bevy_reflect 0.14.2", - "glam 0.27.0", - "rand 0.8.5", - "serde", - "smallvec 1.13.2", - "thiserror", -] - [[package]] name = "bevy_math" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_reflect 0.15.0-dev", - "glam 0.28.0", + "bevy_reflect", + "glam", "itertools 0.13.0", "rand 0.8.5", "rand_distr", @@ -1391,16 +1083,16 @@ dependencies = [ [[package]] name = "bevy_mikktspace" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "glam 0.28.0", + "glam", ] [[package]] name = "bevy_nannou" version = "0.1.0" dependencies = [ - "bevy 0.15.0-dev", + "bevy", "bevy_nannou_draw", "bevy_nannou_isf", "bevy_nannou_video", @@ -1410,7 +1102,7 @@ dependencies = [ name = "bevy_nannou_derive" version = "0.1.0" dependencies = [ - "bevy 0.15.0-dev", + "bevy", "bevy_nannou_draw", "proc-macro2 1.0.86", "quote 1.0.37", @@ -1421,7 +1113,7 @@ dependencies = [ name = "bevy_nannou_draw" version = "0.1.0" dependencies = [ - "bevy 0.15.0-dev", + "bevy", "bytemuck", "lyon", "nannou_core", @@ -1435,7 +1127,7 @@ dependencies = [ name = "bevy_nannou_isf" version = "0.1.0" dependencies = [ - "bevy 0.15.0-dev", + "bevy", "bevy-inspector-egui", "bevy_egui 0.29.0 (git+https://github.com/tychedelia/bevy_egui?branch=main)", "bytemuck", @@ -1448,7 +1140,7 @@ dependencies = [ name = "bevy_nannou_video" version = "0.1.0" dependencies = [ - "bevy 0.15.0-dev", + "bevy", "rayon", "serde", "thiserror", @@ -1458,20 +1150,20 @@ dependencies = [ [[package]] name = "bevy_pbr" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", "bevy_core_pipeline", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_derive", + "bevy_ecs", + "bevy_math", + "bevy_reflect", "bevy_render", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_transform", + "bevy_utils", + "bevy_window", "bitflags 2.6.0", "bytemuck", "fixedbitset 0.5.7", @@ -1484,65 +1176,40 @@ dependencies = [ [[package]] name = "bevy_picking" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" -dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_input 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_math", + "bevy_reflect", "bevy_render", - "bevy_time 0.15.0-dev", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bevy_window", "uuid 1.10.0", ] -[[package]] -name = "bevy_ptr" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61baa1bdc1f4a7ac2c18217570a7cc04e1cd54d38456e91782f0371c79afe0a8" - [[package]] name = "bevy_ptr" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" - -[[package]] -name = "bevy_reflect" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2508785a4a5809f25a237eec4fee2c91a4dbcf81324b2bbc2d6c52629e603781" -dependencies = [ - "bevy_ptr 0.14.2", - "bevy_reflect_derive 0.14.2", - "bevy_utils 0.14.2", - "downcast-rs", - "erased-serde", - "glam 0.27.0", - "serde", - "smallvec 1.13.2", - "smol_str", - "thiserror", - "uuid 1.10.0", -] +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" [[package]] name = "bevy_reflect" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_ptr 0.15.0-dev", - "bevy_reflect_derive 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", "downcast-rs", "erased-serde", - "glam 0.28.0", + "glam", "petgraph 0.6.5", "serde", "smallvec 1.13.2", @@ -1551,25 +1218,12 @@ dependencies = [ "uuid 1.10.0", ] -[[package]] -name = "bevy_reflect_derive" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "967d5da1882ec3bb3675353915d3da909cafac033cbf31e58727824a1ad2a288" -dependencies = [ - "bevy_macro_utils 0.14.2", - "proc-macro2 1.0.86", - "quote 1.0.37", - "syn 2.0.77", - "uuid 1.10.0", -] - [[package]] name = "bevy_reflect_derive" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", @@ -1579,28 +1233,28 @@ dependencies = [ [[package]] name = "bevy_render" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "async-channel", - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", - "bevy_core 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_diagnostic 0.15.0-dev", - "bevy_ecs 0.15.0-dev", + "bevy_core", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", "bevy_encase_derive", - "bevy_hierarchy 0.15.0-dev", - "bevy_math 0.15.0-dev", + "bevy_hierarchy", + "bevy_math", "bevy_mikktspace", - "bevy_reflect 0.15.0-dev", + "bevy_reflect", "bevy_render_macros", - "bevy_tasks 0.15.0-dev", - "bevy_time 0.15.0-dev", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", - "bevy_winit 0.15.0-dev", + "bevy_tasks", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bevy_winit", "bitflags 2.6.0", "bytemuck", "codespan-reporting", @@ -1628,9 +1282,9 @@ dependencies = [ [[package]] name = "bevy_render_macros" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", @@ -1639,17 +1293,17 @@ dependencies = [ [[package]] name = "bevy_scene" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" -dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_reflect 0.15.0-dev", +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_reflect", "bevy_render", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_transform", + "bevy_utils", "serde", "thiserror", "uuid 1.10.0", @@ -1658,21 +1312,21 @@ dependencies = [ [[package]] name = "bevy_sprite" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", "bevy_core_pipeline", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_math 0.15.0-dev", + "bevy_derive", + "bevy_ecs", + "bevy_math", "bevy_picking", - "bevy_reflect 0.15.0-dev", + "bevy_reflect", "bevy_render", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_transform", + "bevy_utils", + "bevy_window", "bitflags 2.6.0", "bytemuck", "fixedbitset 0.5.7", @@ -1686,42 +1340,31 @@ dependencies = [ [[package]] name = "bevy_state" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_hierarchy", + "bevy_reflect", "bevy_state_macros", - "bevy_utils 0.15.0-dev", + "bevy_utils", ] [[package]] name = "bevy_state_macros" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_macro_utils 0.15.0-dev", + "bevy_macro_utils", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", ] -[[package]] -name = "bevy_tasks" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77865f310b1fc48fb05b7c4adbe76607ec01d0c14f8ab4caba4d714c86439946" -dependencies = [ - "async-executor", - "futures-lite", - "wasm-bindgen-futures", -] - [[package]] name = "bevy_tasks" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "async-channel", "async-executor", @@ -1735,20 +1378,20 @@ dependencies = [ [[package]] name = "bevy_text" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_app", + "bevy_asset", "bevy_color", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_derive", + "bevy_ecs", + "bevy_math", + "bevy_reflect", "bevy_render", "bevy_sprite", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_transform", + "bevy_utils", + "bevy_window", "cosmic-text", "serde", "sys-locale", @@ -1756,83 +1399,55 @@ dependencies = [ "unicode-bidi", ] -[[package]] -name = "bevy_time" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e4d53ec32a1b16492396951d04de0d2d90e924bf9adcb8d1adacab5ab6c17c" -dependencies = [ - "bevy_app 0.14.2", - "bevy_ecs 0.14.2", - "bevy_reflect 0.14.2", - "bevy_utils 0.14.2", - "crossbeam-channel", - "thiserror", -] - [[package]] name = "bevy_time" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_utils 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_reflect", + "bevy_utils", "crossbeam-channel", "thiserror", ] -[[package]] -name = "bevy_transform" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5493dce84427d00a9266e8e4386d738a72ee8640423b62dfcecb6dfccbfe0d2" -dependencies = [ - "bevy_app 0.14.2", - "bevy_ecs 0.14.2", - "bevy_hierarchy 0.14.2", - "bevy_math 0.14.2", - "bevy_reflect 0.14.2", - "thiserror", -] - [[package]] name = "bevy_transform" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", + "bevy_app", + "bevy_ecs", + "bevy_hierarchy", + "bevy_math", + "bevy_reflect", "thiserror", ] [[package]] name = "bevy_ui" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "bevy_a11y 0.15.0-dev", - "bevy_app 0.15.0-dev", - "bevy_asset 0.15.0-dev", + "bevy_a11y", + "bevy_app", + "bevy_asset", "bevy_color", "bevy_core_pipeline", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_input 0.15.0-dev", - "bevy_math 0.15.0-dev", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_math", "bevy_picking", - "bevy_reflect 0.15.0-dev", + "bevy_reflect", "bevy_render", "bevy_sprite", "bevy_text", - "bevy_transform 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_transform", + "bevy_utils", + "bevy_window", "bytemuck", "nonmax", "smallvec 1.13.2", @@ -1840,28 +1455,13 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bevy_utils" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb0ec333b5965771153bd746f92ffd8aeeb9d008a8620ffd9ed474859381a5e" -dependencies = [ - "ahash", - "bevy_utils_proc_macros 0.14.2", - "getrandom 0.2.15", - "hashbrown 0.14.5", - "thread_local", - "tracing", - "web-time", -] - [[package]] name = "bevy_utils" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "ahash", - "bevy_utils_proc_macros 0.15.0-dev", + "bevy_utils_proc_macros", "getrandom 0.2.15", "hashbrown 0.14.5", "thread_local", @@ -1869,106 +1469,51 @@ dependencies = [ "web-time", ] -[[package]] -name = "bevy_utils_proc_macros" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f1ab8f2f6f58439d260081d89a42b02690e5fdd64f814edc9417d33fcf2857" -dependencies = [ - "proc-macro2 1.0.86", - "quote 1.0.37", - "syn 2.0.77", -] - [[package]] name = "bevy_utils_proc_macros" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.77", ] -[[package]] -name = "bevy_window" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e88a20db64ea8204540afb4699295947c454738fd50293f7b32ab8be857a6" -dependencies = [ - "bevy_a11y 0.14.2", - "bevy_app 0.14.2", - "bevy_ecs 0.14.2", - "bevy_math 0.14.2", - "bevy_reflect 0.14.2", - "bevy_utils 0.14.2", - "raw-window-handle", - "smol_str", -] - [[package]] name = "bevy_window" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" -dependencies = [ - "bevy_a11y 0.15.0-dev", - "bevy_app 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_input 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_utils 0.15.0-dev", +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" +dependencies = [ + "bevy_a11y", + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_reflect", + "bevy_utils", "raw-window-handle", "smol_str", ] -[[package]] -name = "bevy_winit" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0bef8ec3e4b45db943ad4d1c0bf59b09e382ce0651a706e2f33a70fa955303c" -dependencies = [ - "accesskit_winit 0.20.4", - "approx 0.5.1", - "bevy_a11y 0.14.2", - "bevy_app 0.14.2", - "bevy_derive 0.14.2", - "bevy_ecs 0.14.2", - "bevy_hierarchy 0.14.2", - "bevy_input 0.14.2", - "bevy_log 0.14.2", - "bevy_math 0.14.2", - "bevy_reflect 0.14.2", - "bevy_tasks 0.14.2", - "bevy_utils 0.14.2", - "bevy_window 0.14.2", - "cfg-if 1.0.0", - "crossbeam-channel", - "raw-window-handle", - "wasm-bindgen", - "web-sys", - "winit", -] - [[package]] name = "bevy_winit" version = "0.15.0-dev" -source = "git+https://github.com/bevyengine/bevy?branch=main#90bb1adeb28b82be96546e0c2cde81530241f7af" +source = "git+https://github.com/bevyengine/bevy?branch=main#0c92908baf8b7109e1a80d11823ebc62017b9d85" dependencies = [ - "accesskit_winit 0.22.0", + "accesskit_winit", "approx 0.5.1", - "bevy_a11y 0.15.0-dev", - "bevy_app 0.15.0-dev", - "bevy_derive 0.15.0-dev", - "bevy_ecs 0.15.0-dev", - "bevy_hierarchy 0.15.0-dev", - "bevy_input 0.15.0-dev", - "bevy_log 0.15.0-dev", - "bevy_math 0.15.0-dev", - "bevy_reflect 0.15.0-dev", - "bevy_tasks 0.15.0-dev", - "bevy_utils 0.15.0-dev", - "bevy_window 0.15.0-dev", + "bevy_a11y", + "bevy_app", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_log", + "bevy_math", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_window", "cfg-if 1.0.0", "crossbeam-channel", "raw-window-handle", @@ -2000,6 +1545,24 @@ dependencies = [ "which", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.77", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -2365,9 +1928,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "jobserver", "libc", @@ -2753,11 +2316,11 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" +checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" dependencies = [ - "bindgen", + "bindgen 0.70.1", ] [[package]] @@ -2984,9 +2547,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.74+curl-8.9.0" +version = "0.4.75+curl-8.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af10b986114528fcdc4b63b6f5f021b7057618411046a4de2ba0f0149a097bf" +checksum = "2a4fd752d337342e4314717c0d9b6586b059a120c80029ebe4d49b11fec7875e" dependencies = [ "cc", "libc", @@ -3168,7 +2731,7 @@ checksum = "0265fa0e7bcdb058128cdf7597cdacea42e33911713663a04d971a39cad16afa" dependencies = [ "const_panic", "encase_derive", - "glam 0.28.0", + "glam", "thiserror", ] @@ -3277,9 +2840,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] name = "ether-dream" @@ -3353,6 +2916,7 @@ name = "examples" version = "0.1.0" dependencies = [ "audrey", + "bytemuck", "futures 0.3.30", "hotglsl", "hound", @@ -3434,11 +2998,11 @@ dependencies = [ [[package]] name = "ffmpeg-sys-next" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db1b7546e70609ead8c06b2b4c618a1ba352364675f81608f431dd4f321fe3f1" +checksum = "dc33ac996bad6d164a08069a1e9b3370045860dba3d315acda09420f1febef9e" dependencies = [ - "bindgen", + "bindgen 0.69.4", "cc", "libc", "num_cpus", @@ -3934,17 +3498,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "glam" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" -dependencies = [ - "bytemuck", - "rand 0.8.5", - "serde", -] - [[package]] name = "glam" version = "0.28.0" @@ -4207,7 +3760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "344d5bf5d6b6da1020fcfd4014d44e0cc695356c603db9c774b30bd6d385ad2b" dependencies = [ "constgebra", - "glam 0.28.0", + "glam", ] [[package]] @@ -4916,7 +4469,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.3", + "redox_syscall 0.5.4", ] [[package]] @@ -5135,9 +4688,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -5405,7 +4958,7 @@ dependencies = [ name = "nannou" version = "0.19.0" dependencies = [ - "bevy 0.15.0-dev", + "bevy", "bevy-inspector-egui", "bevy_common_assets", "bevy_egui 0.29.0 (git+https://github.com/tychedelia/bevy_egui?branch=main)", @@ -5446,7 +4999,7 @@ dependencies = [ name = "nannou_core" version = "0.19.0" dependencies = [ - "glam 0.28.0", + "glam", "num-traits", "rand 0.8.5", ] @@ -6267,9 +5820,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" [[package]] name = "opener" @@ -6432,7 +5985,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.5.3", + "redox_syscall 0.5.4", "smallvec 1.13.2", "windows-targets 0.52.6", ] @@ -7171,9 +6724,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -7436,9 +6989,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -7488,11 +7041,10 @@ dependencies = [ [[package]] name = "ruzstd" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c8b8f3d26bd9f945e5cbae77f7cdfbf37af9a66956f1115eb4516e45df519f4" +checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21" dependencies = [ - "byteorder", "twox-hash", ] @@ -8646,9 +8198,9 @@ checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-linebreak" @@ -8679,9 +8231,9 @@ checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" diff --git a/Cargo.toml b/Cargo.toml index 280303fd8..d6307d446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,9 @@ bevy_egui = { git = "https://github.com/tychedelia/bevy_egui", branch = "main" } bevy-inspector-egui = { git = "https://github.com/tychedelia/bevy-inspector-egui", branch = "main" } image = "0.25" rayon = "1.10" -bevy_common_assets = "0.11.0" +bevy_common_assets = { git = "https://github.com/tychedelia/bevy_common_assets", branch = "main" } serde = "1" serde_json = "1" toml = "0.8" serde_yaml = "0.9" -wgpu = "22.0" +wgpu = "22.0" \ No newline at end of file diff --git a/bevy_nannou/src/lib.rs b/bevy_nannou/src/lib.rs index 6f921fb1d..5b584a822 100644 --- a/bevy_nannou/src/lib.rs +++ b/bevy_nannou/src/lib.rs @@ -17,7 +17,7 @@ pub mod prelude { pub use bevy_nannou_draw::color::*; pub use bevy_nannou_draw::draw::*; pub use bevy_nannou_draw::render::blend::*; - pub use bevy_nannou_draw::render::NannouMaterialPlugin; + pub use bevy_nannou_draw::render::NannouShaderModelPlugin; pub use bevy_nannou_draw::text::*; pub use bevy_nannou_draw::*; diff --git a/bevy_nannou_draw/src/draw/drawing.rs b/bevy_nannou_draw/src/draw/drawing.rs index 9e349583d..78ea6c210 100644 --- a/bevy_nannou_draw/src/draw/drawing.rs +++ b/bevy_nannou_draw/src/draw/drawing.rs @@ -35,7 +35,7 @@ where // The draw command index of the material being used. pub(crate) material_index: usize, // Whether the **Drawing** should attempt to finish the drawing on drop. - finish_on_drop: bool, + pub(crate) finish_on_drop: bool, // The node type currently being drawn. _ty: PhantomData, } diff --git a/bevy_nannou_draw/src/draw/indirect.rs b/bevy_nannou_draw/src/draw/indirect.rs new file mode 100644 index 000000000..dfb4e5b5e --- /dev/null +++ b/bevy_nannou_draw/src/draw/indirect.rs @@ -0,0 +1,401 @@ +//! A shader that renders a mesh multiple times in one draw call. + +use crate::draw::drawing::Drawing; +use crate::draw::primitive::Primitive; +use crate::draw::{Draw, DrawCommand}; +use crate::render::{PreparedShaderModel, ShaderModel}; +use bevy::core_pipeline::core_3d::Opaque3dBinKey; +use bevy::pbr::{ + MaterialPipeline, MaterialPipelineKey, PreparedMaterial, RenderMaterialInstances, + SetMaterialBindGroup, +}; +use bevy::render::extract_component::ExtractComponentPlugin; +use bevy::render::extract_instances::ExtractedInstances; +use bevy::render::mesh::allocator::MeshAllocator; +use bevy::render::mesh::RenderMeshBufferInfo; +use bevy::render::render_asset::{prepare_assets, RenderAsset}; +use bevy::render::render_phase::{BinnedRenderPhaseType, ViewBinnedRenderPhases}; +use bevy::render::storage::{GpuShaderStorageBuffer, ShaderStorageBuffer}; +use bevy::render::view; +use bevy::render::view::VisibilitySystems; +use bevy::{ + core_pipeline::core_3d::Opaque3d, + ecs::system::{lifetimeless::*, SystemParamItem}, + pbr::{ + MeshPipeline, MeshPipelineKey, RenderMeshInstances, SetMeshBindGroup, SetMeshViewBindGroup, + }, + prelude::*, + render::{ + extract_component::ExtractComponent, + mesh::{MeshVertexBufferLayoutRef, RenderMesh}, + render_asset::RenderAssets, + render_phase::{ + AddRenderCommand, DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, + RenderCommandResult, SetItemPipeline, TrackedRenderPass, + }, + render_resource::*, + renderer::RenderDevice, + view::ExtractedView, + Render, RenderApp, RenderSet, + }, +}; +use rayon::prelude::*; +use std::hash::Hash; +use std::marker::PhantomData; +use std::ops::Range; + +pub struct Indirect<'a, M> +where + M: Material + Default, +{ + draw: &'a Draw, + primitive_index: Option, + indirect_buffer: Option>, +} + +impl<'a, M> Drop for Indirect<'a, M> +where + M: Material + Default, +{ + fn drop(&mut self) { + if let Some((index, ssbo)) = self.primitive_index.take().zip(self.indirect_buffer.take()) { + self.insert_indirect_draw_command(index, ssbo); + } + } +} + +pub fn new(draw: &Draw) -> Indirect +where + M: Material + Default, +{ + Indirect { + draw, + primitive_index: None, + indirect_buffer: None, + } +} + +impl<'a, M> Indirect<'a, M> +where + M: Material + Default, +{ + pub fn primitive(mut self, drawing: Drawing) -> Indirect<'a, M> + where + T: Into, + { + self.draw + .state + .write() + .unwrap() + .ignored_drawings + .insert(drawing.index); + self.primitive_index = Some(drawing.index); + self + } + + pub fn buffer(mut self, ssbo: Handle) -> Indirect<'a, M> { + self.indirect_buffer = Some(ssbo); + self + } + + fn insert_indirect_draw_command( + &self, + index: usize, + indirect_buffer: Handle, + ) { + let mut state = self.draw.state.write().unwrap(); + let primitive = state.drawing.remove(&index).unwrap(); + state + .draw_commands + .push(Some(DrawCommand::Indirect(primitive, indirect_buffer))); + } +} + +#[derive(Component, ExtractComponent, Clone)] +pub struct IndirectMesh; + +pub struct IndirectMaterialPlugin(PhantomData); + +impl Default for IndirectMaterialPlugin +where + M: Default, +{ + fn default() -> Self { + IndirectMaterialPlugin(PhantomData) + } +} + +impl Plugin for IndirectMaterialPlugin +where + SM: ShaderModel, + SM::Data: PartialEq + Eq + Hash + Clone, +{ + fn build(&self, app: &mut App) { + app.add_plugins(( + ExtractComponentPlugin::::default(), + ExtractComponentPlugin::>::default(), + )) + .add_systems( + PostUpdate, + view::check_visibility::>.in_set(VisibilitySystems::CheckVisibility), + ); + + app.sub_app_mut(RenderApp) + .add_render_command::>() + .init_resource::>>() + .add_systems( + Render, + (queue_indirect:: + .after(prepare_assets::>) + .in_set(RenderSet::QueueMeshes),), + ); + } + + fn finish(&self, app: &mut App) { + app.sub_app_mut(RenderApp) + .init_resource::>(); + } +} + +#[allow(clippy::too_many_arguments)] +fn queue_indirect( + draw_functions: Res>, + custom_pipeline: Res>, + mut pipelines: ResMut>>, + pipeline_cache: Res, + meshes: Res>, + ( + render_mesh_instances, + indirect_meshes, + mut phases, + mut views, + shader_models, + extracted_instances, + ): ( + Res, + Query>, + ResMut>, + Query<(Entity, &ExtractedView, &Msaa)>, + Res>>, + Res>>, + ), +) where + SM: ShaderModel, + SM::Data: PartialEq + Eq + Hash + Clone, +{ + let drawn_function = draw_functions.read().id::>(); + + for (view_entity, view, msaa) in &mut views { + let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples()); + let Some(phase) = phases.get_mut(&view_entity) else { + continue; + }; + + let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); + for (entity) in &indirect_meshes { + let Some(shader_model) = extracted_instances.get(&entity) else { + continue; + }; + let shader_model = shader_models.get(*shader_model).unwrap(); + let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity) else { + continue; + }; + let Some(mesh) = meshes.get(mesh_instance.mesh_asset_id) else { + continue; + }; + let mesh_key = + view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology()); + let key = MaterialPipelineKey { + mesh_key, + bind_group_data: shader_model.key.clone(), + }; + let pipeline = pipelines + .specialize(&pipeline_cache, &custom_pipeline, key, &mesh.layout) + .unwrap(); + phase.add( + Opaque3dBinKey { + draw_function: drawn_function, + pipeline, + asset_id: AssetId::::invalid().untyped(), + material_bind_group_id: None, + lightmap_image: None, + }, + entity, + BinnedRenderPhaseType::NonMesh, + ); + } + } +} + +#[derive(Resource)] +struct IndirectPipeline { + mesh_pipeline: MeshPipeline, + shader_model_layout: BindGroupLayout, + vertex_shader: Option>, + fragment_shader: Option>, + marker: PhantomData, +} + +impl FromWorld for IndirectPipeline { + fn from_world(world: &mut World) -> Self { + let asset_server = world.resource::(); + let render_device = world.resource::(); + + IndirectPipeline { + mesh_pipeline: world.resource::().clone(), + shader_model_layout: SM::bind_group_layout(render_device), + vertex_shader: match ::vertex_shader() { + ShaderRef::Default => None, + ShaderRef::Handle(handle) => Some(handle), + ShaderRef::Path(path) => Some(asset_server.load(path)), + }, + fragment_shader: match ::fragment_shader() { + ShaderRef::Default => None, + ShaderRef::Handle(handle) => Some(handle), + ShaderRef::Path(path) => Some(asset_server.load(path)), + }, + marker: PhantomData, + } + } +} + +impl SpecializedMeshPipeline for IndirectPipeline +where + SM::Data: PartialEq + Eq + Hash + Clone, +{ + type Key = MaterialPipelineKey; + + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayoutRef, + ) -> Result { + let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?; + if let Some(vertex_shader) = &self.vertex_shader { + descriptor.vertex.shader = vertex_shader.clone(); + } + + if let Some(fragment_shader) = &self.fragment_shader { + descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone(); + } + + descriptor + .layout + .insert(2, self.shader_model_layout.clone()); + + let pipeline = MaterialPipeline { + mesh_pipeline: self.mesh_pipeline.clone(), + material_layout: self.shader_model_layout.clone(), + vertex_shader: self.vertex_shader.clone(), + fragment_shader: self.fragment_shader.clone(), + marker: Default::default(), + }; + SM::specialize(&pipeline, &mut descriptor, layout, key)?; + Ok(descriptor) + } +} + +type DrawIndirectMaterial = ( + SetItemPipeline, + SetMeshViewBindGroup<0>, + SetShaderModelBindGroup, + DrawMeshIndirect, +); + +struct SetShaderModelBindGroup(PhantomData); +impl RenderCommand

+ for SetShaderModelBindGroup +{ + type Param = ( + SRes>>, + SRes>>, + ); + type ViewQuery = (); + type ItemQuery = (); + + #[inline] + fn render<'w>( + item: &P, + _view: (), + _item_query: Option<()>, + (models, instances): SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, + ) -> RenderCommandResult { + let models = models.into_inner(); + let instances = instances.into_inner(); + + let Some(asset_id) = instances.get(&item.entity()) else { + return RenderCommandResult::Skip; + }; + let Some(material) = models.get(*asset_id) else { + return RenderCommandResult::Skip; + }; + pass.set_bind_group(I, &material.bind_group, &[]); + RenderCommandResult::Success + } +} + +struct DrawMeshIndirect; +impl RenderCommand

for DrawMeshIndirect { + type Param = ( + SRes>, + SRes, + SRes, + SRes>, + ); + type ViewQuery = (); + type ItemQuery = Read>; + + #[inline] + fn render<'w>( + item: &P, + _view: (), + indirect_buffer: Option<&'w Handle>, + (meshes, render_mesh_instances, mesh_allocator, ssbos): SystemParamItem< + 'w, + '_, + Self::Param, + >, + pass: &mut TrackedRenderPass<'w>, + ) -> RenderCommandResult { + let mesh_allocator = mesh_allocator.into_inner(); + + let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity()) + else { + return RenderCommandResult::Skip; + }; + let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id) else { + return RenderCommandResult::Skip; + }; + let Some(indirect_buffer) = indirect_buffer else { + return RenderCommandResult::Skip; + }; + let Some(indirect_buffer) = ssbos.into_inner().get(indirect_buffer) else { + return RenderCommandResult::Skip; + }; + let Some(vertex_buffer_slice) = + mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) + else { + return RenderCommandResult::Skip; + }; + + pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); + + match &gpu_mesh.buffer_info { + RenderMeshBufferInfo::Indexed { index_format, .. } => { + let Some(index_buffer_slice) = + mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id) + else { + return RenderCommandResult::Skip; + }; + + pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); + pass.draw_indexed_indirect(&indirect_buffer.buffer, 0); + } + RenderMeshBufferInfo::NonIndexed => { + pass.draw_indirect(&indirect_buffer.buffer, 0); + } + } + RenderCommandResult::Success + } +} diff --git a/bevy_nannou_draw/src/draw/instanced.rs b/bevy_nannou_draw/src/draw/instanced.rs index ee1897d55..3065e0bed 100644 --- a/bevy_nannou_draw/src/draw/instanced.rs +++ b/bevy_nannou_draw/src/draw/instanced.rs @@ -1,25 +1,32 @@ //! A shader that renders a mesh multiple times in one draw call. +use crate::draw::drawing::Drawing; +use crate::draw::primitive::Primitive; +use crate::draw::{Draw, DrawCommand}; +use crate::render::{PreparedShaderModel, ShaderModel}; +use bevy::core_pipeline::core_3d::Opaque3dBinKey; +use bevy::pbr::{MaterialPipeline, MaterialPipelineKey, PreparedMaterial}; +use bevy::render::extract_component::ExtractComponentPlugin; +use bevy::render::extract_instances::ExtractedInstances; use bevy::render::mesh::allocator::MeshAllocator; use bevy::render::mesh::RenderMeshBufferInfo; -use bevy::render::render_phase::ViewSortedRenderPhases; +use bevy::render::render_asset::prepare_assets; +use bevy::render::render_phase::{BinnedRenderPhaseType, ViewBinnedRenderPhases}; +use bevy::render::storage::GpuShaderStorageBuffer; +use bevy::render::view; +use bevy::render::view::VisibilitySystems; use bevy::{ - core_pipeline::core_3d::Transparent3d, - ecs::{ - query::QueryItem, - system::{lifetimeless::*, SystemParamItem}, - }, - pbr::{ - MeshPipeline, MeshPipelineKey, RenderMeshInstances, SetMeshBindGroup, SetMeshViewBindGroup, - }, + core_pipeline::core_3d::Opaque3d, + ecs::system::{lifetimeless::*, SystemParamItem}, + pbr::{MeshPipeline, MeshPipelineKey, RenderMeshInstances, SetMeshViewBindGroup}, prelude::*, render::{ - extract_component::{ExtractComponent, ExtractComponentPlugin}, + extract_component::ExtractComponent, mesh::{MeshVertexBufferLayoutRef, RenderMesh}, render_asset::RenderAssets, render_phase::{ - AddRenderCommand, DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, - RenderCommandResult, SetItemPipeline, TrackedRenderPass, + AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, + SetItemPipeline, TrackedRenderPass, }, render_resource::*, renderer::RenderDevice, @@ -27,23 +34,18 @@ use bevy::{ Render, RenderApp, RenderSet, }, }; -use bytemuck::{Pod, Zeroable}; use rayon::prelude::*; - -use crate::draw::drawing::Drawing; -use crate::draw::primitive::Primitive; -use crate::draw::{Draw, DrawCommand}; - -static INSTANCING_RENDER: &str = include_str!("render/instancing.wgsl"); - -const SHADER_HANDLE: Handle = Handle::weak_from_u128(24560125337912185962); +use std::hash::Hash; +use std::marker::PhantomData; +use std::ops::Range; pub struct Instanced<'a, M> where M: Material + Default, { draw: &'a Draw, - data: Option<(usize, InstanceMaterialData)>, + primitive_index: Option, + range: Option>, } impl<'a, M> Drop for Instanced<'a, M> @@ -51,7 +53,7 @@ where M: Material + Default, { fn drop(&mut self) { - if let Some((index, data)) = self.data.take() { + if let Some((index, data)) = self.primitive_index.take().zip(self.range.take()) { self.insert_instanced_draw_command(index, data); } } @@ -61,244 +63,296 @@ pub fn new(draw: &Draw) -> Instanced where M: Material + Default, { - Instanced { draw, data: None } + Instanced { + draw, + primitive_index: None, + range: None, + } } impl<'a, M> Instanced<'a, M> where M: Material + Default, { - pub fn with( - mut self, - drawing: Drawing, - input: Vec, - func: F, - ) -> Instanced<'a, M> + pub fn primitive(mut self, drawing: Drawing) -> Instanced<'a, M> where - I: Send + Sync, T: Into, - F: Fn(&I) -> InstanceData + Sync, { - let data = input - .par_iter() - .map(|i| func(i)) - .collect::>(); - let data = InstanceMaterialData(data); self.draw .state .write() .unwrap() - .instanced + .ignored_drawings .insert(drawing.index); - self.data = Some((drawing.index, data)); + self.primitive_index = Some(drawing.index); + self + } + + pub fn range(mut self, range: Range) -> Instanced<'a, M> { + self.range = Some(range); self } - fn insert_instanced_draw_command(&self, index: usize, data: InstanceMaterialData) { + fn insert_instanced_draw_command(&self, index: usize, range: Range) { let mut state = self.draw.state.write().unwrap(); let primitive = state.drawing.remove(&index).unwrap(); state .draw_commands - .push(Some(DrawCommand::Instanced(primitive, data))); + .push(Some(DrawCommand::Instanced(primitive, range))); } } -#[derive(Component, Deref, Clone, Debug)] -pub struct InstanceMaterialData(pub Vec); +#[derive(Component, ExtractComponent, Clone)] +pub struct InstancedMesh; -impl ExtractComponent for InstanceMaterialData { - type QueryData = &'static InstanceMaterialData; - type QueryFilter = (); - type Out = Self; +#[derive(Component, ExtractComponent, Clone)] +pub struct InstanceRange(pub Range); - fn extract_component(item: QueryItem<'_, Self::QueryData>) -> Option { - Some(InstanceMaterialData(item.0.clone())) +pub struct InstancedMaterialPlugin(PhantomData); + +impl Default for InstancedMaterialPlugin +where + M: Default, +{ + fn default() -> Self { + InstancedMaterialPlugin(PhantomData) } } -pub struct InstancingPlugin; - -impl Plugin for InstancingPlugin { +impl Plugin for InstancedMaterialPlugin +where + SM: ShaderModel, + SM::Data: PartialEq + Eq + Hash + Clone, +{ fn build(&self, app: &mut App) { - app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(( + ExtractComponentPlugin::::default(), + ExtractComponentPlugin::::default(), + )) + .add_systems( + PostUpdate, + view::check_visibility::> + .in_set(VisibilitySystems::CheckVisibility), + ); + app.sub_app_mut(RenderApp) - .add_render_command::() - .init_resource::>() + .add_render_command::>() + .init_resource::>>() .add_systems( Render, - ( - queue_instanced.in_set(RenderSet::QueueMeshes), - prepare_instance_buffers.in_set(RenderSet::PrepareResources), - ), + (queue_instanced:: + .after(prepare_assets::>) + .in_set(RenderSet::QueueMeshes),), ); } fn finish(&self, app: &mut App) { - app.world_mut() - .resource_mut::>() - .get_or_insert_with(&SHADER_HANDLE, || { - Shader::from_wgsl(INSTANCING_RENDER, file!()) - }); - app.sub_app_mut(RenderApp) - .init_resource::(); + .init_resource::>(); } } -#[derive(Debug, Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct InstanceData { - pub position: Vec3, - pub scale: f32, - pub color: [f32; 4], -} - #[allow(clippy::too_many_arguments)] -fn queue_instanced( - transparent_3d_draw_functions: Res>, - custom_pipeline: Res, - mut pipelines: ResMut>, +fn queue_instanced( + draw_functions: Res>, + custom_pipeline: Res>, + mut pipelines: ResMut>>, pipeline_cache: Res, meshes: Res>, - render_mesh_instances: Res, - material_meshes: Query>, - mut transparent_render_phases: ResMut>, - mut views: Query<(Entity, &ExtractedView, &Msaa)>, -) { - let draw_custom = transparent_3d_draw_functions.read().id::(); + ( + render_mesh_instances, + instanced_meshes, + mut phases, + mut views, + shader_models, + extracted_instances, + ): ( + Res, + Query>, + ResMut>, + Query<(Entity, &ExtractedView, &Msaa)>, + Res>>, + Res>>, + ), +) where + SM: ShaderModel, + SM::Data: PartialEq + Eq + Hash + Clone, +{ + let drawn_function = draw_functions.read().id::>(); for (view_entity, view, msaa) in &mut views { let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples()); - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + let Some(phase) = phases.get_mut(&view_entity) else { continue; }; let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); - let rangefinder = view.rangefinder3d(); - for entity in &material_meshes { + for (entity) in &instanced_meshes { + let Some(shader_model) = extracted_instances.get(&entity) else { + continue; + }; + let shader_model = shader_models.get(*shader_model).unwrap(); let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity) else { continue; }; let Some(mesh) = meshes.get(mesh_instance.mesh_asset_id) else { continue; }; - let key = + let mesh_key = view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology()); + let key = MaterialPipelineKey { + mesh_key, + bind_group_data: shader_model.key.clone(), + }; let pipeline = pipelines .specialize(&pipeline_cache, &custom_pipeline, key, &mesh.layout) .unwrap(); - transparent_phase.add(Transparent3d { + phase.add( + Opaque3dBinKey { + draw_function: drawn_function, + pipeline, + asset_id: AssetId::::invalid().untyped(), + material_bind_group_id: None, + lightmap_image: None, + }, entity, - pipeline, - draw_function: draw_custom, - distance: rangefinder.distance_translation(&mesh_instance.translation), - batch_range: 0..1, - extra_index: PhaseItemExtraIndex::NONE, - }); + BinnedRenderPhaseType::NonMesh, + ); } } } -#[derive(Component)] -struct InstanceBuffer { - buffer: Buffer, - length: usize, -} - -fn prepare_instance_buffers( - mut commands: Commands, - query: Query<(Entity, &InstanceMaterialData)>, - render_device: Res, -) { - for (entity, instance_data) in &query { - let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { - label: Some("instance data buffer"), - contents: bytemuck::cast_slice(instance_data.as_slice()), - usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, - }); - commands.entity(entity).insert(InstanceBuffer { - buffer, - length: instance_data.len(), - }); - } -} - #[derive(Resource)] -struct InstancedDataPipeline { - shader: Handle, +struct InstancedPipeline { mesh_pipeline: MeshPipeline, + shader_model_layout: BindGroupLayout, + vertex_shader: Option>, + fragment_shader: Option>, + marker: PhantomData, } -impl FromWorld for InstancedDataPipeline { +impl FromWorld for InstancedPipeline { fn from_world(world: &mut World) -> Self { - let mesh_pipeline = world.resource::(); - - InstancedDataPipeline { - shader: SHADER_HANDLE, - mesh_pipeline: mesh_pipeline.clone(), + let asset_server = world.resource::(); + let render_device = world.resource::(); + + InstancedPipeline { + mesh_pipeline: world.resource::().clone(), + shader_model_layout: SM::bind_group_layout(render_device), + vertex_shader: match ::vertex_shader() { + ShaderRef::Default => None, + ShaderRef::Handle(handle) => Some(handle), + ShaderRef::Path(path) => Some(asset_server.load(path)), + }, + fragment_shader: match ::fragment_shader() { + ShaderRef::Default => None, + ShaderRef::Handle(handle) => Some(handle), + ShaderRef::Path(path) => Some(asset_server.load(path)), + }, + marker: PhantomData, } } } -impl SpecializedMeshPipeline for InstancedDataPipeline { - type Key = MeshPipelineKey; +impl SpecializedMeshPipeline for InstancedPipeline +where + SM::Data: PartialEq + Eq + Hash + Clone, +{ + type Key = MaterialPipelineKey; fn specialize( &self, key: Self::Key, layout: &MeshVertexBufferLayoutRef, ) -> Result { - let mut descriptor = self.mesh_pipeline.specialize(key, layout)?; - - descriptor.vertex.shader = self.shader.clone(); - descriptor.vertex.buffers.push(VertexBufferLayout { - array_stride: std::mem::size_of::() as u64, - step_mode: VertexStepMode::Instance, - attributes: vec![ - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 0, - shader_location: 3, // shader locations 0-2 are taken up by Position, Normal and UV attributes - }, - VertexAttribute { - format: VertexFormat::Float32x4, - offset: VertexFormat::Float32x4.size(), - shader_location: 4, - }, - ], - }); - descriptor.fragment.as_mut().unwrap().shader = self.shader.clone(); + let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?; + if let Some(vertex_shader) = &self.vertex_shader { + descriptor.vertex.shader = vertex_shader.clone(); + } + + if let Some(fragment_shader) = &self.fragment_shader { + descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone(); + } + + descriptor + .layout + .insert(2, self.shader_model_layout.clone()); + + let pipeline = MaterialPipeline { + mesh_pipeline: self.mesh_pipeline.clone(), + material_layout: self.shader_model_layout.clone(), + vertex_shader: self.vertex_shader.clone(), + fragment_shader: self.fragment_shader.clone(), + marker: Default::default(), + }; + SM::specialize(&pipeline, &mut descriptor, layout, key)?; Ok(descriptor) } } -type DrawInstanced = ( +type DrawInstancedMaterial = ( SetItemPipeline, SetMeshViewBindGroup<0>, - SetMeshBindGroup<1>, + SetShaderModelBindGroup, DrawMeshInstanced, ); -struct DrawMeshInstanced; +struct SetShaderModelBindGroup(PhantomData); +impl RenderCommand

+ for SetShaderModelBindGroup +{ + type Param = ( + SRes>>, + SRes>>, + ); + type ViewQuery = (); + type ItemQuery = (); + #[inline] + fn render<'w>( + item: &P, + _view: (), + _item_query: Option<()>, + (models, instances): SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, + ) -> RenderCommandResult { + let models = models.into_inner(); + let instances = instances.into_inner(); + + let Some(asset_id) = instances.get(&item.entity()) else { + return RenderCommandResult::Skip; + }; + let Some(material) = models.get(*asset_id) else { + return RenderCommandResult::Skip; + }; + pass.set_bind_group(I, &material.bind_group, &[]); + RenderCommandResult::Success + } +} + +struct DrawMeshInstanced; impl RenderCommand

for DrawMeshInstanced { type Param = ( SRes>, SRes, SRes, + SRes>, ); type ViewQuery = (); - type ItemQuery = Read; + type ItemQuery = Read; #[inline] fn render<'w>( item: &P, _view: (), - instance_buffer: Option<&'w InstanceBuffer>, - (meshes, render_mesh_instances, mesh_allocator): SystemParamItem<'w, '_, Self::Param>, + instance_range: Option<&'w InstanceRange>, + (meshes, render_mesh_instances, mesh_allocator, ssbos): SystemParamItem< + 'w, + '_, + Self::Param, + >, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { - // A borrow check workaround. let mesh_allocator = mesh_allocator.into_inner(); let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity()) @@ -308,7 +362,7 @@ impl RenderCommand

for DrawMeshInstanced { let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id) else { return RenderCommandResult::Skip; }; - let Some(instance_buffer) = instance_buffer else { + let Some(instance_range) = instance_range else { return RenderCommandResult::Skip; }; let Some(vertex_buffer_slice) = @@ -318,13 +372,9 @@ impl RenderCommand

for DrawMeshInstanced { }; pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); - pass.set_vertex_buffer(1, instance_buffer.buffer.slice(..)); match &gpu_mesh.buffer_info { - RenderMeshBufferInfo::Indexed { - index_format, - count, - } => { + RenderMeshBufferInfo::Indexed { index_format, .. } => { let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id) else { @@ -333,13 +383,13 @@ impl RenderCommand

for DrawMeshInstanced { pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); pass.draw_indexed( - index_buffer_slice.range.start..(index_buffer_slice.range.start + count), - vertex_buffer_slice.range.start as i32, - 0..instance_buffer.length as u32, + index_buffer_slice.range.clone(), + 0, + instance_range.0.clone(), ); } RenderMeshBufferInfo::NonIndexed => { - pass.draw(0..gpu_mesh.vertex_count, 0..instance_buffer.length as u32); + pass.draw(vertex_buffer_slice.range.clone(), instance_range.0.clone()); } } RenderCommandResult::Success diff --git a/bevy_nannou_draw/src/draw/mod.rs b/bevy_nannou_draw/src/draw/mod.rs index 9c2155150..54ab47a45 100644 --- a/bevy_nannou_draw/src/draw/mod.rs +++ b/bevy_nannou_draw/src/draw/mod.rs @@ -4,27 +4,29 @@ use std::any::{Any, TypeId}; use std::marker::PhantomData; -use std::ops::Deref; +use std::ops::{Deref, Range}; use std::sync::{Arc, RwLock}; +pub use self::background::Background; +pub use self::drawing::{Drawing, DrawingContext}; +use self::primitive::Primitive; +pub use self::theme::Theme; +use crate::draw::indirect::Indirect; +use crate::draw::instanced::Instanced; +use crate::draw::mesh::MeshExt; +use crate::render::DefaultNannouMaterial; use bevy::asset::UntypedAssetId; use bevy::prelude::*; use bevy::render::render_resource as wgpu; use bevy::render::render_resource::{BlendComponent, BlendState}; +use bevy::render::storage::ShaderStorageBuffer; use bevy::utils::{HashMap, HashSet}; use lyon::path::PathEvent; use uuid::Uuid; -pub use self::background::Background; -pub use self::drawing::{Drawing, DrawingContext}; -use self::primitive::Primitive; -pub use self::theme::Theme; -use crate::draw::instanced::{InstanceMaterialData, Instanced}; -use crate::draw::mesh::MeshExt; -use crate::render::DefaultNannouMaterial; - pub mod background; mod drawing; +pub mod indirect; pub mod instanced; pub mod mesh; pub mod primitive; @@ -117,7 +119,9 @@ pub enum DrawCommand { /// Draw a primitive. Primitive(Primitive), /// Draw an instanced primitive - Instanced(Primitive, InstanceMaterialData), + Instanced(Primitive, Range), + /// Draw a primitive using an indirect buffer. + Indirect(Primitive, Handle), /// A change in the rendering context occurred. Context(DrawContext), /// A change in the material occurred. @@ -146,8 +150,9 @@ pub struct State { drawing: HashMap, /// A map of all type erased materials used by the draw. pub(crate) materials: HashMap>, - /// A list of indices of primitives that are being drawn as instances and should not be drawn - instanced: HashSet, + /// A list of indices of primitives that are being drawn via an alternate method and + /// should not be drawn + ignored_drawings: HashSet, /// The list of recorded draw commands. /// /// An element may be `None` if it is a primitive in the process of being drawn. @@ -204,7 +209,7 @@ impl State { // Finish the drawing at the given node index if it is not yet complete. pub(crate) fn finish_drawing(&mut self, index: usize) { // Don't draw if the primitive is going to be instanced - if self.instanced.contains(&index) { + if self.ignored_drawings.contains(&index) { return; } @@ -512,6 +517,10 @@ where instanced::new(self) } + pub fn indirect<'a>(&'a self) -> Indirect<'a, M> { + indirect::new(self) + } + /// Add the given type to be drawn. pub fn a(&self, primitive: T) -> Drawing where @@ -700,7 +709,7 @@ impl Default for State { intermediary_state, theme, background_color, - instanced: Default::default(), + ignored_drawings: Default::default(), materials: Default::default(), } } diff --git a/bevy_nannou_draw/src/draw/render/instancing.wgsl b/bevy_nannou_draw/src/draw/render/instancing.wgsl deleted file mode 100644 index 856022536..000000000 --- a/bevy_nannou_draw/src/draw/render/instancing.wgsl +++ /dev/null @@ -1,36 +0,0 @@ -#import bevy_pbr::mesh_functions::{get_model_matrix, mesh_position_local_to_clip} - -struct Vertex { - @location(0) position: vec3, - @location(1) normal: vec3, - @location(2) uv: vec2, - - @location(3) i_pos_scale: vec4, - @location(4) i_color: vec4, -}; - -struct VertexOutput { - @builtin(position) clip_position: vec4, - @location(0) color: vec4, -}; - -@vertex -fn vertex(vertex: Vertex) -> VertexOutput { - let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz; - var out: VertexOutput; - // NOTE: Passing 0 as the instance_index to get_model_matrix() is a hack - // for this example as the instance_index builtin would map to the wrong - // index in the Mesh array. This index could be passed in via another - // uniform instead but it's unnecessary for the example. - out.clip_position = mesh_position_local_to_clip( - get_model_matrix(0u), - vec4(position, 1.0) - ); - out.color = vec4(1.0, 0.0, 0.0, 1.0); - return out; -} - -@fragment -fn fragment(in: VertexOutput) -> @location(0) vec4 { - return in.color; -} diff --git a/bevy_nannou_draw/src/render.rs b/bevy_nannou_draw/src/render.rs index abc475516..c1a37c3fc 100644 --- a/bevy_nannou_draw/src/render.rs +++ b/bevy_nannou_draw/src/render.rs @@ -3,26 +3,34 @@ use std::hash::Hash; use bevy::asset::Asset; use bevy::asset::UntypedAssetId; +use bevy::ecs::system::lifetimeless::SRes; +use bevy::ecs::system::SystemParamItem; use bevy::pbr::{ - ExtendedMaterial, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline, - StandardMaterial, + DefaultOpaqueRendererMethod, ExtendedMaterial, MaterialExtension, MaterialExtensionKey, + MaterialExtensionPipeline, MaterialPipeline, MaterialProperties, MeshPipelineKey, + OpaqueRendererMethod, PreparedMaterial, StandardMaterial, }; use bevy::prelude::TypePath; use bevy::prelude::*; use bevy::render::camera::RenderTarget; use bevy::render::extract_component::{ExtractComponent, ExtractComponentPlugin}; +use bevy::render::extract_instances::ExtractInstancesPlugin; use bevy::render::extract_resource::{ExtractResource, ExtractResourcePlugin}; use bevy::render::mesh::MeshVertexBufferLayoutRef; -use bevy::render::render_resource as wgpu; +use bevy::render::render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin}; use bevy::render::render_resource::{ - AsBindGroup, BlendState, PolygonMode, RenderPipelineDescriptor, ShaderRef, - SpecializedMeshPipelineError, + AsBindGroup, AsBindGroupError, BindGroup, BlendState, OwnedBindingResource, PolygonMode, + RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError, }; +use bevy::render::renderer::RenderDevice; use bevy::render::view::{NoFrustumCulling, RenderLayers}; +use bevy::render::RenderSet::Render; +use bevy::render::{render_resource as wgpu, RenderApp}; use bevy::window::WindowRef; use lyon::lyon_tessellation::{FillTessellator, StrokeTessellator}; -use crate::draw::instanced::InstancingPlugin; +use crate::draw::indirect::{IndirectMaterialPlugin, IndirectMesh}; +use crate::draw::instanced::{InstanceRange, InstancedMaterialPlugin, InstancedMesh}; use crate::draw::mesh::MeshExt; use crate::draw::render::{RenderContext, RenderPrimitive}; use crate::draw::{DrawCommand, DrawContext}; @@ -50,11 +58,7 @@ pub struct NannouRenderPlugin; impl Plugin for NannouRenderPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup_default_texture) - .add_plugins(( - ExtractComponentPlugin::::default(), - NannouMaterialPlugin::::default(), - InstancingPlugin, - )) + .add_plugins((ExtractComponentPlugin::::default(),)) .add_plugins(ExtractResourcePlugin::::default()) .add_systems(Update, texture_event_handler) .add_systems(PostUpdate, update_draw_mesh); @@ -64,16 +68,64 @@ impl Plugin for NannouRenderPlugin { #[derive(Default)] pub struct NannouMaterialPlugin(std::marker::PhantomData); -impl Plugin for NannouMaterialPlugin +impl Plugin for NannouMaterialPlugin where + M: Material + Default, M::Data: PartialEq + Eq + Hash + Clone, { fn build(&self, app: &mut App) { - app.add_plugins(MaterialPlugin::::default()) + app.add_plugins((MaterialPlugin::::default(),)) .add_systems(PostUpdate, update_material::.after(update_draw_mesh)); } } +#[derive(Default)] +pub struct NannouShaderModelPlugin(std::marker::PhantomData); + +impl Plugin for NannouShaderModelPlugin +where + SM: ShaderModel, + SM::Data: PartialEq + Eq + Hash + Clone, +{ + fn build(&self, app: &mut App) { + app.add_plugins(( + RenderAssetPlugin::>::default(), + IndirectMaterialPlugin::::default(), + InstancedMaterialPlugin::::default(), + )) + .add_systems(PostUpdate, update_material::.after(update_draw_mesh)); + } +} + +pub struct PreparedShaderModel { + pub bindings: Vec<(u32, OwnedBindingResource)>, + pub bind_group: BindGroup, + pub key: T::Data, +} + +impl RenderAsset for PreparedShaderModel { + type SourceAsset = T; + + type Param = (SRes, SRes>, T::Param); + + fn prepare_asset( + material: Self::SourceAsset, + (render_device, pipeline, ref mut material_param): &mut SystemParamItem, + ) -> Result> { + match material.as_bind_group(&pipeline.material_layout, render_device, material_param) { + Ok(prepared) => Ok(PreparedShaderModel { + bindings: prepared.bindings, + bind_group: prepared.bind_group, + key: prepared.data, + }), + Err(AsBindGroupError::RetryNextUpdate) => { + Err(PrepareAssetError::RetryNextUpdate(material)) + } + Err(other) => Err(PrepareAssetError::AsBindGroupError(other)), + } + } +} + // ---------------------------------------------------------------------------- // Components and Resources // ---------------------------------------------------------------------------- @@ -220,6 +272,7 @@ fn update_draw_mesh( let mut fill_tessellator = FillTessellator::new(); let mut stroke_tessellator = StrokeTessellator::new(); + let mut last_mat = None; let mut mesh = meshes.add(Mesh::init()); let mut curr_ctx: DrawContext = Default::default(); @@ -248,7 +301,7 @@ fn update_draw_mesh( let mut mesh = meshes.get_mut(&mesh).unwrap(); prim.render_primitive(ctxt, &mut mesh); } - DrawCommand::Instanced(prim, instance_data) => { + DrawCommand::Instanced(prim, range) => { let ctxt = RenderContext { intermediary_mesh: &intermediary_state.intermediary_mesh, path_event_buffer: &intermediary_state.path_event_buffer, @@ -265,13 +318,54 @@ fn update_draw_mesh( // Render the primitive. let mut mesh = Mesh::init(); prim.render_primitive(ctxt, &mut mesh); - mesh = mesh.with_removed_attribute(Mesh::ATTRIBUTE_COLOR); let mesh = meshes.add(mesh); + let mat_id = last_mat.expect("No material set for instanced draw command"); commands.spawn(( + InstancedMesh, + InstanceRange(range), + UntypedMaterialId(mat_id), + mesh.clone(), + Transform::default(), + GlobalTransform::default(), + Visibility::default(), + InheritedVisibility::default(), + ViewVisibility::default(), + NannouMesh, + NoFrustumCulling, + window_layers.clone(), + )); + } + DrawCommand::Indirect(prim, indirect_buffer) => { + // Info required during rendering. + let ctxt = RenderContext { + intermediary_mesh: &intermediary_state.intermediary_mesh, + path_event_buffer: &intermediary_state.path_event_buffer, + path_points_vertex_buffer: &intermediary_state.path_points_vertex_buffer, + text_buffer: &intermediary_state.text_buffer, + theme: &draw_state.theme, + transform: &curr_ctx.transform, + fill_tessellator: &mut fill_tessellator, + stroke_tessellator: &mut stroke_tessellator, + output_attachment_size: Vec2::new(window.width(), window.height()), + output_attachment_scale_factor: window.scale_factor(), + }; + + // Render the primitive. + let mut mesh = Mesh::init(); + prim.render_primitive(ctxt, &mut mesh); + let mesh = meshes.add(mesh); + let mat_id = last_mat.expect("No material set for instanced draw command"); + commands.spawn(( + IndirectMesh, + indirect_buffer, + UntypedMaterialId(mat_id), + mesh.clone(), + Transform::default(), + GlobalTransform::default(), + Visibility::default(), + InheritedVisibility::default(), + ViewVisibility::default(), NannouMesh, - mesh, - SpatialBundle::INHERITED_IDENTITY, - instance_data, NoFrustumCulling, window_layers.clone(), )); @@ -281,6 +375,7 @@ fn update_draw_mesh( } DrawCommand::Material(mat_id) => { // We switched materials, so start rendering into a new mesh + last_mat = Some(mat_id.clone()); mesh = meshes.add(Mesh::init()); commands.spawn(( UntypedMaterialId(mat_id), diff --git a/bevy_nannou_isf/src/lib.rs b/bevy_nannou_isf/src/lib.rs index 5e4e1a83e..9e2b324f4 100644 --- a/bevy_nannou_isf/src/lib.rs +++ b/bevy_nannou_isf/src/lib.rs @@ -6,13 +6,8 @@ use bevy::prelude::*; use bevy::render::extract_component::ExtractComponentPlugin; use bevy::render::extract_resource::ExtractResourcePlugin; use bevy::render::view; -use bevy::render::view::{NoFrustumCulling, VisibilitySystems}; -use bevy::window::PrimaryWindow; -use bevy_egui::{egui, EguiContext}; use bevy_inspector_egui::inspector_egui_impls::InspectorEguiImpl; use bevy_inspector_egui::quick::ResourceInspectorPlugin; -use bevy_inspector_egui::DefaultInspectorConfigPlugin; -use std::any::TypeId; mod asset; mod inputs; diff --git a/default.nix b/default.nix index 8c43d66c3..bd0394553 100644 --- a/default.nix +++ b/default.nix @@ -25,7 +25,8 @@ rustPlatform.buildRustPackage rec { lockFile = ./Cargo.lock; outputHashes = { "skeptic-0.13.8" = "sha256-LLVrpuyQsMdbp8OYcHN0nq+uKC8xgJzpNy+gyXxTYbo="; - "bevy-0.15.0-dev" = "sha256-68Jwn6QEt3F24j6WmPAZz6D5gN8BYE706TRx3FE/6qs="; + "bevy-0.15.0-dev" = "sha256-rWYgxycLt96gScoRV3qDY/qV7DV8+LVqClbuIZ462hI="; + "bevy_common_assets-0.11.0" = "sha256-e19BQw/mS0sLfJzoS+g6un0UPcRFdSZVsTWf75ws6sQ="; "bevy-inspector-egui-0.25.2" = "sha256-yjzmnHAxkejNtW8+cOV85IiGRM0614D7WtiauE6KWMA="; "bevy_egui-0.29.0" = "sha256-3UiUBpDhpud42ZcDwPHhSzmnlXkd9rH14lqXeRHdLlU="; }; diff --git a/examples/Cargo.toml b/examples/Cargo.toml index bd5af915c..06e638d6d 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -27,16 +27,13 @@ walkdir = "2" hound = "3.4.0" ringbuf = "0.2.2" futures = "0.3" +bytemuck = "1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { version = "1", features = ["full"]} [target.'cfg(target_arch = "wasm32")'.dependencies] tokio = { version = "1", features = ["rt"]} -[features] -egui = ["nannou/egui"] -isf = ["nannou/isf"] -video = ["nannou/video"] # Audio [[example]] @@ -63,6 +60,17 @@ path = "communication/osc_receiver.rs" name = "osc_sender" path = "communication/osc_sender.rs" +# Compute +[[example]] +name = "game_of_life" +path = "compute/game_of_life.rs" +[[example]] +name = "particle_mouse" +path = "compute/particle_mouse.rs" +[[example]] +name = "particle_sdf" +path = "compute/particle_sdf.rs" + # Draw [[example]] name = "draw" @@ -127,7 +135,7 @@ path = "draw/draw_transform.rs" [[example]] name = "isf_simple" path = "isf/simple.rs" -required-features = ["isf", "egui"] +required-features = ["nannou/isf", "nannou/egui"] # Laser [[example]] @@ -139,7 +147,7 @@ path = "laser/laser_frame_stream.rs" [[example]] name = "laser_frame_stream_gui" path = "laser/laser_frame_stream_gui.rs" -required-features = ["egui"] +required-features = ["nannou/egui"] [[example]] name = "laser_raw_stream" path = "laser/laser_raw_stream.rs" @@ -204,19 +212,19 @@ path = "templates/template_sketch.rs" [[example]] name = "circle_packing" path = "ui/egui/circle_packing.rs" -required-features = ["egui"] +required-features = ["nannou/egui"] [[example]] name = "tune_color" path = "ui/egui/tune_color.rs" -required-features = ["egui"] +required-features = ["nannou/egui"] [[example]] name = "simple_ui" path = "ui/egui/simple_ui.rs" -required-features = ["egui"] +required-features = ["nannou/egui"] [[example]] name = "inspector_ui" path = "ui/egui/inspector_ui.rs" -required-features = ["egui"] +required-features = ["nannou/egui"] # Video [[example]] diff --git a/examples/assets/shaders/game_of_life.wgsl b/examples/assets/shaders/game_of_life.wgsl new file mode 100644 index 000000000..31833fe38 --- /dev/null +++ b/examples/assets/shaders/game_of_life.wgsl @@ -0,0 +1,70 @@ +// The shader reads the previous frame's state from the `input` texture, and writes the new state of +// each pixel to the `output` texture. The textures are flipped each step to progress the +// simulation. +// Two textures are needed for the game of life as each pixel of step N depends on the state of its +// neighbors at step N-1. + +@group(0) @binding(0) var input: texture_storage_2d; +@group(0) @binding(1) var output: texture_storage_2d; + +fn hash(value: u32) -> u32 { + var state = value; + state = state ^ 2747636419u; + state = state * 2654435769u; + state = state ^ state >> 16u; + state = state * 2654435769u; + state = state ^ state >> 16u; + state = state * 2654435769u; + return state; +} + +fn randomFloat(value: u32) -> f32 { + return f32(hash(value)) / 4294967295.0; +} + +@compute @workgroup_size(8, 8, 1) +fn init(@builtin(global_invocation_id) invocation_id: vec3, @builtin(num_workgroups) num_workgroups: vec3) { + let location = vec2(i32(invocation_id.x), i32(invocation_id.y)); + + let randomNumber = randomFloat(invocation_id.y << 16u | invocation_id.x); + let alive = randomNumber > 0.9; + let color = vec4(f32(alive)); + + textureStore(output, location, color); +} + +fn is_alive(location: vec2, offset_x: i32, offset_y: i32) -> i32 { + let value: vec4 = textureLoad(input, location + vec2(offset_x, offset_y)); + return i32(value.x); +} + +fn count_alive(location: vec2) -> i32 { + return is_alive(location, -1, -1) + + is_alive(location, -1, 0) + + is_alive(location, -1, 1) + + is_alive(location, 0, -1) + + is_alive(location, 0, 1) + + is_alive(location, 1, -1) + + is_alive(location, 1, 0) + + is_alive(location, 1, 1); +} + +@compute @workgroup_size(8, 8, 1) +fn update(@builtin(global_invocation_id) invocation_id: vec3) { + let location = vec2(i32(invocation_id.x), i32(invocation_id.y)); + + let n_alive = count_alive(location); + + var alive: bool; + if (n_alive == 3) { + alive = true; + } else if (n_alive == 2) { + let currently_alive = is_alive(location, 0, 0); + alive = bool(currently_alive); + } else { + alive = false; + } + let color = vec4(f32(alive)); + + textureStore(output, location, color); +} diff --git a/examples/assets/shaders/particle_mouse_compute.wgsl b/examples/assets/shaders/particle_mouse_compute.wgsl new file mode 100644 index 000000000..bf6ab364c --- /dev/null +++ b/examples/assets/shaders/particle_mouse_compute.wgsl @@ -0,0 +1,78 @@ +struct Particle { + position: vec2, + velocity: vec2, + color: vec4, +}; + +@group(0) @binding(0) var particles: array; +@group(0) @binding(1) var mouse: vec2; +@group(0) @binding(2) var attract_strength: f32; +@group(0) @binding(3) var particle_count: u32; + +fn random(seed: vec2) -> f32 { + return fract(sin(dot(seed, vec2(12.9898, 78.233))) * 43758.5453); +} + +@compute @workgroup_size(64) +fn init(@builtin(global_invocation_id) global_id: vec3) { + let index = global_id.x; + if (index >= particle_count) { + return; + } + + var particle: Particle; + + // Initialize position randomly within clip space (-1 to 1) + particle.position = vec2( + random(vec2(f32(index), 0.0)) * 2.0 - 1.0, + random(vec2(0.0, f32(index))) * 2.0 - 1.0 + ); + + // Initialize velocity (in clip space, so use smaller values) + particle.velocity = vec2( + (random(vec2(f32(index), 1.0)) - 0.5) * 0.002, + (random(vec2(1.0, f32(index))) - 0.5) * 0.002 + ); + + particle.color = vec4(0.0, 0.0, 0.0, 0.0); + + particles[index] = particle; +} + +@compute @workgroup_size(64) +fn update(@builtin(global_invocation_id) global_id: vec3) { + let index = global_id.x; + if (index >= particle_count) { + return; + } + + var particle = particles[index]; + + // Update particle position + particle.position = particle.position + particle.velocity; + + // Attract particles to mouse + let to_mouse = mouse - particle.position; + particle.velocity = particle.velocity + (normalize(to_mouse) * 0.00001 * attract_strength); + + // Bounce off screen edges + if (particle.position.x < -1.0 || particle.position.x > 1.0) { + particle.velocity.x = -particle.velocity.x; + } + if (particle.position.y < -1.0 || particle.position.y > 1.0) { + particle.velocity.y = -particle.velocity.y; + } + + // Keep particles within bounds + particle.position = clamp(particle.position, vec2(-1.0), vec2(1.0)); + // Limit velocity to prevent particles from becoming too energetic + particle.velocity = clamp(particle.velocity, vec2(-0.01), vec2(0.01)); + + // Update color based on velocity + particle.color = vec4(abs(particle.velocity) * 100.0, 1.0, 1.0); + if (index == 0) { + particle.color = vec4(1.0, 0.0, 0.0, 1.0); // Red for first particle + } + + particles[index] = particle; +} \ No newline at end of file diff --git a/examples/assets/shaders/particle_mouse_material.wgsl b/examples/assets/shaders/particle_mouse_material.wgsl new file mode 100644 index 000000000..6ad0a6400 --- /dev/null +++ b/examples/assets/shaders/particle_mouse_material.wgsl @@ -0,0 +1,40 @@ +#import bevy_pbr::{ + forward_io::{Vertex} + mesh_functions::{get_world_from_local, mesh_position_local_to_clip, mesh_position_local_to_world} + view_transformations::{position_world_to_clip} +} + +struct Particle { + position: vec2, + velocity: vec2, + color: vec4, +}; + +@group(2) @binding(0) var particles: array; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) color: vec4, +}; + +@vertex +fn vertex(vertex: Vertex) -> VertexOutput { + let particle = particles[vertex.instance_index]; + var out: VertexOutput; + out.clip_position = mesh_position_local_to_clip( + mat4x4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ), + vec4(vertex.position, 1.0) + ) + vec4(particle.position, 0.0, 0.0); + out.color = particle.color; + return out; +} + +@fragment +fn fragment(in: VertexOutput) -> @location(0) vec4 { + return in.color; +} \ No newline at end of file diff --git a/examples/assets/shaders/particle_sdf_compute.wgsl b/examples/assets/shaders/particle_sdf_compute.wgsl new file mode 100644 index 000000000..e8e9861fc --- /dev/null +++ b/examples/assets/shaders/particle_sdf_compute.wgsl @@ -0,0 +1,216 @@ +struct Particle { + position: vec2, + original_position: vec2, + velocity: vec2, + energy: f32, + color: vec4, +}; + +struct DrawIndirectArgs { + index_count: u32, + instance_count: atomic, + first_index: u32, + base_vertex: i32, + first_instance: u32, +} + +@group(0) @binding(0) var particles: array; +@group(0) @binding(1) var sphere_center: vec4; +@group(0) @binding(2) var sphere_radius: f32; +@group(0) @binding(3) var scaling_factor: u32; +@group(0) @binding(4) var resolution: vec2; +@group(0) @binding(5) var indirect_args: array; + +fn sdf_sphere(point: vec3, center: vec3, radius: f32) -> f32 { + return length(point - center) - radius; +} + +fn get_ray_direction(uv: vec2) -> vec3 { + let aspect = f32(resolution.x) / f32(resolution.y); + return normalize(vec3((uv.x * 2.0 - 1.0) * aspect, (uv.y * 2.0 - 1.0), -1.0)); +} + +fn raymarch(ro: vec3, rd: vec3, max_steps: u32, max_dist: f32) -> f32 { + var total_distance = 0.0; + for (var i: u32 = 0u; i < max_steps; i++) { + let p = ro + total_distance * rd; + let distance = sdf_sphere(p, sphere_center.xyz, sphere_radius); + if distance < 0.001 { + return total_distance; + } + total_distance += distance; + if total_distance > max_dist { + break; + } + } + return -1.0; +} + +fn closest_point_on_sphere(point: vec2) -> vec3 { + let aspect = f32(resolution.x) / f32(resolution.y); + // Adjust for the new coordinate system + let point3d = vec3(point.x * aspect, point.y, 0.0); + let to_sphere = normalize(point3d - sphere_center.xyz); + let surface_point = sphere_center.xyz + to_sphere * sphere_radius; + return vec3(surface_point.x / surface_point.z / aspect, surface_point.y / surface_point.z, surface_point.z); +} + +@compute @workgroup_size(64) +fn update(@builtin(global_invocation_id) global_id: vec3) { + let pixels = resolution.x * resolution.y; + let particle_count = pixels / scaling_factor; + + /// Update our indirect params if we are thread 0 + if (global_id.x == 0u && global_id.y == 0u && global_id.z == 0u) { + atomicStore(&indirect_args[0].instance_count, particle_count); + } + + let index = global_id.x; + if (index >= particle_count) { + return; + } + + var particle = particles[index]; + + // Convert particle position to UV coordinates + let uv = (particle.original_position + 1.0) * 0.5; + + // Get ray direction for this particle + let ray_direction = get_ray_direction(uv); + + // Set camera position + let camera_position = vec3(0.0, 0.0, 2.0); + + // Perform raymarching + let hit_distance = raymarch(camera_position, ray_direction, 100u, 3.0); + + let max_speed = 0.01; + let acceleration = 0.0005; + let damping = 0.95; // Damping factor to reduce oscillations + + let sphere_point = closest_point_on_sphere(particle.original_position); + let target_position = sphere_point.xy; + + if (hit_distance > 0.0) { + let to_target = target_position - particle.position; + let distance_to_target = length(to_target); + + if (distance_to_target > 0.001) { + let direction = normalize(to_target); + particle.velocity += direction * acceleration; + + // Apply damping + particle.velocity *= damping; + + // Cap speed + let speed = length(particle.velocity); + if (speed > max_speed) { + particle.velocity = normalize(particle.velocity) * max_speed; + } + } else { + // Very close to target, stop movement + particle.velocity = vec2(0.0, 0.0); + particle.position = target_position; + } + + particle.energy = min(particle.energy + 0.1, 1.0); + } else { + // The ray didn't hit, move back to the original position + let to_original = particle.original_position - particle.position; + let distance_to_original = length(to_original); + + if (distance_to_original > 0.001) { + let direction = normalize(to_original); + particle.velocity += direction * acceleration; + + // Apply damping + particle.velocity *= damping; + + // Cap speed + let speed = length(particle.velocity); + if (speed > max_speed) { + particle.velocity = normalize(particle.velocity) * max_speed; + } + } else { + // Very close to original position, stop movement + particle.velocity = vec2(0.0, 0.0); + particle.position = particle.original_position; + } + + particle.energy = max(particle.energy - 0.05, 0.0); + } + + // Update position + particle.position += particle.velocity; + + // Calculate depth based on current particle position relative to sphere center + let aspect = f32(resolution.x) / f32(resolution.y); + let p = closest_point_on_sphere(particle.position); + let particle_3d = vec3(particle.position.x * aspect, particle.position.y, p.z); + + // Calculate the vector from sphere center to the particle on the sphere surface + let to_particle = particle_3d - sphere_center.xyz; + + // Calculate the view direction (assuming camera is looking along negative z-axis) + let view_direction = vec3(0.0, 0.0, -1.0); + + // Calculate the angle between to_particle and view_direction + let cos_angle = dot(normalize(to_particle), -view_direction); + + // Calculate depth based on this angle + // This will be 1 at the center (cos_angle = 1) and 0 at the edges (cos_angle = 0) + let depth = cos_angle; + + // Optionally, apply a non-linear function to emphasize the effect + let emphasized_depth = pow(depth, 2.0); // Adjust power as needed + + // Update color based on energy and depth + let unenergized_color = vec3(0.1, 0.1, 0.1); + let energized_color = vec3(emphasized_depth, 0.0, 0.0); + let color = mix(unenergized_color, energized_color, particle.energy); + particle.color = vec4(color, 1.0); + + particles[index] = particle; +} + + +@compute @workgroup_size(64) +fn init(@builtin(global_invocation_id) global_id: vec3) { + let pixels = resolution.x * resolution.y; + let particle_count = pixels / scaling_factor; + + /// Update our indirect params if we are thread 0 + if (global_id.x == 0u && global_id.y == 0u && global_id.z == 0u) { + atomicStore(&indirect_args[0].instance_count, particle_count); + } + + let index = global_id.x; + if (index >= particle_count) { + return; + } + + var particle: Particle; + + // Calculate aspect ratio + let aspect_ratio = f32(resolution.x) / f32(resolution.y); + + // Determine grid dimensions + let grid_width = sqrt(f32(particle_count) * aspect_ratio); + let grid_height = f32(particle_count) / grid_width; + + // Calculate particle position in grid + let grid_x = f32(index % u32(grid_width)) / (grid_width - 1.0); + let grid_y = floor(f32(index) / grid_width) / (grid_height - 1.0); + + // Map grid position to screen space + let screen_x = grid_x * 2.0 - 1.0; + let screen_y = grid_y * 2.0 - 1.0; + + particle.position = vec2(screen_x, screen_y); + particle.original_position = particle.position; + particle.energy = 0.0; + particle.velocity = vec2(0.0, 0.0); + particle.color = vec4(0.1, 0.1, 0.1, 1.0); + + particles[index] = particle; +} \ No newline at end of file diff --git a/examples/assets/shaders/particle_sdf_material.wgsl b/examples/assets/shaders/particle_sdf_material.wgsl new file mode 100644 index 000000000..2e9245674 --- /dev/null +++ b/examples/assets/shaders/particle_sdf_material.wgsl @@ -0,0 +1,42 @@ +#import bevy_pbr::{ + forward_io::{Vertex} + mesh_functions::{get_world_from_local, mesh_position_local_to_clip, mesh_position_local_to_world} + view_transformations::{position_world_to_clip} +} + +struct Particle { + position: vec2, + original_position: vec2, + velocity: vec2, + energy: f32, + color: vec4, +} + +@group(2) @binding(0) var particles: array; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) color: vec4, +}; + +@vertex +fn vertex(vertex: Vertex) -> VertexOutput { + let particle = particles[vertex.instance_index]; + var out: VertexOutput; + out.clip_position = mesh_position_local_to_clip( + mat4x4( + vec4(1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ), + vec4(vertex.position, 1.0) + ) + vec4(particle.position, 0.0, 0.0); + out.color = particle.color; + return out; +} + +@fragment +fn fragment(in: VertexOutput) -> @location(0) vec4 { + return in.color; +} \ No newline at end of file diff --git a/examples/compute/game_of_life.rs b/examples/compute/game_of_life.rs new file mode 100644 index 000000000..c9e2ea536 --- /dev/null +++ b/examples/compute/game_of_life.rs @@ -0,0 +1,129 @@ +mod particle_mouse; + +use nannou::prelude::bevy_render::texture::{ImageSampler, ImageSamplerDescriptor}; +use nannou::prelude::*; + +const DISPLAY_FACTOR: u32 = 4; +const SIZE: (u32, u32) = (1280 / DISPLAY_FACTOR, 720 / DISPLAY_FACTOR); +const WORKGROUP_SIZE: u32 = 8; + +fn main() { + nannou::app(model) + .update(update) + .compute(compute) + .view(view) + .run() +} + +struct Model { + texture_a: Handle, + texture_b: Handle, + display: Handle, +} + +#[derive(Eq, PartialEq, Hash, Clone, Default)] +enum State { + #[default] + Init, + Update(usize), +} + +#[derive(AsBindGroup, Clone)] +struct ComputeModel { + #[storage_texture(0, image_format = R32Float, access = ReadOnly)] + texture_read: Handle, + #[storage_texture(1, image_format = R32Float, access = WriteOnly)] + texture_write: Handle, +} + +impl Compute for ComputeModel { + type State = State; + + fn shader() -> ShaderRef { + "shaders/game_of_life.wgsl".into() + } + + fn entry(state: &Self::State) -> &'static str { + match state { + State::Init => "init", + State::Update(_) => "update", + } + } + + fn dispatch_size(state: &Self::State) -> (u32, u32, u32) { + (SIZE.0 / WORKGROUP_SIZE, SIZE.1 / WORKGROUP_SIZE, 1) + } +} + +fn model(app: &App) -> Model { + let _window = app + .new_window::() + .primary() + .size(SIZE.0 * DISPLAY_FACTOR, SIZE.1 * DISPLAY_FACTOR) + .build(); + + let mut image = Image::new_fill( + Extent3d { + width: SIZE.0, + height: SIZE.1, + depth_or_array_layers: 1, + }, + TextureDimension::D2, + &[0, 0, 0, 0], + TextureFormat::R32Float, + RenderAssetUsages::RENDER_WORLD, + ); + image.texture_descriptor.usage = + TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING; + image.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor::nearest()); + let image0 = app.assets_mut().add(image.clone()); + let image1 = app.assets_mut().add(image); + Model { + texture_a: image0.clone(), + texture_b: image1, + display: image0.clone(), + } +} + +fn update(app: &App, model: &mut Model) { + if model.display == model.texture_a { + model.display = model.texture_b.clone(); + } else { + model.display = model.texture_b.clone(); + } +} + +fn compute(app: &App, model: &Model, state: State, view: Entity) -> (State, ComputeModel) { + match state { + State::Init => ( + State::Update(1), + ComputeModel { + texture_read: model.texture_a.clone(), + texture_write: model.texture_b.clone(), + }, + ), + State::Update(0) => ( + State::Update(1), + ComputeModel { + texture_read: model.texture_a.clone(), + texture_write: model.texture_b.clone(), + }, + ), + State::Update(1) => ( + State::Update(0), + ComputeModel { + texture_read: model.texture_b.clone(), + texture_write: model.texture_a.clone(), + }, + ), + State::Update(_) => panic!("Invalid state"), + } +} + +fn view(app: &App, model: &Model, view: Entity) { + let draw = app.draw(); + let window_rect = app.window_rect(); + draw.rect() + .w_h(window_rect.w(), window_rect.h()) + .texture(&model.display); +} diff --git a/examples/compute/particle_mouse.rs b/examples/compute/particle_mouse.rs new file mode 100644 index 000000000..986c33a74 --- /dev/null +++ b/examples/compute/particle_mouse.rs @@ -0,0 +1,198 @@ +use nannou::prelude::bevy_render::renderer::RenderDevice; +use nannou::prelude::bevy_render::storage::ShaderStorageBuffer; +use nannou::prelude::*; +use std::sync::Arc; + +const NUM_PARTICLES: u32 = 100000; +const WORKGROUP_SIZE: u32 = 64; + +fn main() { + nannou::app(model) + .compute(compute) + .update(update) + .shader_model::() + .run(); +} + +pub enum Shape { + Circle, + Square, + Triangle, +} + +struct Model { + particles: Handle, + shape: Shape, + attract_strength: f32, +} + +impl Model { + fn material(&self) -> ShaderModel { + ShaderModel { + particles: self.particles.clone(), + } + } +} + +#[repr(C)] +#[derive(ShaderType, Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] +struct Particle { + position: Vec2, + velocity: Vec2, + color: Vec4, +} + +#[derive(Default, Debug, Eq, PartialEq, Hash, Clone)] +enum State { + #[default] + Init, + Update, +} + +#[derive(AsBindGroup, Clone)] +struct ComputeModel { + #[storage(0, visibility(compute))] + particles: Handle, + #[uniform(1)] + mouse: Vec2, + #[uniform(2)] + attract_strength: f32, + #[uniform(3)] + particle_count: u32, +} + +impl Compute for ComputeModel { + type State = State; + + fn shader() -> ShaderRef { + "shaders/particle_mouse_compute.wgsl".into() + } + + fn entry(state: &Self::State) -> &'static str { + match state { + State::Init => "init", + State::Update => "update", + } + } + + fn dispatch_size(_state: &Self::State) -> (u32, u32, u32) { + ((NUM_PARTICLES + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE, 1, 1) + } +} + +#[shader_model( + fragment = "shaders/particle_mouse_material.wgsl", + vertex = "shaders/particle_mouse_material.wgsl" +)] +struct ShaderModel { + #[storage(0, read_only, visibility(vertex))] + particles: Handle, +} + +fn model(app: &App) -> Model { + let _window_id = app + .new_window() + .primary() + .size(1024, 768) + .view(view) + .build(); + + // Create a buffer to store the particles. + let particle_size = Particle::min_size().get() as usize; + let mut particles = ShaderStorageBuffer::with_size( + NUM_PARTICLES as usize * particle_size * 2, + RenderAssetUsages::RENDER_WORLD, + ); + particles.buffer_description.label = Some("particles"); + particles.buffer_description.usage |= BufferUsages::STORAGE | BufferUsages::VERTEX; + + let particles = app.assets_mut().add(particles); + + Model { + particles, + shape: Shape::Circle, + attract_strength: 1.0, + } +} + +fn update(app: &App, model: &mut Model) { + if app.keys().just_pressed(KeyCode::ArrowLeft) { + match model.shape { + Shape::Circle => model.shape = Shape::Square, + Shape::Square => model.shape = Shape::Triangle, + Shape::Triangle => model.shape = Shape::Circle, + } + } + if app.keys().just_pressed(KeyCode::ArrowRight) { + match model.shape { + Shape::Circle => model.shape = Shape::Triangle, + Shape::Square => model.shape = Shape::Circle, + Shape::Triangle => model.shape = Shape::Square, + } + } + if app.keys().just_pressed(KeyCode::ArrowUp) { + model.attract_strength += 1.0; + } + if app.keys().just_pressed(KeyCode::ArrowDown) { + model.attract_strength -= 1.0; + } +} + +fn compute(app: &App, model: &Model, state: State, view: Entity) -> (State, ComputeModel) { + let window = app.main_window(); + let window_rect = window.rect(); + + let mouse_pos = app.mouse(); + let mouse_norm = Vec2::new( + mouse_pos.x / window_rect.w() * 2.0, + mouse_pos.y / window_rect.h() * 2.0, + ); + + let compute_model = ComputeModel { + particles: model.particles.clone(), + mouse: mouse_norm, + attract_strength: model.attract_strength, + particle_count: NUM_PARTICLES, + }; + + match state { + State::Init => (State::Update, compute_model), + State::Update => (State::Update, compute_model), + } +} + +fn view(app: &App, model: &Model) { + let draw = app.draw(); + draw.background().color(GRAY); + + let draw = draw.material(model.material()); + match model.shape { + Shape::Circle => { + draw_particles_circle(&draw); + } + Shape::Square => { + draw_particles_square(&draw); + } + Shape::Triangle => { + draw_particles_triangle(&draw); + } + } +} + +fn draw_particles_circle(draw: &Draw) { + draw.instanced() + .primitive(draw.ellipse().w_h(5.0, 5.0)) + .range(0..NUM_PARTICLES); +} + +fn draw_particles_square(draw: &Draw) { + draw.instanced() + .primitive(draw.rect().w_h(5.0, 5.0)) + .range(0..NUM_PARTICLES); +} + +fn draw_particles_triangle(draw: &Draw) { + draw.instanced() + .primitive(draw.tri().w_h(5.0, 5.0)) + .range(0..NUM_PARTICLES); +} diff --git a/examples/compute/particle_sdf.rs b/examples/compute/particle_sdf.rs new file mode 100644 index 000000000..72cbc9427 --- /dev/null +++ b/examples/compute/particle_sdf.rs @@ -0,0 +1,265 @@ +use nannou::prelude::bevy_render::renderer::RenderDevice; +use nannou::prelude::bevy_render::storage::ShaderStorageBuffer; +use nannou::prelude::*; +use std::sync::Arc; + +const WORKGROUP_SIZE: u32 = 64; + +fn main() { + nannou::app(model) + .compute(compute) + .update(update) + .shader_model::() + .run(); +} + +pub enum Shape { + Circle, + Square, + Triangle, +} + +struct Model { + particles: Handle, + indirect_params: Handle, + circle_radius: f32, + size: u32, + buffer_size: u32, + scaling_factor: u32, +} + +impl Model { + fn material(&self) -> ShaderModel { + ShaderModel { + particles: self.particles.clone(), + } + } +} + +// This struct isn't used on the CPU side, but it's necessary to define the layout of the data +// to correctly size the buffer on the GPU side. +#[repr(C)] +#[derive(ShaderType)] +struct Particle { + position: Vec2, + original_position: Vec2, + velocity: Vec2, + energy: f32, + _pad: f32, + color: Vec4, +} + +// The draw indirect args struct is used to pass the number of instances to draw to the GPU +// The compute shader will write the number of particles to draw to `instance_count` +#[repr(C)] +#[derive(ShaderType)] +struct DrawIndirectArgs { + pub index_count: u32, + pub instance_count: u32, + pub first_index: u32, + pub base_vertex: i32, + pub first_instance: u32, +} + +// Our compute shader has two states, `Init` and `Update` +// The `Init` state is used to initialize the particles +// The `Update` state is used to run the simulation +#[derive(Debug, Eq, PartialEq, Hash, Clone)] +enum State { + Init(u32), + Update(u32), +} + +impl Default for State { + fn default() -> Self { + State::Init(1) + } +} + +// Compute model, defining uniforms and storage buffers that will be passed to the compute shader +#[derive(AsBindGroup, Clone)] +struct ComputeModel { + #[storage(0, visibility(compute))] + particles: Handle, + #[uniform(1)] + circle_center: Vec4, + #[uniform(2)] + circle_radius: f32, + #[uniform(3)] + scaling_factor: u32, + #[uniform(4)] + resolution: UVec2, + #[storage(5, visibility(compute))] + indirect_params: Handle, +} + +impl Compute for ComputeModel { + type State = State; + + fn shader() -> ShaderRef { + "shaders/particle_sdf_compute.wgsl".into() + } + + fn entry(state: &Self::State) -> &'static str { + match state { + State::Init(_) => "init", + State::Update(_) => "update", + } + } + + fn dispatch_size(state: &Self::State) -> (u32, u32, u32) { + let size = match state { + State::Init(size) => size, + State::Update(size) => size, + }; + + ((size + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE, 1, 1) + } +} + +// Our shader model that will be used to render the particles +#[shader_model( + fragment = "shaders/particle_sdf_material.wgsl", + vertex = "shaders/particle_sdf_material.wgsl" +)] +struct ShaderModel { + #[storage(0, read_only, visibility(vertex))] + particles: Handle, +} + +fn model(app: &App) -> Model { + let _window_id = app + .new_window() + .primary() + .size(1024, 768) + .view(view) + .build(); + + // Create a buffer to store the particles. + let size = 1; // This will be updated in the `update` function + let particles = create_particle_buffer(app, size); + + // Create a buffer store our indirect draw params + let indirect_params = create_indirect_params_buffer(app, size); + + Model { + particles, + indirect_params, + circle_radius: 0.5, + size, + buffer_size: size, + scaling_factor: 50, + } +} + +// Controls: +// - Arrow keys to change the circle radius +// - Left and right arrow keys to change the scaling factor (i.e. density) +fn update(app: &App, model: &mut Model) { + if app.keys().pressed(KeyCode::ArrowUp) { + model.circle_radius += 0.01; + } + if app.keys().pressed(KeyCode::ArrowDown) { + model.circle_radius -= 0.01; + } + if app.keys().pressed(KeyCode::ArrowRight) { + model.scaling_factor += 1; + } + if app.keys().pressed(KeyCode::ArrowLeft) { + // Don't let the scaling factor go below 20 + model.scaling_factor = model.scaling_factor.saturating_sub(1).max(20); + } + + let pixels = app.main_window().size_pixels().element_product(); + let new_size = pixels / model.scaling_factor.clamp(20, 1000); + + // Resize the buffer if necessary + if new_size > model.buffer_size { + model.particles = create_particle_buffer(app, new_size); + model.buffer_size = new_size; + } + + model.size = new_size; +} + +fn compute( + app: &App, + model: &Model, + previous_state: State, + _view: Entity, +) -> (State, ComputeModel) { + let window = app.main_window(); + let window_rect = window.rect(); + + let mouse_pos = app.mouse(); + let mouse_norm = Vec2::new( + mouse_pos.x / window_rect.w() * 2.0, + mouse_pos.y / window_rect.h() * 2.0, + ); + + let compute_model = ComputeModel { + particles: model.particles.clone(), + circle_center: mouse_norm.extend(1.0).extend(0.0), + circle_radius: model.circle_radius, + scaling_factor: model.scaling_factor, + resolution: window.size_pixels(), + indirect_params: model.indirect_params.clone(), + }; + + // If the size has changed, we need to re-initialize the particles + // Otherwise, we can just update them + match previous_state { + State::Init(size) => { + // Even if we are in the `Init` state, we still need to update the size of the buffer + // in case the window has been resized + if size != model.size { + (State::Init(model.size), compute_model) + } else { + (State::Update(model.size), compute_model) + } + } + State::Update(size) => { + if size != model.size { + (State::Init(model.size), compute_model) + } else { + (State::Update(model.size), compute_model) + } + } + } +} + +fn view(app: &App, model: &Model) { + let draw = app.draw(); + draw.background().color(GRAY); + + let draw = draw.material(model.material()); + draw.indirect() + .primitive(draw.rect().w_h(2.0, 2.0)) + .buffer(model.indirect_params.clone()); +} + +fn create_particle_buffer(app: &App, size: u32) -> Handle { + let particle_size = Particle::min_size().get() as usize; + let mut particles = ShaderStorageBuffer::with_size( + size as usize * particle_size * 2, + RenderAssetUsages::RENDER_WORLD, + ); + particles.buffer_description.label = Some("particles"); + particles.buffer_description.usage |= BufferUsages::STORAGE | BufferUsages::VERTEX; + let particles = app.assets_mut().add(particles); + particles +} + +fn create_indirect_params_buffer(app: &App, size: u32) -> Handle { + let mut indirect_params = ShaderStorageBuffer::from(DrawIndirectArgs { + index_count: 6, // Hardcoded for now, 2 triangles + instance_count: size, + first_index: 0, + base_vertex: 0, + first_instance: 0, + }); + indirect_params.buffer_description.label = Some("indirect_params"); + indirect_params.buffer_description.usage |= BufferUsages::STORAGE | BufferUsages::INDIRECT; + let indirect_params = app.assets_mut().add(indirect_params); + indirect_params +} diff --git a/guide/.dropboxignore b/guide/.dropboxignore deleted file mode 100644 index d8df5e971..000000000 --- a/guide/.dropboxignore +++ /dev/null @@ -1,5 +0,0 @@ -# ---- -# Automatically Generated .dropboxignore file at Tue Apr 23 13:35:37 PDT 2024 -# ---- -book -# ---- diff --git a/guide/book_tests/.dropboxignore b/guide/book_tests/.dropboxignore deleted file mode 100644 index 32026499e..000000000 --- a/guide/book_tests/.dropboxignore +++ /dev/null @@ -1,7 +0,0 @@ -# ---- -# Automatically Generated .dropboxignore file at Tue Apr 23 13:35:37 PDT 2024 -# ---- -target/ -Cargo.lock -libtest.rmeta -# ---- diff --git a/nannou/src/app.rs b/nannou/src/app.rs index cf9f22c4c..44539a48a 100644 --- a/nannou/src/app.rs +++ b/nannou/src/app.rs @@ -7,15 +7,6 @@ //! - [**Proxy**](./struct.Proxy.html) - a handle to an **App** that may be used from a non-main //! thread. //! - [**LoopMode**](./enum.LoopMode.html) - describes the behaviour of the application event loop. -use std::any::Any; -use std::cell::{RefCell, RefMut}; -use std::hash::Hash; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::Duration; -use std::{self}; - use bevy::app::AppExit; #[cfg(not(target_arch = "wasm32"))] use bevy::asset::io::file::FileAssetReader; @@ -29,7 +20,7 @@ use bevy::input::ButtonState; use bevy::prelude::*; use bevy::reflect::{ ApplyError, DynamicTypePath, GetTypeRegistration, ReflectMut, ReflectOwned, ReflectRef, - TypeInfo, + TypeInfo, Typed, }; use bevy::render::extract_resource::ExtractResource; use bevy::window::{ @@ -45,22 +36,44 @@ use bevy_inspector_egui::quick::ResourceInspectorPlugin; use bevy_inspector_egui::quick::WorldInspectorPlugin; #[cfg(feature = "egui")] use bevy_inspector_egui::DefaultInspectorConfigPlugin; -use find_folder; - -use bevy_nannou::prelude::{draw, DrawHolder}; -use bevy_nannou::NannouPlugin; use crate::frame::{Frame, FramePlugin}; use crate::prelude::bevy_ecs::system::SystemState; -use crate::prelude::render::{NannouMesh, ShaderModel}; -use crate::prelude::NannouMaterialPlugin; -use crate::render::{RenderApp, RenderPlugin}; +use crate::prelude::bevy_reflect::DynamicTyped; +use crate::prelude::render::{NannouMaterialPlugin, NannouMesh, ShaderModel}; +use crate::prelude::NannouShaderModelPlugin; +use crate::render::{ + compute::{Compute, ComputeModel, ComputePlugin, ComputeShaderHandle, ComputeState}, + NannouRenderNode, RenderApp, RenderPlugin, +}; use crate::window::WindowUserFunctions; use crate::{camera, geom, light, window}; +use bevy_nannou::prelude::render::NannouCamera; +use bevy_nannou::prelude::{draw, DrawHolder}; +use bevy_nannou::NannouPlugin; +use find_folder; +use std::any::Any; +use std::cell::{RefCell, RefMut}; +use std::hash::Hash; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; +use std::{self}; /// The user function type for initialising their model. pub type ModelFn = fn(&App) -> Model; +/// The user function type for producing the compute model post-update. +pub type ComputeUpdateFn = + fn( + &App, + &Model, + ::State, + Entity, + ) -> (::State, ComputeModel); + /// The user function type for updating their model in accordance with some event. pub type EventFn = fn(&App, &mut Model, &Event); @@ -154,6 +167,9 @@ struct EventFnRes(Option>); #[derive(Resource, Deref, DerefMut)] struct UpdateFnRes(Option>); +#[derive(Resource, Deref, DerefMut)] +struct ComputeUpdateFnRes(ComputeUpdateFn); + #[derive(Resource, Deref, DerefMut)] pub(crate) struct RenderFnRes(Option>); @@ -239,7 +255,7 @@ where DefaultPlugins.set(WindowPlugin { // Don't spawn a window by default, we'll handle this ourselves primary_window: None, - exit_condition: ExitCondition::DontExit, + exit_condition: ExitCondition::OnAllClosed, ..default() }), #[cfg(feature = "egui")] @@ -336,12 +352,46 @@ where self } - pub fn shader_model(mut self) -> Self + pub fn shader_model(mut self) -> Self where - T: ShaderModel, - T::Data: PartialEq + Eq + Hash + Clone, + SM: ShaderModel, + SM::Data: PartialEq + Eq + Hash + Clone, { - self.app.add_plugins(NannouMaterialPlugin::::default()); + self.app.add_plugins(( + NannouMaterialPlugin::::default(), + NannouShaderModelPlugin::::default(), + )); + self + } + + /// Load a fragment shader asset from the given path for use with the nannou `Draw` API. + #[cfg(feature = "nightly")] + pub fn init_fragment_shader(mut self) -> Self { + self.app + .add_plugins(NannouShaderModelPlugin::>::default()); + self + } + + pub fn compute(mut self, compute_fn: ComputeUpdateFn) -> Self { + let render_app = self.app.sub_app_mut(bevy::render::RenderApp); + render_app.insert_resource(ComputeShaderHandle(CM::shader())); + self.app + .add_systems( + First, + |mut commands: Commands, views_q: Query>| { + for view in views_q.iter() { + info!("Adding compute state to view {:?}", view); + commands.entity(view).insert(ComputeState { + current: CM::State::default(), + next: None, + }); + } + }, + ) + .insert_resource(ComputeUpdateFnRes(compute_fn)) + .add_systems(Update, compute::.after(update::)) + .add_systems(Last, |query: Query<&ComputeModel>| {}) + .add_plugins(ComputePlugin::::default()); self } @@ -571,6 +621,15 @@ where } } +impl DynamicTyped for ModelHolder +where + M: 'static + Any + DynamicTypePath + GetTypeRegistration + Reflect, +{ + fn reflect_type_info(&self) -> &'static TypeInfo { + self.0.reflect_type_info() + } +} + impl Reflect for ModelHolder where M: Reflect + DynamicTypePath + Any + GetTypeRegistration + 'static, @@ -1118,6 +1177,39 @@ fn update( *ticks += 1; } +fn compute( + world: &mut World, + state: &mut SystemState<( + ResMut>, + Res>, + Query<(Entity, &mut ComputeState)>, + )>, +) where + M: 'static + Send + Sync, + CM: Compute, +{ + let (mut app, (mut model, compute, mut views_q)) = get_app_and_state(world, state); + let compute = compute.0; + for (view, mut state) in views_q.iter_mut() { + let (new_state, compute_model) = compute(&app, &model, state.current.clone(), view); + unsafe { + app.component_world + .borrow_mut() + .world_mut() + .entity_mut(view) + .insert(ComputeState { + current: state.current.clone(), + next: if new_state != state.current { + Some(new_state) + } else { + None + }, + }) + .insert(ComputeModel(compute_model)); + } + } +} + fn events( world: &mut World, state: &mut SystemState<( diff --git a/nannou/src/camera.rs b/nannou/src/camera.rs index 1aabc924c..3314f1d4f 100644 --- a/nannou/src/camera.rs +++ b/nannou/src/camera.rs @@ -134,9 +134,9 @@ pub trait SetCamera: Sized { }) } - fn bloom_prefilter_settings(self, threshold: f32, threshold_softness: f32) -> Self { + fn bloom_prefilter(self, threshold: f32, threshold_softness: f32) -> Self { self.map_bloom_settings(|mut settings| { - settings.prefilter_settings = bevy::core_pipeline::bloom::BloomPrefilterSettings { + settings.prefilter = bevy::core_pipeline::bloom::BloomPrefilter { threshold, threshold_softness, }; diff --git a/nannou/src/prelude.rs b/nannou/src/prelude.rs index 25378492c..0c08121b0 100644 --- a/nannou/src/prelude.rs +++ b/nannou/src/prelude.rs @@ -11,6 +11,7 @@ pub use bevy::tasks::{futures_lite::future, Task}; pub use bevy_egui::egui; pub use crate::frame::*; +pub use crate::render::compute::*; pub use crate::render::*; pub use crate::wgpu; pub use crate::wgpu::util::{BufferInitDescriptor, DeviceExt}; diff --git a/nannou/src/render/compute.rs b/nannou/src/render/compute.rs new file mode 100644 index 000000000..e0673a126 --- /dev/null +++ b/nannou/src/render/compute.rs @@ -0,0 +1,312 @@ +use crate::app::{ModelHolder, RenderFnRes}; +use crate::frame::Frame; +use crate::prelude::bevy_render::extract_component::ExtractComponent; +use crate::prelude::bevy_render::extract_resource::extract_resource; +use crate::prelude::bevy_render::{Extract, MainWorld}; +use bevy::core_pipeline::core_3d::graph::{Core3d, Node3d}; +use bevy::ecs::entity::{EntityHash, EntityHashMap}; +use bevy::ecs::query::QueryItem; +use bevy::ecs::system::StaticSystemParam; +use bevy::prelude::*; +use bevy::render::extract_component::ExtractComponentPlugin; +use bevy::render::render_graph::{ + NodeRunError, RenderGraphApp, RenderGraphContext, RenderLabel, ViewNode, ViewNodeRunner, +}; +use bevy::render::render_resource::{ + BindGroup, BindGroupLayout, BindGroupLayoutEntries, CachedComputePipelineId, + ComputePipelineDescriptor, PipelineCache, ShaderRef, SpecializedComputePipeline, + SpecializedComputePipelines, +}; +use bevy::render::renderer::{RenderContext, RenderDevice}; +use bevy::render::texture::{FallbackImage, GpuImage}; +use bevy::render::view::{ExtractedView, ExtractedWindows, ViewTarget}; +use bevy::render::{Render, RenderSet}; +use bevy::utils::HashMap; +use bevy_nannou::prelude::{ + AsBindGroup, CachedPipelineState, ShaderStages, StorageTextureAccess, TextureFormat, +}; +use noise::NoiseFn; +use std::borrow::Cow; +use std::hash::Hash; +use std::ops::Deref; +use std::sync::atomic::AtomicBool; +use std::sync::mpsc::{Receiver, Sender}; +use std::sync::Arc; +use wgpu::ComputePassDescriptor; + +pub(crate) struct ComputePlugin(std::marker::PhantomData); + +impl Default for ComputePlugin { + fn default() -> Self { + Self(std::marker::PhantomData) + } +} + +impl Plugin for ComputePlugin +where + CM: Compute, +{ + fn build(&self, app: &mut App) { + app.add_plugins(( + ExtractComponentPlugin::>::default(), + ExtractComponentPlugin::>::default(), + )); + + let Some(render_app) = app.get_sub_app_mut(bevy::render::RenderApp) else { + return; + }; + + render_app + .add_systems(ExtractSchedule, sync_pipeline_cache::) + .add_systems( + Render, + ( + queue_pipeline::.in_set(RenderSet::Queue), + prepare_bind_group::.in_set(RenderSet::PrepareBindGroups), + ), + ); + } + + fn finish(&self, app: &mut App) { + let Some(render_app) = app.get_sub_app_mut(bevy::render::RenderApp) else { + return; + }; + + render_app + .insert_resource(ComputePipelineIds::(HashMap::default())) + .init_resource::>() + .init_resource::>>() + .add_render_graph_node::>>( + Core3d, + NannouComputeNodeLabel, + ) + .add_render_graph_edges( + Core3d, + ( + Node3d::EndPrepasses, + NannouComputeNodeLabel, + Node3d::StartMainPass, + ), + ); + } +} + +fn sync_pipeline_cache( + mut main_world: ResMut, + pipelines: Res, + pipeline_ids: Res>, +) where + CM: Compute, +{ + let mut states_q = main_world.query::<&mut ComputeState>(); + for mut state in states_q.iter_mut(&mut main_world) { + if let Some(next_state) = &state.next { + if let Some(id) = pipeline_ids.get(next_state) { + match pipelines.get_compute_pipeline_state(*id) { + CachedPipelineState::Queued => {} + CachedPipelineState::Creating(_) => {} + CachedPipelineState::Ok(_) => { + state.current = next_state.clone(); + state.next = None; + } + CachedPipelineState::Err(e) => { + error!("Failed to create pipeline {:?}", e); + } + }; + } + } + } +} + +fn queue_pipeline( + pipeline: Res>, + mut pipelines: ResMut>>, + pipeline_cache: Res, + mut pipeline_ids: ResMut>, + views_q: Query<&ComputeState>, +) where + CM: Compute, +{ + for state in views_q.iter() { + if !pipeline_ids.contains_key(&state.current) { + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &pipeline, + NannouComputePipelineKey { + shader_entry: CM::entry(&state.current), + }, + ); + pipeline_ids.insert(state.current.clone(), pipeline_id); + } + + if let Some(next) = &state.next { + if !pipeline_ids.contains_key(next) { + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &pipeline, + NannouComputePipelineKey { + shader_entry: CM::entry(next), + }, + ); + pipeline_ids.insert(next.clone(), pipeline_id); + } + } + } +} + +fn prepare_bind_group( + mut commands: Commands, + pipeline: Res>, + render_device: Res, + views_q: Query<(Entity, &ComputeModel)>, + mut bind_group_param: StaticSystemParam, +) where + CM: Compute, +{ + for (view, compute_model) in views_q.iter() { + let bind_group = compute_model + .0 + .as_bind_group(&pipeline.layout, &render_device, &mut bind_group_param) + .expect("Failed to create bind group"); + commands + .entity(view) + .insert(ComputeBindGroup(bind_group.bind_group)); + } +} + +#[derive(Component, ExtractComponent, Clone, Default)] +pub(crate) struct ComputeState { + pub current: S, + pub next: Option, +} + +#[derive(Resource, Deref, DerefMut)] +struct ComputePipelineIds(HashMap); + +#[derive(Component, ExtractComponent, Clone)] +pub(crate) struct ComputeModel(pub CM); + +#[derive(Component)] +struct ComputeBindGroup(pub BindGroup); + +#[derive(Resource)] +pub struct ComputeShaderHandle(pub ShaderRef); + +#[derive(Resource)] +struct NannouComputePipeline +where + CM: Compute, +{ + shader: Handle, + layout: BindGroupLayout, + _compute_model: std::marker::PhantomData, +} + +impl FromWorld for NannouComputePipeline +where + CM: Compute, +{ + fn from_world(world: &mut World) -> Self { + let render_device = world.resource::(); + let asset_server = world.resource::(); + let shader = world.resource::(); + let shader = match &shader.0 { + ShaderRef::Default => panic!("Default shader not supported"), + ShaderRef::Handle(handle) => handle.clone(), + ShaderRef::Path(path) => asset_server.load(path), + }; + NannouComputePipeline { + shader: shader.clone(), + layout: CM::bind_group_layout(&render_device), + _compute_model: std::marker::PhantomData, + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +struct NannouComputePipelineKey { + pub shader_entry: &'static str, +} + +impl SpecializedComputePipeline for NannouComputePipeline +where + CM: Compute, +{ + type Key = NannouComputePipelineKey; + + fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor { + ComputePipelineDescriptor { + label: Some("NannouComputePipeline".into()), + layout: vec![self.layout.clone()], + push_constant_ranges: Vec::new(), + shader: self.shader.clone(), + shader_defs: vec![], + entry_point: Cow::from(key.shader_entry), + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] +struct NannouComputeNodeLabel; + +struct NannouComputeNode(std::marker::PhantomData); + +impl FromWorld for NannouComputeNode +where + CM: Compute, +{ + fn from_world(_world: &mut World) -> Self { + Self(std::marker::PhantomData) + } +} + +impl ViewNode for NannouComputeNode +where + CM: Compute, +{ + type ViewQuery = (&'static ComputeBindGroup, &'static ComputeState); + + fn run<'w>( + &self, + _graph: &mut RenderGraphContext, + render_context: &mut RenderContext<'w>, + (bind_group, state): QueryItem<'w, Self::ViewQuery>, + world: &'w World, + ) -> Result<(), NodeRunError> { + let pipeline_cache = world.resource::(); + let pipeline_ids = world.resource::>(); + let Some(pipeline_id) = pipeline_ids.get(&state.current) else { + return Ok(()); + }; + let Some(pipeline) = pipeline_cache.get_compute_pipeline(*pipeline_id) else { + return Ok(()); + }; + let mut pass = render_context + .command_encoder() + .begin_compute_pass(&ComputePassDescriptor::default()); + pass.set_bind_group(0, &bind_group.0, &[]); + pass.set_pipeline(pipeline); + let (x, y, z) = CM::dispatch_size(&state.current); + pass.dispatch_workgroups(x, y, z); + Ok(()) + } +} + +pub trait Compute: AsBindGroup + Clone + Send + Sync + 'static { + type State: Default + Eq + PartialEq + Hash + Clone + Send + Sync + 'static; + + /// The shader to use for this compute model. + fn shader() -> ShaderRef; + + /// The entry point for the compute shader as derived from the state. This can be + /// used in combination with state to advance a compute shader through a series of + /// stages. + fn entry(_state: &Self::State) -> &'static str { + "main" + } + + /// The size used to dispatch the compute pass. + fn dispatch_size(_state: &Self::State) -> (u32, u32, u32) { + (1, 1, 1) + } +} diff --git a/nannou/src/render.rs b/nannou/src/render/mod.rs similarity index 89% rename from nannou/src/render.rs rename to nannou/src/render/mod.rs index 0bceb7de9..9cefc5c37 100644 --- a/nannou/src/render.rs +++ b/nannou/src/render/mod.rs @@ -1,17 +1,25 @@ use crate::app::{ModelHolder, RenderFnRes}; use crate::frame::Frame; +use crate::prelude::bevy_render::extract_component::ExtractComponent; use crate::prelude::bevy_render::extract_resource::extract_resource; use bevy::core_pipeline::core_3d::graph::{Core3d, Node3d}; use bevy::ecs::query::QueryItem; -use bevy::prelude::*; +pub use bevy::prelude::*; +use bevy::render::extract_resource::ExtractResource; use bevy::render::render_graph::{ NodeRunError, RenderGraphApp, RenderGraphContext, RenderLabel, ViewNode, ViewNodeRunner, }; +use bevy::render::render_resource::SpecializedComputePipeline; use bevy::render::renderer::RenderContext; use bevy::render::view::{ExtractedView, ExtractedWindows, ViewTarget}; +use bevy_nannou::prelude::AsBindGroup; +use noise::NoiseFn; +use std::hash::Hash; use std::ops::Deref; -pub(crate) struct RenderPlugin(std::marker::PhantomData); +pub mod compute; + +pub struct RenderPlugin(std::marker::PhantomData); impl Default for RenderPlugin where @@ -96,7 +104,7 @@ impl FromWorld for NannouRenderNode where M: Send + Sync + Clone + 'static, { - fn from_world(_world: &mut World) -> Self { + fn from_world(world: &mut World) -> Self { Self(std::marker::PhantomData) } } diff --git a/nannou_audio/.dropboxignore b/nannou_audio/.dropboxignore deleted file mode 100644 index 833029195..000000000 --- a/nannou_audio/.dropboxignore +++ /dev/null @@ -1,16 +0,0 @@ -# ---- -# Automatically Generated .dropboxignore file at Tue Apr 23 13:35:38 PDT 2024 -# ---- -<<<<<<< HEAD -capture_hi_res/ -simple_capture/ -target/ -**/*.rs.bk -Cargo.lock -.DS_Store -======= -/target -**/*.rs.bk -Cargo.lock ->>>>>>> nannou_audio/master -# ---- diff --git a/nannou_audio/.gitignore b/nannou_audio/.gitignore index f1830b29b..dea721d65 100644 --- a/nannou_audio/.gitignore +++ b/nannou_audio/.gitignore @@ -1,12 +1,6 @@ -<<<<<<< HEAD capture_hi_res/ simple_capture/ target/ **/*.rs.bk Cargo.lock -.DS_Store -======= -/target -**/*.rs.bk -Cargo.lock ->>>>>>> nannou_audio/master +.DS_Store \ No newline at end of file diff --git a/nannou_laser/.dropboxignore b/nannou_laser/.dropboxignore deleted file mode 100644 index 308cac8b1..000000000 --- a/nannou_laser/.dropboxignore +++ /dev/null @@ -1,7 +0,0 @@ -# ---- -# Automatically Generated .dropboxignore file at Tue Apr 23 13:35:38 PDT 2024 -# ---- -/target -**/*.rs.bk -Cargo.lock -# ---- diff --git a/nannou_osc/.dropboxignore b/nannou_osc/.dropboxignore deleted file mode 100644 index 308cac8b1..000000000 --- a/nannou_osc/.dropboxignore +++ /dev/null @@ -1,7 +0,0 @@ -# ---- -# Automatically Generated .dropboxignore file at Tue Apr 23 13:35:38 PDT 2024 -# ---- -/target -**/*.rs.bk -Cargo.lock -# ----