Consistent screen-space coordinates (#8306)
# Objective Make the coordinate systems of screen-space items (cursor position, UI, viewports, etc.) consistent. ## Solution Remove the weird double inversion of the cursor position's Y origin. Once in bevy_winit to the bottom and then again in bevy_ui back to the top. This leaves the origin at the top left like it is in every other popular app framework. Update the `world_to_viewport`, `viewport_to_world`, and `viewport_to_world_2d` methods to flip the Y origin (as they should since the viewport coordinates were always relative to the top left). ## Migration Guide `Window::cursor_position` now returns the position of the cursor relative to the top left instead of the bottom left. This now matches other screen-space coordinates like `RelativeCursorPosition`, UI, and viewports. The `world_to_viewport`, `viewport_to_world`, and `viewport_to_world_2d` methods on `Camera` now return/take the viewport position relative to the top left instead of the bottom left. If you were using `world_to_viewport` to position a UI node the returned `y` value should now be passed into the `top` field on `Style` instead of the `bottom` field. Note that this might shift the position of the UI node as it is now anchored at the top. If you were passing `Window::cursor_position` to `viewport_to_world` or `viewport_to_world_2d` no change is necessary.
This commit is contained in:
parent
1a7f046c4d
commit
585baf0a66
@ -233,7 +233,10 @@ impl Camera {
|
||||
}
|
||||
|
||||
// Once in NDC space, we can discard the z element and rescale x/y to fit the screen
|
||||
Some((ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * target_size)
|
||||
let mut viewport_position = (ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * target_size;
|
||||
// Flip the Y co-ordinate origin from the bottom to the top.
|
||||
viewport_position.y = target_size.y - viewport_position.y;
|
||||
Some(viewport_position)
|
||||
}
|
||||
|
||||
/// Returns a ray originating from the camera, that passes through everything beyond `viewport_position`.
|
||||
@ -247,9 +250,11 @@ impl Camera {
|
||||
pub fn viewport_to_world(
|
||||
&self,
|
||||
camera_transform: &GlobalTransform,
|
||||
viewport_position: Vec2,
|
||||
mut viewport_position: Vec2,
|
||||
) -> Option<Ray> {
|
||||
let target_size = self.logical_viewport_size()?;
|
||||
// Flip the Y co-ordinate origin from the top to the bottom.
|
||||
viewport_position.y = target_size.y - viewport_position.y;
|
||||
let ndc = viewport_position * 2. / target_size - Vec2::ONE;
|
||||
|
||||
let ndc_to_world =
|
||||
@ -273,9 +278,11 @@ impl Camera {
|
||||
pub fn viewport_to_world_2d(
|
||||
&self,
|
||||
camera_transform: &GlobalTransform,
|
||||
viewport_position: Vec2,
|
||||
mut viewport_position: Vec2,
|
||||
) -> Option<Vec2> {
|
||||
let target_size = self.logical_viewport_size()?;
|
||||
// Flip the Y co-ordinate origin from the top to the bottom.
|
||||
viewport_position.y = target_size.y - viewport_position.y;
|
||||
let ndc = viewport_position * 2. / target_size - Vec2::ONE;
|
||||
|
||||
let world_near_plane = self.ndc_to_world(camera_transform, ndc.extend(1.))?;
|
||||
|
||||
@ -180,12 +180,10 @@ pub fn ui_focus_system(
|
||||
}
|
||||
})
|
||||
.find_map(|window_ref| {
|
||||
windows.get(window_ref.entity()).ok().and_then(|window| {
|
||||
window.cursor_position().map(|mut cursor_pos| {
|
||||
cursor_pos.y = window.height() - cursor_pos.y;
|
||||
cursor_pos
|
||||
})
|
||||
})
|
||||
windows
|
||||
.get(window_ref.entity())
|
||||
.ok()
|
||||
.and_then(|window| window.cursor_position())
|
||||
})
|
||||
.or_else(|| touches_input.first_pressed_position());
|
||||
|
||||
|
||||
@ -434,11 +434,7 @@ pub fn winit_runner(mut app: App) {
|
||||
.send(converters::convert_keyboard_input(input));
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let physical_position = DVec2::new(
|
||||
position.x,
|
||||
// Flip the coordinate space from winit's context to our context.
|
||||
window.resolution.physical_height() as f64 - position.y,
|
||||
);
|
||||
let physical_position = DVec2::new(position.x, position.y);
|
||||
|
||||
window.set_physical_cursor_position(Some(physical_position));
|
||||
|
||||
|
||||
@ -220,51 +220,34 @@ fn setup(
|
||||
ExampleDisplay,
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section("┌─ Opaque\n│\n│\n│\n│", label_text_style.clone()).with_style(
|
||||
Style {
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
),
|
||||
ExampleLabel { entity: opaque },
|
||||
));
|
||||
let mut label = |entity: Entity, label: &str| {
|
||||
commands
|
||||
.spawn((
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
ExampleLabel { entity },
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn(
|
||||
TextBundle::from_section(label, label_text_style.clone()).with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
bottom: Val::Px(0.),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section("┌─ Blend\n│\n│\n│", label_text_style.clone()).with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
}),
|
||||
ExampleLabel { entity: blend },
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section("┌─ Premultiplied\n│\n│", label_text_style.clone()).with_style(
|
||||
Style {
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
),
|
||||
ExampleLabel {
|
||||
entity: premultiplied,
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section("┌─ Add\n│", label_text_style.clone()).with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
}),
|
||||
ExampleLabel { entity: add },
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
TextBundle::from_section("┌─ Multiply", label_text_style).with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
}),
|
||||
ExampleLabel { entity: multiply },
|
||||
));
|
||||
label(opaque, "┌─ Opaque\n│\n│\n│\n│");
|
||||
label(blend, "┌─ Blend\n│\n│\n│");
|
||||
label(premultiplied, "┌─ Premultiplied\n│\n│");
|
||||
label(add, "┌─ Add\n│");
|
||||
label(multiply, "┌─ Multiply");
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
@ -347,20 +330,16 @@ fn example_control_system(
|
||||
0.0
|
||||
};
|
||||
|
||||
camera_transform.rotate_around(
|
||||
Vec3::ZERO,
|
||||
Quat::from_euler(EulerRot::XYZ, 0.0, rotation, 0.0),
|
||||
);
|
||||
camera_transform.rotate_around(Vec3::ZERO, Quat::from_rotation_y(rotation));
|
||||
|
||||
for (mut style, label) in &mut labels {
|
||||
let world_position =
|
||||
labelled.get(label.entity).unwrap().translation() + Vec3::new(0.0, 1.0, 0.0);
|
||||
let world_position = labelled.get(label.entity).unwrap().translation() + Vec3::Y;
|
||||
|
||||
let viewport_position = camera
|
||||
.world_to_viewport(camera_global_transform, world_position)
|
||||
.unwrap();
|
||||
|
||||
style.bottom = Val::Px(viewport_position.y);
|
||||
style.top = Val::Px(viewport_position.y);
|
||||
style.left = Val::Px(viewport_position.x);
|
||||
}
|
||||
|
||||
|
||||
@ -108,10 +108,7 @@ fn toggle_ime(
|
||||
if input.just_pressed(MouseButton::Left) {
|
||||
let mut window = windows.single_mut();
|
||||
|
||||
window.ime_position = window
|
||||
.cursor_position()
|
||||
.map(|p| Vec2::new(p.x, window.height() - p.y))
|
||||
.unwrap();
|
||||
window.ime_position = window.cursor_position().unwrap();
|
||||
window.ime_enabled = !window.ime_enabled;
|
||||
|
||||
let mut text = text.single_mut();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user