From f3ab38a8023837455b353a6477788ffaccf8134f Mon Sep 17 00:00:00 2001
From: ira <JustTheCoolDude@gmail.com>
Date: Mon, 11 Sep 2023 20:52:11 +0200
Subject: [PATCH] Add example for `Camera::viewport_to_world` (#7179)

Fixes #7177

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
---
 Cargo.toml                          | 21 +++++++++
 examples/2d/2d_viewport_to_world.rs | 34 ++++++++++++++
 examples/3d/3d_viewport_to_world.rs | 70 +++++++++++++++++++++++++++++
 examples/README.md                  |  2 +
 4 files changed, 127 insertions(+)
 create mode 100644 examples/2d/2d_viewport_to_world.rs
 create mode 100644 examples/3d/3d_viewport_to_world.rs

diff --git a/Cargo.toml b/Cargo.toml
index b0f2d6f610912..b6967a880d1e6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -290,6 +290,16 @@ description = "Changes the transform of a sprite"
 category = "2D Rendering"
 wasm = true
 
+[[example]]
+name = "2d_viewport_to_world"
+path = "examples/2d/2d_viewport_to_world.rs"
+
+[package.metadata.example.2d_viewport_to_world]
+name = "2D Viewport To World"
+description = "Demonstrates how to use the `Camera::viewport_to_world_2d` method"
+category = "2D Rendering"
+wasm = true
+
 [[example]]
 name = "rotation"
 path = "examples/2d/rotation.rs"
@@ -467,6 +477,17 @@ description = "A scene showcasing the built-in 3D shapes"
 category = "3D Rendering"
 wasm = true
 
+[[example]]
+name = "3d_viewport_to_world"
+path = "examples/3d/3d_viewport_to_world.rs"
+doc-scrape-examples = true
+
+[package.metadata.example.3d_viewport_to_world]
+name = "3D Viewport To World"
+description = "Demonstrates how to use the `Camera::viewport_to_world` method"
+category = "3D Rendering"
+wasm = true
+
 [[example]]
 name = "generate_custom_mesh"
 path = "examples/3d/generate_custom_mesh.rs"
diff --git a/examples/2d/2d_viewport_to_world.rs b/examples/2d/2d_viewport_to_world.rs
new file mode 100644
index 0000000000000..8ed4f881f993f
--- /dev/null
+++ b/examples/2d/2d_viewport_to_world.rs
@@ -0,0 +1,34 @@
+//! This example demonstrates how to use the `Camera::viewport_to_world_2d` method.
+
+use bevy::prelude::*;
+
+fn main() {
+    App::new()
+        .add_plugins(DefaultPlugins)
+        .add_systems(Startup, setup)
+        .add_systems(Update, draw_cursor)
+        .run();
+}
+
+fn draw_cursor(
+    camera_query: Query<(&Camera, &GlobalTransform)>,
+    windows: Query<&Window>,
+    mut gizmos: Gizmos,
+) {
+    let (camera, camera_transform) = camera_query.single();
+
+    let Some(cursor_position) = windows.single().cursor_position() else {
+        return;
+    };
+
+    // Calculate a world position based on the cursor's position.
+    let Some(point) = camera.viewport_to_world_2d(camera_transform, cursor_position) else {
+        return;
+    };
+
+    gizmos.circle_2d(point, 10., Color::WHITE);
+}
+
+fn setup(mut commands: Commands) {
+    commands.spawn(Camera2dBundle::default());
+}
diff --git a/examples/3d/3d_viewport_to_world.rs b/examples/3d/3d_viewport_to_world.rs
new file mode 100644
index 0000000000000..d2c175197f57c
--- /dev/null
+++ b/examples/3d/3d_viewport_to_world.rs
@@ -0,0 +1,70 @@
+//! This example demonstrates how to use the `Camera::viewport_to_world` method.
+
+use bevy::prelude::*;
+
+fn main() {
+    App::new()
+        .add_plugins(DefaultPlugins)
+        .add_systems(Startup, setup)
+        .add_systems(Update, draw_cursor)
+        .run();
+}
+
+fn draw_cursor(
+    camera_query: Query<(&Camera, &GlobalTransform)>,
+    ground_query: Query<&GlobalTransform, With<Ground>>,
+    windows: Query<&Window>,
+    mut gizmos: Gizmos,
+) {
+    let (camera, camera_transform) = camera_query.single();
+    let ground = ground_query.single();
+
+    let Some(cursor_position) = windows.single().cursor_position() else {
+        return;
+    };
+
+    // Calculate a ray pointing from the camera into the world based on the cursor's position.
+    let Some(ray) = camera.viewport_to_world(camera_transform, cursor_position) else {
+        return;
+    };
+
+    // Calculate if and where the ray is hitting the ground plane.
+    let Some(distance) = ray.intersect_plane(ground.translation(), ground.up()) else {
+        return;
+    };
+    let point = ray.get_point(distance);
+
+    // Draw a circle just above the ground plane at that position.
+    gizmos.circle(point + ground.up() * 0.01, ground.up(), 0.2, Color::WHITE);
+}
+
+#[derive(Component)]
+struct Ground;
+
+fn setup(
+    mut commands: Commands,
+    mut meshes: ResMut<Assets<Mesh>>,
+    mut materials: ResMut<Assets<StandardMaterial>>,
+) {
+    // plane
+    commands.spawn((
+        PbrBundle {
+            mesh: meshes.add(shape::Plane::from_size(20.).into()),
+            material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
+            ..default()
+        },
+        Ground,
+    ));
+
+    // light
+    commands.spawn(DirectionalLightBundle {
+        transform: Transform::from_translation(Vec3::ONE).looking_at(Vec3::ZERO, Vec3::Y),
+        ..default()
+    });
+
+    // camera
+    commands.spawn(Camera3dBundle {
+        transform: Transform::from_xyz(15.0, 5.0, 15.0).looking_at(Vec3::ZERO, Vec3::Y),
+        ..default()
+    });
+}
diff --git a/examples/README.md b/examples/README.md
index d48c6646ae6e5..d345cd5707edd 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -95,6 +95,7 @@ Example | Description
 [2D Gizmos](../examples/2d/2d_gizmos.rs) | A scene showcasing 2D gizmos
 [2D Rotation](../examples/2d/rotation.rs) | Demonstrates rotating entities in 2D with quaternions
 [2D Shapes](../examples/2d/2d_shapes.rs) | Renders a rectangle, circle, and hexagon
+[2D Viewport To World](../examples/2d/2d_viewport_to_world.rs) | Demonstrates how to use the `Camera::viewport_to_world_2d` method
 [Custom glTF vertex attribute 2D](../examples/2d/custom_gltf_vertex_attribute.rs) | Renders a glTF mesh in 2D with a custom vertex attribute
 [Manual Mesh 2D](../examples/2d/mesh2d_manual.rs) | Renders a custom mesh "manually" with "mid-level" renderer apis
 [Mesh 2D](../examples/2d/mesh2d.rs) | Renders a 2d mesh
@@ -116,6 +117,7 @@ Example | Description
 [3D Gizmos](../examples/3d/3d_gizmos.rs) | A scene showcasing 3D gizmos
 [3D Scene](../examples/3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting
 [3D Shapes](../examples/3d/3d_shapes.rs) | A scene showcasing the built-in 3D shapes
+[3D Viewport To World](../examples/3d/3d_viewport_to_world.rs) | Demonstrates how to use the `Camera::viewport_to_world` method
 [Anti-aliasing](../examples/3d/anti_aliasing.rs) | Compares different anti-aliasing methods
 [Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect
 [Blend Modes](../examples/3d/blend_modes.rs) | Showcases different blend modes