Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ui/viewport_node.rs
6595 views
1
//! A simple scene to demonstrate spawning a viewport widget. The example will demonstrate how to
2
//! pick entities visible in the widget's view.
3
4
use bevy::{
5
asset::RenderAssetUsages,
6
camera::RenderTarget,
7
picking::pointer::PointerInteraction,
8
prelude::*,
9
render::render_resource::{TextureDimension, TextureFormat, TextureUsages},
10
ui::widget::ViewportNode,
11
};
12
13
fn main() {
14
App::new()
15
.add_plugins((DefaultPlugins, MeshPickingPlugin))
16
.add_systems(Startup, test)
17
.add_systems(Update, draw_mesh_intersections)
18
.run();
19
}
20
21
#[derive(Component, Reflect, Debug)]
22
#[reflect(Component)]
23
struct Shape;
24
25
fn test(
26
mut commands: Commands,
27
mut images: ResMut<Assets<Image>>,
28
mut meshes: ResMut<Assets<Mesh>>,
29
mut materials: ResMut<Assets<StandardMaterial>>,
30
) {
31
// Spawn a UI camera
32
commands.spawn(Camera3d::default());
33
34
// Set up an texture for the 3D camera to render to.
35
// The size of the texture will be based on the viewport's ui size.
36
let mut image = Image::new_uninit(
37
default(),
38
TextureDimension::D2,
39
TextureFormat::Bgra8UnormSrgb,
40
RenderAssetUsages::all(),
41
);
42
image.texture_descriptor.usage =
43
TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT;
44
let image_handle = images.add(image);
45
46
// Spawn the 3D camera
47
let camera = commands
48
.spawn((
49
Camera3d::default(),
50
Camera {
51
// Render this camera before our UI camera
52
order: -1,
53
target: RenderTarget::Image(image_handle.clone().into()),
54
..default()
55
},
56
))
57
.id();
58
59
// Spawn something for the 3D camera to look at
60
commands
61
.spawn((
62
Mesh3d(meshes.add(Cuboid::new(5.0, 5.0, 5.0))),
63
MeshMaterial3d(materials.add(Color::WHITE)),
64
Transform::from_xyz(0.0, 0.0, -10.0),
65
Shape,
66
))
67
// We can observe pointer events on our objects as normal, the
68
// `bevy::ui::widgets::viewport_picking` system will take care of ensuring our viewport
69
// clicks pass through
70
.observe(on_drag_cuboid);
71
72
// Spawn our viewport widget
73
commands
74
.spawn((
75
Node {
76
position_type: PositionType::Absolute,
77
top: px(50),
78
left: px(50),
79
width: px(200),
80
height: px(200),
81
border: UiRect::all(px(5)),
82
..default()
83
},
84
BorderColor::all(Color::WHITE),
85
ViewportNode::new(camera),
86
))
87
.observe(on_drag_viewport);
88
}
89
90
fn on_drag_viewport(drag: On<Pointer<Drag>>, mut node_query: Query<&mut Node>) {
91
if matches!(drag.button, PointerButton::Secondary) {
92
let mut node = node_query.get_mut(drag.entity()).unwrap();
93
94
if let (Val::Px(top), Val::Px(left)) = (node.top, node.left) {
95
node.left = px(left + drag.delta.x);
96
node.top = px(top + drag.delta.y);
97
};
98
}
99
}
100
101
fn on_drag_cuboid(drag: On<Pointer<Drag>>, mut transform_query: Query<&mut Transform>) {
102
if matches!(drag.button, PointerButton::Primary) {
103
let mut transform = transform_query.get_mut(drag.entity()).unwrap();
104
transform.rotate_y(drag.delta.x * 0.02);
105
transform.rotate_x(drag.delta.y * 0.02);
106
}
107
}
108
109
fn draw_mesh_intersections(
110
pointers: Query<&PointerInteraction>,
111
untargetable: Query<Entity, Without<Shape>>,
112
mut gizmos: Gizmos,
113
) {
114
for (point, normal) in pointers
115
.iter()
116
.flat_map(|interaction| interaction.iter())
117
.filter_map(|(entity, hit)| {
118
if !untargetable.contains(*entity) {
119
hit.position.zip(hit.normal)
120
} else {
121
None
122
}
123
})
124
{
125
gizmos.arrow(point, point + normal.normalize() * 0.5, Color::WHITE);
126
}
127
}
128
129