├── media ├── docking.gif └── screenshot.png ├── assets └── Textures │ └── example_texture.png ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── rust.yml ├── examples ├── empty.rs ├── minimal.rs ├── minimal-docking.rs ├── docking.rs ├── hello-world.rs ├── hello-world-postupdate.rs ├── custom-texture.rs └── render-to-texture.rs ├── LICENSE-MIT ├── Cargo.toml ├── src ├── imgui_wgpu_rs_local │ ├── README.md │ ├── imgui.wgsl │ └── mod.rs └── lib.rs ├── README.md └── LICENSE-APACHE /media/docking.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrd/bevy_mod_imgui/HEAD/media/docking.gif -------------------------------------------------------------------------------- /media/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrd/bevy_mod_imgui/HEAD/media/screenshot.png -------------------------------------------------------------------------------- /assets/Textures/example_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbrd/bevy_mod_imgui/HEAD/assets/Textures/example_texture.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Ignore Imgui .ini files 13 | *.ini -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /examples/empty.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_mod_imgui::prelude::*; 3 | 4 | #[derive(Resource)] 5 | struct ImguiState; 6 | 7 | fn main() { 8 | let mut app = App::new(); 9 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 10 | .insert_resource(ImguiState {}) 11 | .add_plugins(DefaultPlugins) 12 | .add_plugins(bevy_mod_imgui::ImguiPlugin::default()) 13 | .add_systems(Startup, |mut commands: Commands| { 14 | commands.spawn(Camera3d::default()); 15 | }) 16 | .add_systems(Update, imgui_example_ui); 17 | app.run(); 18 | } 19 | 20 | fn imgui_example_ui(_context: NonSendMut, _state: ResMut) { 21 | // Example to regression test our workaround for https://github.com/Yatekii/imgui-wgpu-rs/issues/114 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Platform (please complete the following information):** 27 | - Device: [e.g. PC, Mac, etc...] 28 | - OS: [e.g. iOS] 29 | - Version: [e.g. 22] 30 | - Rust Version: 31 | - Bevy Version: 32 | - WGPU Renderer Backend: [e.g. DX12, Vulcan, etc] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main", "0.6.0" ] 6 | pull_request: 7 | branches: [ "main", "0.6.0" ] 8 | merge_group: 9 | types: [checks_requested] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: windows-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: dtolnay/rust-toolchain@stable 22 | with: 23 | components: rustfmt, clippy 24 | - name: Build 25 | run: cargo build --verbose --all-targets --all-features 26 | - name: Run tests 27 | run: cargo test --verbose --all-targets --all-features 28 | - name: Run format checks 29 | run: cargo fmt --all -- --check 30 | - name: Run clippy 31 | run: cargo clippy --workspace --all-targets --all-features -- -Dwarnings --no-deps 32 | - name: Run doc 33 | run: cargo doc --no-deps --all-features 34 | -------------------------------------------------------------------------------- /examples/minimal.rs: -------------------------------------------------------------------------------- 1 | // Example that matches the minimal example in the documentation. 2 | use bevy::prelude::*; 3 | use bevy_mod_imgui::prelude::*; 4 | 5 | #[derive(Resource)] 6 | struct ImguiState { 7 | demo_window_open: bool, 8 | } 9 | 10 | fn main() { 11 | let mut app = App::new(); 12 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 13 | .insert_resource(ImguiState { 14 | demo_window_open: true, 15 | }) 16 | .add_plugins(DefaultPlugins) 17 | .add_plugins(bevy_mod_imgui::ImguiPlugin::default()) 18 | .add_systems(Startup, |mut commands: Commands| { 19 | commands.spawn(Camera3d::default()); 20 | }) 21 | .add_systems(Update, imgui_example_ui); 22 | app.run(); 23 | } 24 | 25 | fn imgui_example_ui(mut context: NonSendMut, mut state: ResMut) { 26 | let ui = context.ui(); 27 | if state.demo_window_open { 28 | ui.show_demo_window(&mut state.demo_window_open); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_mod_imgui" 3 | version = "0.8.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | categories = ["game-engines", "graphics", "gui", "rendering"] 7 | description = "A Dear ImGui integration for the Bevy game engine." 8 | readme = "README.md" 9 | repository = "https://github.com/jbrd/bevy_mod_imgui" 10 | rust-version = "1.88.0" 11 | exclude = [".github/", ".gitignore"] 12 | 13 | [features] 14 | default = [] 15 | docking = ["imgui/docking"] 16 | 17 | [dependencies] 18 | imgui = "0.12.0" 19 | wgpu = "26" 20 | 21 | # For imgui_wgpu_rs_local 22 | # imgui-wgpu = "0.24.0" # temporarily bundling imgui-wgpu with this crate 23 | bytemuck = "1" 24 | log = "0.4" 25 | smallvec = "1" 26 | 27 | [dependencies.bevy] 28 | version = "0.17.0" 29 | default-features = false 30 | features = ["bevy_core_pipeline", "bevy_render", "bevy_window"] 31 | 32 | [dev-dependencies.bevy] 33 | version = "0.17.0" 34 | default-features = false 35 | features = ["bevy_asset", "bevy_core_pipeline", "bevy_pbr", "bevy_render", "bevy_window", "bevy_winit", "png", "multi_threaded", "tonemapping_luts", "zstd_rust"] 36 | 37 | [package.metadata.docs.rs] 38 | all-features = true -------------------------------------------------------------------------------- /src/imgui_wgpu_rs_local/README.md: -------------------------------------------------------------------------------- 1 | # Bundled Module - imgui_wgpu_rs 0.24.0 2 | 3 | This software contains portions of code derived from [imgui-wgpu-rs](https://github.com/Yatekii/imgui-wgpu-rs/tree/master). 4 | https://github.com/Yatekii/imgui-wgpu-rs/tree/master 5 | Licensed under the Apache License 6 | 7 | Copyright (c) 2019 Steven Wittens 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/minimal-docking.rs: -------------------------------------------------------------------------------- 1 | // Example that matches the minimal docking example in the documentation. 2 | // In the documentation, the #cfg and not docking main function is stripped for brevity. 3 | #[cfg(feature = "docking")] 4 | use bevy::prelude::*; 5 | 6 | #[cfg(feature = "docking")] 7 | use bevy_mod_imgui::prelude::*; 8 | 9 | #[cfg(not(feature = "docking"))] 10 | fn main() -> Result<(), ()> { 11 | println!("Error: `docking` feature is not enabled. Please compile with `--features docking`"); 12 | Err(()) 13 | } 14 | 15 | #[cfg(feature = "docking")] 16 | fn main() { 17 | let mut app = App::new(); 18 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 19 | .add_plugins(DefaultPlugins) 20 | .add_plugins(bevy_mod_imgui::ImguiPlugin { 21 | ..Default::default() 22 | }) 23 | .add_systems(Startup, |mut commands: Commands| { 24 | commands.spawn(Camera3d::default()); 25 | }) 26 | .add_systems(Update, imgui_example_ui); 27 | app.run(); 28 | } 29 | 30 | #[cfg(feature = "docking")] 31 | fn imgui_example_ui(mut context: NonSendMut) { 32 | let ui = context.ui(); 33 | ui.dockspace_over_main_viewport(); 34 | let window = ui.window("Drag me"); 35 | window 36 | .size([300.0, 100.0], imgui::Condition::FirstUseEver) 37 | .position([0.0, 0.0], imgui::Condition::FirstUseEver) 38 | .build(|| { 39 | ui.text("Drag the window title-bar to dock it!"); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /examples/docking.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[cfg(feature = "docking")] 4 | use bevy_mod_imgui::prelude::*; 5 | 6 | #[cfg(not(feature = "docking"))] 7 | fn main() -> Result<(), ()> { 8 | println!("Error: `docking` feature is not enabled. Please compile with `--features docking`"); 9 | Err(()) 10 | } 11 | 12 | #[cfg(feature = "docking")] 13 | fn main() { 14 | let mut app = App::new(); 15 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 16 | .add_plugins(DefaultPlugins) 17 | .add_plugins(bevy_mod_imgui::ImguiPlugin { 18 | ..Default::default() 19 | }) 20 | .add_systems(Startup, |mut commands: Commands| { 21 | commands.spawn(Camera3d::default()); 22 | }) 23 | .add_systems(Startup, |mut imgui: NonSendMut| { 24 | // Use `with_io_mut` to get mutable access to the underlying ImGui io structure 25 | // for configuration... 26 | imgui.with_io_mut(|io| { 27 | io.config_docking_always_tab_bar = true; 28 | }); 29 | }) 30 | .add_systems(Update, imgui_example_ui); 31 | app.run(); 32 | } 33 | 34 | #[cfg(feature = "docking")] 35 | fn imgui_example_ui(mut context: NonSendMut) { 36 | let ui = context.ui(); 37 | 38 | // Create a dockspace that covers the entire viewport (so that windows can be docked to it) 39 | ui.dockspace_over_main_viewport(); 40 | 41 | // Create some windows that can be docked... 42 | let window_a = ui.window("Window A"); 43 | window_a 44 | .size([300.0, 100.0], imgui::Condition::FirstUseEver) 45 | .position([0.0, 0.0], imgui::Condition::FirstUseEver) 46 | .build(|| { 47 | ui.text("This is Window A!"); 48 | ui.text("Drag the window tabs to dock them"); 49 | }); 50 | 51 | let window_b = ui.window("Window B"); 52 | window_b 53 | .size([300.0, 100.0], imgui::Condition::FirstUseEver) 54 | .position([350.0, 0.0], imgui::Condition::FirstUseEver) 55 | .build(|| { 56 | ui.text("This is Window B!"); 57 | }); 58 | 59 | let window_c = ui.window("Window C"); 60 | window_c 61 | .size([300.0, 100.0], imgui::Condition::FirstUseEver) 62 | .position([0.0, 150.0], imgui::Condition::FirstUseEver) 63 | .build(|| { 64 | ui.text("This is Window C!"); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /examples/hello-world.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_mod_imgui::prelude::*; 3 | 4 | #[derive(Resource)] 5 | struct ImguiState { 6 | demo_window_open: bool, 7 | } 8 | 9 | fn main() { 10 | let mut app = App::new(); 11 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 12 | .insert_resource(ImguiState { 13 | demo_window_open: true, 14 | }) 15 | .add_plugins(DefaultPlugins) 16 | .add_systems(Startup, setup) 17 | .add_plugins(bevy_mod_imgui::ImguiPlugin { 18 | ini_filename: Some("hello-world.ini".into()), 19 | font_oversample_h: 2, 20 | font_oversample_v: 2, 21 | ..default() 22 | }) 23 | .add_systems(Update, imgui_example_ui); 24 | 25 | app.run(); 26 | } 27 | 28 | fn setup( 29 | mut commands: Commands, 30 | mut meshes: ResMut>, 31 | mut materials: ResMut>, 32 | ) { 33 | // plane 34 | commands.spawn(( 35 | Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), 36 | MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), 37 | )); 38 | // cube 39 | commands.spawn(( 40 | Mesh3d(meshes.add(Cuboid::default().mesh())), 41 | MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))), 42 | Transform::from_xyz(0.0, 0.5, 0.0), 43 | )); 44 | // light 45 | commands.spawn(( 46 | PointLight { 47 | shadows_enabled: true, 48 | ..default() 49 | }, 50 | Transform::from_xyz(4.0, 8.0, 4.0), 51 | )); 52 | // camera 53 | commands.spawn(( 54 | Transform::from_xyz(1.7, 1.7, 2.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), 55 | Camera3d::default(), 56 | )); 57 | } 58 | 59 | fn imgui_example_ui(mut context: NonSendMut, mut state: ResMut) { 60 | let ui = context.ui(); 61 | let window = ui.window("Hello world"); 62 | window 63 | .size([300.0, 100.0], imgui::Condition::FirstUseEver) 64 | .position([0.0, 0.0], imgui::Condition::FirstUseEver) 65 | .build(|| { 66 | ui.text("Hello world!"); 67 | ui.text("This...is...bevy_mod_imgui!"); 68 | ui.separator(); 69 | let mouse_pos = ui.io().mouse_pos; 70 | ui.text(format!( 71 | "Mouse Position: ({:.1},{:.1})", 72 | mouse_pos[0], mouse_pos[1] 73 | )); 74 | }); 75 | 76 | if state.demo_window_open { 77 | ui.show_demo_window(&mut state.demo_window_open); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/hello-world-postupdate.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_mod_imgui::prelude::*; 3 | 4 | #[derive(Resource)] 5 | struct ImguiState { 6 | demo_window_open: bool, 7 | } 8 | 9 | fn main() { 10 | let mut app = App::new(); 11 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 12 | .insert_resource(ImguiState { 13 | demo_window_open: true, 14 | }) 15 | .add_plugins(DefaultPlugins) 16 | .add_systems(Startup, setup) 17 | .add_plugins(bevy_mod_imgui::ImguiPlugin { 18 | ini_filename: Some("hello-world.ini".into()), 19 | font_oversample_h: 2, 20 | font_oversample_v: 2, 21 | ..default() 22 | }) 23 | .add_systems(PostUpdate, imgui_example_ui); 24 | 25 | app.run(); 26 | } 27 | 28 | fn setup( 29 | mut commands: Commands, 30 | mut meshes: ResMut>, 31 | mut materials: ResMut>, 32 | ) { 33 | // plane 34 | commands.spawn(( 35 | Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), 36 | MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), 37 | )); 38 | // cube 39 | commands.spawn(( 40 | Mesh3d(meshes.add(Cuboid::default().mesh())), 41 | MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))), 42 | Transform::from_xyz(0.0, 0.5, 0.0), 43 | )); 44 | // light 45 | commands.spawn(( 46 | PointLight { 47 | shadows_enabled: true, 48 | ..default() 49 | }, 50 | Transform::from_xyz(4.0, 8.0, 4.0), 51 | )); 52 | // camera 53 | commands.spawn(( 54 | Transform::from_xyz(1.7, 1.7, 2.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), 55 | Camera3d::default(), 56 | )); 57 | } 58 | 59 | fn imgui_example_ui(mut context: NonSendMut, mut state: ResMut) { 60 | let ui = context.ui(); 61 | let window = ui.window("Hello world"); 62 | window 63 | .size([300.0, 100.0], imgui::Condition::FirstUseEver) 64 | .position([0.0, 0.0], imgui::Condition::FirstUseEver) 65 | .build(|| { 66 | ui.text("Hello world!"); 67 | ui.text("This...is...bevy_mod_imgui!"); 68 | ui.separator(); 69 | let mouse_pos = ui.io().mouse_pos; 70 | ui.text(format!( 71 | "Mouse Position: ({:.1},{:.1})", 72 | mouse_pos[0], mouse_pos[1] 73 | )); 74 | }); 75 | 76 | if state.demo_window_open { 77 | ui.show_demo_window(&mut state.demo_window_open); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/imgui_wgpu_rs_local/imgui.wgsl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Steven Wittens 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | struct Uniforms { 24 | u_Matrix: mat4x4, 25 | }; 26 | 27 | struct VertexInput { 28 | @location(0) a_Pos: vec2, 29 | @location(1) a_UV: vec2, 30 | @location(2) a_Color: vec4, 31 | }; 32 | 33 | struct VertexOutput { 34 | @location(0) v_UV: vec2, 35 | @location(1) v_Color: vec4, 36 | @builtin(position) v_Position: vec4, 37 | }; 38 | 39 | @group(0) @binding(0) 40 | var uniforms: Uniforms; 41 | 42 | @vertex 43 | fn vs_main(in: VertexInput) -> VertexOutput { 44 | var out: VertexOutput; 45 | out.v_UV = in.a_UV; 46 | out.v_Color = in.a_Color; 47 | out.v_Position = uniforms.u_Matrix * vec4(in.a_Pos.xy, 0.0, 1.0); 48 | return out; 49 | } 50 | 51 | struct FragmentOutput { 52 | @location(0) o_Target: vec4, 53 | }; 54 | 55 | @group(1) @binding(0) 56 | var u_Texture: texture_2d; 57 | @group(1) @binding(1) 58 | var u_Sampler: sampler; 59 | 60 | fn srgb_to_linear(srgb: vec4) -> vec4 { 61 | let color_srgb = srgb.rgb; 62 | let selector = ceil(color_srgb - 0.04045); // 0 if under value, 1 if over 63 | let under = color_srgb / 12.92; 64 | let over = pow((color_srgb + 0.055) / 1.055, vec3(2.4)); 65 | let result = mix(under, over, selector); 66 | return vec4(result, srgb.a); 67 | } 68 | 69 | @fragment 70 | fn fs_main_linear(in: VertexOutput) -> FragmentOutput { 71 | let color = srgb_to_linear(in.v_Color); 72 | 73 | return FragmentOutput(color * textureSample(u_Texture, u_Sampler, in.v_UV)); 74 | } 75 | 76 | @fragment 77 | fn fs_main_srgb(in: VertexOutput) -> FragmentOutput { 78 | let color = in.v_Color; 79 | 80 | return FragmentOutput(color * textureSample(u_Texture, u_Sampler, in.v_UV)); 81 | } -------------------------------------------------------------------------------- /examples/custom-texture.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_mod_imgui::prelude::*; 3 | 4 | #[derive(Default, Resource)] 5 | struct ImguiState { 6 | demo_window_open: bool, 7 | texture_handle: Option>, 8 | texture_id: usize, 9 | } 10 | 11 | fn main() { 12 | let mut app = App::new(); 13 | app.insert_resource(ClearColor(Color::srgba(0.2, 0.2, 0.2, 1.0))) 14 | .insert_resource(ImguiState { 15 | demo_window_open: true, 16 | ..Default::default() 17 | }) 18 | .add_plugins(DefaultPlugins) 19 | .add_plugins(bevy_mod_imgui::ImguiPlugin::default()) 20 | .add_systems(Startup, |mut commands: Commands| { 21 | commands.spawn(Camera3d::default()); 22 | }) 23 | .add_systems(Update, imgui_example_ui) 24 | .add_systems(Startup, startup); 25 | app.run(); 26 | } 27 | 28 | fn startup(mut state: ResMut, asset_server: Res) { 29 | load_texture(&mut state, &asset_server); 30 | } 31 | 32 | fn load_texture(state: &mut ResMut, asset_server: &Res) { 33 | state.texture_handle = Some(asset_server.load("Textures/example_texture.png")); 34 | } 35 | 36 | fn register_texture_if_loaded( 37 | state: &mut ResMut, 38 | asset_server: &Res, 39 | context: &mut NonSendMut, 40 | ) { 41 | if state.texture_id == 0 { 42 | if let Some(texture_handle) = &state.texture_handle { 43 | if asset_server 44 | .get_load_state(texture_handle.id()) 45 | .unwrap() 46 | .is_loaded() 47 | { 48 | state.texture_id = context.register_bevy_texture(texture_handle.clone()).id(); 49 | } 50 | } 51 | } 52 | } 53 | 54 | fn unload_texture(state: &mut ResMut, context: &mut NonSendMut) { 55 | context.unregister_bevy_texture(&TextureId::new(state.texture_id)); 56 | state.texture_handle = Default::default(); 57 | state.texture_id = 0; 58 | } 59 | 60 | fn imgui_example_ui( 61 | mut state: ResMut, 62 | asset_server: Res, 63 | images: Res>, 64 | mut context: NonSendMut, 65 | ) { 66 | register_texture_if_loaded(&mut state, &asset_server, &mut context); 67 | 68 | // Do we have a texture? 69 | let has_texture = state.texture_id != 0; 70 | let mut should_unload_texture = false; 71 | let mut should_load_texture = false; 72 | 73 | let ui = context.ui(); 74 | if state.demo_window_open { 75 | let window = ui.window("Custom Texture"); 76 | window 77 | .size([700.0, 700.0], imgui::Condition::FirstUseEver) 78 | .position([0.0, 0.0], imgui::Condition::FirstUseEver) 79 | .build(|| { 80 | ui.text("This is a custom Bevy texture"); 81 | ui.separator(); 82 | if has_texture { 83 | if ui.button("Unload Texture") { 84 | should_unload_texture = true; 85 | } else if let Some(image) = images.get( 86 | state 87 | .texture_handle 88 | .as_ref() 89 | .expect("Should always have a texture at this point"), 90 | ) { 91 | let image_size = [image.width() as f32, image.height() as f32]; 92 | let image_control = 93 | imgui::Image::new(TextureId::new(state.texture_id), image_size); 94 | image_control.build(ui); 95 | } 96 | } else if ui.button("Load Texture") { 97 | should_load_texture = true; 98 | } 99 | }); 100 | } 101 | 102 | if should_load_texture { 103 | load_texture(&mut state, &asset_server); 104 | } else if should_unload_texture { 105 | unload_texture(&mut state, &mut context); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bevy_mod_imgui 2 | 3 | ![Crates.io](https://img.shields.io/crates/v/bevy_mod_imgui) 4 | ![Crates.io](https://img.shields.io/crates/l/bevy_mod_imgui) 5 | ![Build Status](https://github.com/jbrd/bevy_mod_imgui/actions/workflows/rust.yml/badge.svg) 6 | ![docs.rs](https://img.shields.io/docsrs/bevy_mod_imgui) 7 | 8 | A Dear ImGui integration for the Bevy game engine. 9 | 10 | ![bevy_mod_imgui screenshot](media/screenshot.png) 11 | 12 | ## Current Status 13 | 14 | This repository is actively maintained and updated when new versions of Bevy are released. New feature 15 | requests are also welcome, although this project remains relatively low priority, so it may take some 16 | time for these to be honoured. Contributions welcome - please do start an issue prior to working on 17 | a feature so that it can be discussed before spending time on it. 18 | 19 | This crate is not related to any official Bevy organisation repository in any way. 20 | 21 | ## Compatibility Table 22 | 23 | |`bevy_mod_imgui`|`bevy` |`wgpu` |`imgui` |`imgui-wgpu` | 24 | |----------------|--------|--------|--------|------------------| 25 | | 0.8.* | 0.17.* | 26.* | 0.12.* | 0.24.0 (bundled) | 26 | | 0.7.* | 0.16.* | 24.* | 0.12.* | 0.24.0 (bundled) | 27 | | 0.6.* | 0.15.* | 23.* | 0.12.* | 0.24.0 (bundled) | 28 | | 0.5.* | 0.14.* | 0.20.* | 0.11.* | 0.24.0 (bundled) | 29 | | 0.4.* | 0.14.* | 0.20.* | 0.11.* | 0.24.0 (bundled) | 30 | | 0.3.* | 0.13.* | 0.19.* | 0.11.* | 0.24.0 (bundled) | 31 | | 0.2.* | 0.12.* | 0.17.1 | 0.11.* | 0.24.* | 32 | | 0.1.* | 0.11.* | 0.16.* | 0.11.* | 0.23.* | 33 | 34 | ## Optional Features 35 | 36 | ### Docking Windows 37 | 38 | Support for docking windows can be turned on by enabling the `docking` feature. 39 | 40 | Use the following command to run the docking demo: `cargo run --example docking --features docking` 41 | 42 | ![docking example](media/docking.gif) 43 | 44 | ## Examples 45 | 46 | The following examples are provided: 47 | 48 | * `custom-texture` - to demonstrate how to display a Bevy texture in an ImGui window 49 | * `docking` - to demonstrate the `docking` feature (see above) 50 | * `empty` - to demonstrate that an empty draw list is handled gracefully (bug regression example) 51 | * `hello-world` - to demonstrate basic ImGui functionality (via its demo window) 52 | * `hello-world-postupdate` - to demonstrate emitting ImGui from the PostUpdate stage 53 | * `minimal` - matches the minimal example from the documentation 54 | * `minimal-docking` - matches the minimal docking example from the documentation 55 | * `render-to-texture` - to demonstrate rendering a Bevy scene to a texture and displaying the result on an ImGui window 56 | 57 | 58 | ## Changelog 59 | 60 | * `0.8.0` - Add `docking` feature. Update to wgpu `26.0`, Bevy `0.17.0`. Fix render glitch when texture format / display scale is changed. 61 | * `0.7.2` - Fix backend renderer to support ImGui 1.86+ modals 62 | * `0.7.1` - Fix for crash in imgui-wpu-rs when the draw list is empty 63 | * `0.7.0` - Update to wgpu `24.0`, Bevy `0.16.0`. Improved safety / stability of texture management 64 | * `0.6.0` - Update to imgui-rs `0.12.0`, wgpu `23.0`, Bevy `0.15.0` 65 | * `0.5.1` - Various threading and safety fixes. Fix crash when plugin used with Bevy `multi_threaded` feature 66 | * `0.5.0` - Add support for drawing Bevy textures in ImGui windows. Fix assert on exit introduced in Bevy 0.14.1 67 | * `0.4.0` - Updated dependencies for Bevy `0.14.0`. Improved handling of display scale changes. 68 | * `0.3.0` - Updated dependencies for Bevy `0.13.0` with bundled `imgui-wgpu-rs`. 69 | * `0.2.1` - Fix Issue #20 - unchecked window lookup which could cause panic during exit 70 | * `0.2.0` - Updated dependencies for Bevy `0.12.0` 71 | * `0.1.1` - Fix Issue #20 - unchecked window lookup which could cause panic during exit (backported from `0.2.1`) 72 | * `0.1.0` - Initial crate publish 73 | 74 | ## Contributors 75 | 76 | * James Bird (@jbrd) 77 | * @nhlest 78 | * @PJB3005 79 | * Marius Metzger (@CrushedPixel) 80 | 81 | ## Acknowledgements 82 | 83 | This crate builds upon the fantastic work of the following projects: 84 | 85 | * [Dear ImGui](https://github.com/ocornut/imgui) 86 | * [imgui-rs](https://github.com/imgui-rs/imgui-rs) 87 | * [imgui-wgpu-rs](https://github.com/Yatekii/imgui-wgpu-rs) 88 | * [Bevy](https://github.com/bevyengine/bevy) 89 | 90 | ## License 91 | 92 | All code in this repository is permissively dual-licensed under: 93 | 94 | * MIT License - [LICENSE-MIT](LICENSE-MIT) 95 | * Apache License, Version 2.0 - [LICENSE-APACHE](LICENSE-APACHE) 96 | 97 | ## Bundled Software License Notices 98 | 99 | ### imgui-wgpu-rs 100 | 101 | This software contains portions of code derived from [imgui-wgpu-rs](https://github.com/Yatekii/imgui-wgpu-rs/tree/master). 102 | https://github.com/Yatekii/imgui-wgpu-rs/tree/master 103 | Licensed under the Apache License 104 | 105 | Copyright (c) 2019 Steven Wittens 106 | 107 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 108 | 109 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 110 | 111 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 112 | -------------------------------------------------------------------------------- /examples/render-to-texture.rs: -------------------------------------------------------------------------------- 1 | //! Shows how to render to a texture. Useful for mirrors, UI, or exporting images. 2 | 3 | use bevy::{ 4 | camera::visibility::RenderLayers, 5 | prelude::*, 6 | render::render_resource::{ 7 | Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, 8 | }, 9 | }; 10 | 11 | use bevy_mod_imgui::prelude::*; 12 | 13 | #[derive(Default, Resource)] 14 | struct ImguiState { 15 | demo_window_open: bool, 16 | texture_handle: Option>, 17 | texture_id: usize, 18 | } 19 | 20 | fn main() { 21 | App::new() 22 | .insert_resource(ImguiState { 23 | demo_window_open: true, 24 | ..Default::default() 25 | }) 26 | .add_plugins(DefaultPlugins) 27 | .add_plugins(bevy_mod_imgui::ImguiPlugin::default()) 28 | .add_systems(Startup, setup) 29 | .add_systems(Update, (imgui_example_ui, rotator_system)) 30 | .run(); 31 | } 32 | 33 | // Marks the first pass cube (rendered to a texture.) 34 | #[derive(Component)] 35 | struct FirstPassCube; 36 | 37 | fn setup( 38 | mut commands: Commands, 39 | mut meshes: ResMut>, 40 | mut materials: ResMut>, 41 | mut images: ResMut>, 42 | mut state: ResMut, 43 | ) { 44 | // Set up a render target 45 | let size = Extent3d { 46 | width: 512, 47 | height: 512, 48 | ..default() 49 | }; 50 | 51 | let mut image = Image { 52 | texture_descriptor: TextureDescriptor { 53 | label: None, 54 | size, 55 | dimension: TextureDimension::D2, 56 | format: TextureFormat::Bgra8UnormSrgb, 57 | mip_level_count: 1, 58 | sample_count: 1, 59 | usage: TextureUsages::TEXTURE_BINDING 60 | | TextureUsages::COPY_DST 61 | | TextureUsages::RENDER_ATTACHMENT, 62 | view_formats: &[], 63 | }, 64 | ..default() 65 | }; 66 | 67 | // fill render target with zeroes 68 | image.resize(size); 69 | let image_handle = images.add(image); 70 | 71 | // Store texture handle so we can use it during the imgui update 72 | state.texture_handle = Some(image_handle.clone()); 73 | 74 | // Set up a cube that will be rendered to the texture 75 | let cube_handle = meshes.add(Cuboid::new(4.0, 4.0, 4.0)); 76 | let cube_material_handle = materials.add(StandardMaterial { 77 | base_color: Color::srgb(0.8, 0.7, 0.6), 78 | reflectance: 0.02, 79 | unlit: false, 80 | ..default() 81 | }); 82 | 83 | // This specifies the layer used for the first pass, which will be attached to the first pass camera and cube. 84 | let first_pass_layer = RenderLayers::layer(1); 85 | 86 | // The cube that will be rendered to the texture. 87 | commands.spawn(( 88 | Mesh3d(cube_handle), 89 | MeshMaterial3d(cube_material_handle), 90 | Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)), 91 | FirstPassCube, 92 | first_pass_layer.clone(), 93 | )); 94 | 95 | // Light 96 | // NOTE: we add the light to both layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture 97 | // Setting the layer to RenderLayers::layer(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. 98 | // Setting the layer to RenderLayers::layer(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. 99 | commands.spawn(( 100 | PointLight { 101 | shadows_enabled: true, 102 | intensity: 10_000_000., 103 | range: 100.0, 104 | shadow_depth_bias: 0.2, 105 | ..default() 106 | }, 107 | Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), 108 | RenderLayers::layer(0).with(1), 109 | )); 110 | 111 | commands.spawn(( 112 | Camera { 113 | // render before the "main pass" camera 114 | order: -1, 115 | target: image_handle.clone().into(), 116 | clear_color: Color::WHITE.into(), 117 | ..default() 118 | }, 119 | Camera3d::default(), 120 | Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)).looking_at(Vec3::ZERO, Vec3::Y), 121 | first_pass_layer, 122 | )); 123 | 124 | // The main pass camera. 125 | commands.spawn(Transform::from_xyz(0.0, 0.0, 15.0).looking_at(Vec3::ZERO, Vec3::Y)); 126 | } 127 | 128 | /// Rotates the inner cube (first pass) 129 | fn rotator_system(time: Res