-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP: Begin egui-texture skill * Stuck on actually getting GpuImage * Need to fix which resources are used when * Added extract keyword to in * Now runs, but warnings about managed textures * Fixed bug where I was constantly adding render nodes * Got it working * Rename to * Updated docs * Make it unlit * Added screenshot * nit: fix space in readme
- Loading branch information
Showing
9 changed files
with
589 additions
and
1 deletion.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "worldspace-ui" | ||
version.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
|
||
[dependencies] | ||
bevy.workspace = true | ||
bevy_egui.workspace = true | ||
bevy_flycam.workspace = true | ||
color-eyre.workspace = true | ||
egui-wgpu.workspace = true | ||
egui.workspace = true | ||
wgpu.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# `worldspace-ui` | ||
|
||
A cube that renders an [egui](https://www.egui.rs/#demo) application to a texture, | ||
and applies it to a cube. | ||
|
||
To run the code: | ||
```bash | ||
cargo run -p worldspace-ui | ||
``` | ||
|
||
![Screenshot](screenshot.png) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
mod render_node; | ||
mod render_systems; | ||
|
||
use bevy::prelude::*; | ||
use bevy_egui::egui; | ||
use bevy_egui::EguiContexts; | ||
use color_eyre::Result; | ||
|
||
fn main() -> Result<()> { | ||
color_eyre::install()?; | ||
let mut app = App::new(); | ||
app.add_plugins(DefaultPlugins) | ||
.add_plugins(bevy_flycam::PlayerPlugin) | ||
.add_plugins(bevy_egui::EguiPlugin) | ||
.insert_resource(AmbientLight { | ||
color: Color::WHITE, | ||
brightness: 1., | ||
}) | ||
.add_systems(Startup, setup) | ||
.add_systems(Update, screenspace_ui); | ||
|
||
let Ok(render_app) = app.get_sub_app_mut(bevy::render::RenderApp) else { | ||
panic!("The render plugin should have added this subapp"); | ||
}; | ||
render_app.add_systems(ExtractSchedule, crate::render_systems::add_render_node); | ||
|
||
app.run(); | ||
Ok(()) | ||
} | ||
|
||
#[derive(Component, Clone)] | ||
pub struct EguiContext { | ||
output_texture: Handle<Image>, | ||
ctx: egui::Context, | ||
} | ||
|
||
fn setup( | ||
mut commands: Commands, | ||
mut meshes: ResMut<Assets<Mesh>>, | ||
mut materials: ResMut<Assets<StandardMaterial>>, | ||
mut images: ResMut<Assets<Image>>, | ||
) { | ||
let egui_thing = { | ||
let size = wgpu::Extent3d { | ||
width: 256, | ||
height: 256, | ||
depth_or_array_layers: 1, | ||
}; | ||
let mut output_texture = Image { | ||
data: vec![255; (size.width * size.height * 4) as usize], | ||
..default() | ||
}; | ||
output_texture.texture_descriptor.usage |= | ||
wgpu::TextureUsages::RENDER_ATTACHMENT; | ||
output_texture.texture_descriptor.size = size; | ||
let output_texture = images.add(output_texture); | ||
|
||
EguiContext { | ||
output_texture, | ||
ctx: egui::Context::default(), | ||
} | ||
}; | ||
|
||
commands.spawn(( | ||
PbrBundle { | ||
mesh: meshes.add(shape::Cube::default().into()), | ||
material: materials.add(StandardMaterial { | ||
base_color: Color::WHITE, | ||
base_color_texture: Some(Handle::clone(&egui_thing.output_texture)), | ||
// Remove this if you want it to use the world's lighting. | ||
unlit: true, | ||
..default() | ||
}), | ||
..default() | ||
}, | ||
egui_thing, | ||
)); | ||
} | ||
|
||
fn screenspace_ui(mut contexts: EguiContexts) { | ||
egui::Window::new("Screenspace Window").show(contexts.ctx_mut(), |ui| { | ||
ui.label("I am rendering to the screen!"); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use bevy::{prelude::*, render::render_asset::RenderAssets}; | ||
use std::sync::Mutex; | ||
|
||
use crate::EguiContext; | ||
|
||
pub struct EguiNode { | ||
pub output_image: Handle<Image>, | ||
pub egui_ctx: EguiContext, | ||
pub renderer: Mutex<egui_wgpu::Renderer>, | ||
} | ||
|
||
impl bevy::render::render_graph::Node for EguiNode { | ||
// TODO: When should I use this instead of an Extract render system? | ||
fn update(&mut self, _world: &mut World) {} | ||
|
||
fn run( | ||
&self, | ||
_graph: &mut bevy::render::render_graph::RenderGraphContext, | ||
render_context: &mut bevy::render::renderer::RenderContext, | ||
world: &World, | ||
) -> Result<(), bevy::render::render_graph::NodeRunError> { | ||
let device = render_context.render_device().clone(); | ||
let device = device.wgpu_device(); | ||
let queue = world | ||
.get_resource::<bevy::render::renderer::RenderQueue>() | ||
.unwrap(); | ||
let encoder = render_context.command_encoder(); | ||
let gpu_images = world.get_resource::<RenderAssets<Image>>().unwrap(); | ||
let output_gpu_image = gpu_images | ||
.get(&self.output_image) | ||
.expect("Should have been a `GpuImage` that corresponds to the `Image`"); | ||
let screen_descriptor = egui_wgpu::renderer::ScreenDescriptor { | ||
pixels_per_point: 1.0, | ||
size_in_pixels: [ | ||
output_gpu_image.texture.size().width, | ||
output_gpu_image.texture.size().height, | ||
], | ||
}; | ||
|
||
let mut renderer = self.renderer.lock().unwrap(); | ||
|
||
// TODO: Eventually I'll move this to a separate user defined system. | ||
let egui_output = self.egui_ctx.ctx.run(egui::RawInput::default(), |ctx| { | ||
egui::Window::new("Worldspace Window") | ||
.show(ctx, |ui| ui.label("I am rendering to a texture on a cube")); | ||
}); | ||
// TODO: Handle textures to delete | ||
for (tid, delta) in egui_output.textures_delta.set.iter() { | ||
renderer.update_texture(device, queue, *tid, delta); | ||
} | ||
let clipped_primitives = self.egui_ctx.ctx.tessellate(egui_output.shapes); | ||
|
||
renderer.update_buffers( | ||
device, | ||
queue, | ||
encoder, | ||
&clipped_primitives, | ||
&screen_descriptor, | ||
); | ||
|
||
let mut egui_render_pass = | ||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { | ||
label: Some("Egui Render Pass"), | ||
color_attachments: &[Some(wgpu::RenderPassColorAttachment { | ||
view: &output_gpu_image.texture_view, | ||
resolve_target: None, | ||
ops: wgpu::Operations { | ||
load: wgpu::LoadOp::Clear(wgpu::Color { | ||
r: 0.1, | ||
g: 0.2, | ||
b: 0.3, | ||
a: 1.0, | ||
}), | ||
store: true, | ||
}, | ||
})], | ||
depth_stencil_attachment: None, | ||
}); | ||
|
||
renderer.render( | ||
&mut egui_render_pass, | ||
&clipped_primitives, | ||
&screen_descriptor, | ||
); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use bevy::render::Extract; | ||
use bevy::{prelude::*, render::render_graph::RenderGraph}; | ||
|
||
use crate::render_node::EguiNode; | ||
use crate::EguiContext; | ||
|
||
pub fn add_render_node( | ||
q: Extract<Query<(Entity, &EguiContext), Added<EguiContext>>>, | ||
// q: Extract<Query<&EguiContext>>, | ||
textures: Extract<Res<Assets<Image>>>, | ||
mut render_graph: ResMut<RenderGraph>, | ||
device: Res<bevy::render::renderer::RenderDevice>, | ||
) { | ||
for (entity, egui_ctx) in q.iter() { | ||
info!("adding render node for {entity:?}"); | ||
let output_texture_format = textures | ||
.get(&egui_ctx.output_texture) | ||
.expect("Should have found the matching `Image`") | ||
.texture_descriptor | ||
.format; | ||
let renderer = egui_wgpu::Renderer::new( | ||
device.wgpu_device(), | ||
output_texture_format, | ||
None, | ||
1, | ||
); | ||
let new_node = EguiNode { | ||
output_image: Handle::clone(&egui_ctx.output_texture), | ||
renderer: renderer.into(), | ||
egui_ctx: egui_ctx.clone(), | ||
}; | ||
let node_label = "egui-texture"; | ||
render_graph.add_node(node_label, new_node); | ||
render_graph | ||
.add_node_edge(bevy::render::main_graph::node::CAMERA_DRIVER, node_label); | ||
} | ||
} |