Added render-to-UI-image example

The gotcha was that the 3d camera was rendering the UI but shouldn't
have.

Now I think we can show the previews.

Still not sure how to render each preview in it's own "space" without
them overlapping...
main
Elijah Voigt 2 years ago
parent c82a38dbc8
commit 07eefb11ce

@ -7,5 +7,9 @@ edition = "2021"
name = "gltf-inspect" name = "gltf-inspect"
path = "bin/gltf-inspect.rs" path = "bin/gltf-inspect.rs"
[[bin]]
name = "camera-image-ui"
path = "bin/camera-image-ui.rs"
[dependencies] [dependencies]
bevy = "0.10" bevy = "0.10"

@ -0,0 +1,130 @@
use bevy::{
core_pipeline::clear_color::ClearColorConfig,
prelude::*,
render::{camera::RenderTarget, render_resource::*, view::RenderLayers},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Camera Image UI".into(),
resolution: (640., 480.).into(),
..default()
}),
..default()
}))
.add_startup_system(init)
.run();
}
fn init(
mut commands: Commands,
mut images: ResMut<Assets<Image>>,
mut meshes: ResMut<Assets<Mesh>>,
) {
info!("Creating preview");
let size = Extent3d {
width: 512,
height: 512,
..default()
};
// Create render target image for the preview camera
let mut image = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8UnormSrgb,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
..default()
};
// Fill with zeroes
image.resize(size);
let image_handle = images.add(image);
commands
.spawn((
TransformBundle { ..default() },
VisibilityBundle { ..default() },
))
.with_children(|parent| {
// Spawn preview camera
parent.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
..default()
},
camera: Camera {
order: -1,
target: RenderTarget::Image(image_handle.clone()),
..default()
},
transform: Transform::from_xyz(2.0, 2.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
UiCameraConfig { show_ui: false },
RenderLayers::layer(1),
));
parent.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.5 })),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
},
RenderLayers::layer(1),
));
parent.spawn(PointLightBundle {
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)),
..default()
});
});
// Assign render target image to UI element
commands
.spawn(NodeBundle {
style: Style {
size: Size::all(Val::Percent(50.0)),
justify_content: JustifyContent::SpaceBetween,
..default()
},
..default()
})
.with_children(|parent| {
parent.spawn(ImageBundle {
image: UiImage {
texture: image_handle.clone(),
..default()
},
style: Style {
size: Size::all(Val::Percent(100.0)),
..default()
},
..default()
});
});
commands.spawn((
Camera2dBundle {
camera_2d: Camera2d {
clear_color: ClearColorConfig::Custom(Color::TEAL),
},
transform: Transform::from_xyz(2.0, 2.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
RenderLayers::layer(0),
));
}

@ -1,4 +1,5 @@
use bevy::{ use bevy::{
core_pipeline::clear_color::ClearColorConfig,
gltf::Gltf, gltf::Gltf,
input::{ input::{
keyboard::KeyboardInput, keyboard::KeyboardInput,
@ -7,6 +8,14 @@ use bevy::{
}, },
pbr::CascadeShadowConfigBuilder, pbr::CascadeShadowConfigBuilder,
prelude::*, prelude::*,
render::{
camera::RenderTarget,
render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
view::RenderLayers,
},
winit::WinitSettings,
}; };
fn main() { fn main() {
@ -19,9 +28,12 @@ fn main() {
}), }),
..default() ..default()
})) }))
// Only run when window is active to reduce cpu cycles
// .insert_resource(WinitSettings::desktop_app())
.add_startup_system(load_models) .add_startup_system(load_models)
.add_startup_system(spawn_base_scene) .add_startup_system(spawn_base_scene)
.add_system(spawn_models) .add_system(spawn_models)
// .add_system(control_ui)
.add_system(control_camera) .add_system(control_camera)
.add_system(rotate_model) .add_system(rotate_model)
.add_system(zoom_model) .add_system(zoom_model)
@ -32,7 +44,7 @@ fn main() {
/// Stores GLTF handles for later use /// Stores GLTF handles for later use
#[derive(Resource)] #[derive(Resource)]
struct Models { struct Models {
handles: Vec<Handle<Gltf>>, _handles: Vec<Handle<Gltf>>,
} }
/// ///
@ -43,16 +55,19 @@ struct Inspect;
#[derive(Component)] #[derive(Component)]
struct SelectionUI; struct SelectionUI;
#[derive(Component)]
struct PreviewCamera;
/// ///
/// Load all GLTF models on startup /// Load all GLTF models on startup
fn load_models(mut commands: Commands, ass: Res<AssetServer>) { fn load_models(mut commands: Commands, ass: Res<AssetServer>) {
let weak_handles = ass.load_folder("models").expect("Load gltfs"); let weak_handles = ass.load_folder("models").expect("Load gltfs");
let handles: Vec<Handle<Gltf>> = weak_handles let _handles: Vec<Handle<Gltf>> = weak_handles
.iter() .iter()
.map(|weak| weak.clone().typed::<Gltf>()) .map(|weak| weak.clone().typed::<Gltf>())
.collect(); .collect();
info!("Scene Handles: {:#?}", handles); info!("Scene Handles: {:#?}", _handles);
commands.insert_resource(Models { handles }); commands.insert_resource(Models { _handles });
} }
/// ///
@ -60,7 +75,9 @@ fn load_models(mut commands: Commands, ass: Res<AssetServer>) {
fn spawn_base_scene(mut commands: Commands) { fn spawn_base_scene(mut commands: Commands) {
commands.spawn(( commands.spawn((
Camera2dBundle { Camera2dBundle {
transform: Transform::from_xyz(0.0, 0.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), camera_2d: Camera2d {
clear_color: ClearColorConfig::Custom(Color::BLACK),
},
..default() ..default()
}, },
UiCameraConfig { ..default() }, UiCameraConfig { ..default() },
@ -72,7 +89,6 @@ fn control_camera(
mut keyboard_evr: EventReader<KeyboardInput>, mut keyboard_evr: EventReader<KeyboardInput>,
mut ui_camera: Query<&mut Camera, (With<SelectionUI>, Without<Inspect>)>, mut ui_camera: Query<&mut Camera, (With<SelectionUI>, Without<Inspect>)>,
mut scene_camera: Query<&mut Camera, (With<Inspect>, Without<SelectionUI>)>, mut scene_camera: Query<&mut Camera, (With<Inspect>, Without<SelectionUI>)>,
mut scene_objects: Query<&mut ComputedVisibility, With<Inspect>>,
) { ) {
for ev in keyboard_evr.iter() { for ev in keyboard_evr.iter() {
match ev { match ev {
@ -111,8 +127,8 @@ fn control_camera(
/// TODO: Update/add/delete when files are updated /// TODO: Update/add/delete when files are updated
fn spawn_models( fn spawn_models(
mut commands: Commands, mut commands: Commands,
// TODO: Need to iter over gltf not scenes?
mut scene_evr: EventReader<AssetEvent<Scene>>, mut scene_evr: EventReader<AssetEvent<Scene>>,
mut images: ResMut<Assets<Image>>,
) { ) {
if !scene_evr.is_empty() { if !scene_evr.is_empty() {
info!("Spawning scenes"); info!("Spawning scenes");
@ -121,15 +137,99 @@ fn spawn_models(
match ev { match ev {
AssetEvent::Created { handle } => { AssetEvent::Created { handle } => {
info!("Creating scene {:#?}", handle); info!("Creating scene {:#?}", handle);
// Preview image
{
info!("Creating preview");
let size = Extent3d {
width: 512,
height: 512,
..default()
};
// Create render target image for the preview camera
let mut image = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8UnormSrgb,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
..default()
};
// Fill with zeroes
image.resize(size);
let image_handle = images.add(image);
// Spawn preview camera
commands.spawn((
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
..default()
},
camera: Camera {
order: -1,
target: RenderTarget::Image(image_handle.clone()),
..default()
},
transform: Transform::from_xyz(5.0, 5.0, 5.0)
.looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
UiCameraConfig { show_ui: false },
PreviewCamera,
));
// Assign render target image to UI element
commands
.spawn(NodeBundle {
style: Style {
size: Size::all(Val::Percent(50.0)),
justify_content: JustifyContent::SpaceBetween,
..default()
},
..default()
})
.with_children(|parent| {
parent.spawn(ImageBundle {
style: Style {
size: Size::all(Val::Percent(100.0)),
..default()
},
image: UiImage {
texture: image_handle.clone(),
..default()
},
..default()
});
});
}
// Spawn the actual scene
commands commands
.spawn(( .spawn((
Inspect, Inspect,
VisibilityBundle::default(), VisibilityBundle::default(),
TransformBundle::default(), TransformBundle::default(), // TODO: Move to staging area
RenderLayers::layer(1), // SUS
)) ))
.with_children(|builder| { .with_children(|builder| {
builder.spawn(( builder.spawn((
Camera3dBundle { Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::Custom(Color::WHITE),
..default()
},
camera: Camera { camera: Camera {
is_active: false, is_active: false,
..default() ..default()
@ -139,6 +239,7 @@ fn spawn_models(
..default() ..default()
}, },
Inspect, Inspect,
RenderLayers::layer(1), // SUS
)); ));
builder.spawn(( builder.spawn((
@ -156,6 +257,7 @@ fn spawn_models(
..default() ..default()
}, },
Inspect, Inspect,
RenderLayers::layer(1), // SUS
)); ));
builder.spawn(( builder.spawn((
@ -164,6 +266,7 @@ fn spawn_models(
..default() ..default()
}, },
Inspect, Inspect,
RenderLayers::layer(1), // SUS
)); ));
}); });
} }
@ -177,6 +280,10 @@ fn spawn_models(
} }
} }
// fn control_ui(mut commands: Commands, cameras: Query<Camera, With<Inspect>>) {
// todo!()
// }
/// ///
/// Rotate a model as part of inspection /// Rotate a model as part of inspection
fn rotate_model( fn rotate_model(

Loading…
Cancel
Save