11use  crate :: { 
2-     camera:: CameraProjection ,  prelude:: Image ,  render_asset:: RenderAssets , 
3-     render_resource:: TextureView ,  view:: ExtractedWindows , 
2+     camera:: CameraProjection , 
3+     prelude:: Image , 
4+     primitives:: { Line ,  Plane } , 
5+     render_asset:: RenderAssets , 
6+     render_resource:: TextureView , 
7+     view:: ExtractedWindows , 
48} ; 
59use  bevy_asset:: { AssetEvent ,  Assets ,  Handle } ; 
610use  bevy_ecs:: { 
@@ -12,7 +16,7 @@ use bevy_ecs::{
1216    reflect:: ReflectComponent , 
1317    system:: { QuerySet ,  Res } , 
1418} ; 
15- use  bevy_math:: { Mat4 ,  UVec2 ,  Vec2 ,  Vec3 } ; 
19+ use  bevy_math:: { Mat4 ,  UVec2 ,  Vec2 ,  Vec3 ,   Vec4 } ; 
1620use  bevy_reflect:: { Reflect ,  ReflectDeserialize } ; 
1721use  bevy_transform:: components:: GlobalTransform ; 
1822use  bevy_utils:: HashSet ; 
@@ -138,6 +142,74 @@ impl Camera {
138142            None 
139143        } 
140144    } 
145+ 
146+     /// Given a position in screen space, compute the world-space line that corresponds to it. 
147+ pub  fn  screen_to_world_ray ( 
148+         & self , 
149+         pos_screen :  Vec2 , 
150+         windows :  & Windows , 
151+         images :  & Assets < Image > , 
152+         camera_transform :  & GlobalTransform , 
153+     )  -> Line  { 
154+         let  camera_position = camera_transform. compute_matrix ( ) ; 
155+         let  window_size = self . target . get_logical_size ( windows,  images) . unwrap ( ) ; 
156+         let  projection_matrix = self . projection_matrix ; 
157+ 
158+         // Normalized device coordinate cursor position from (-1, -1, -1) to (1, 1, 1) 
159+         let  cursor_ndc = ( pos_screen / window_size)  *  2.0  - Vec2 :: from ( [ 1.0 ,  1.0 ] ) ; 
160+         let  cursor_pos_ndc_near:  Vec3  = cursor_ndc. extend ( -1.0 ) ; 
161+         let  cursor_pos_ndc_far:  Vec3  = cursor_ndc. extend ( 1.0 ) ; 
162+ 
163+         // Use near and far ndc points to generate a ray in world space 
164+         // This method is more robust than using the location of the camera as the start of 
165+         // the ray, because ortho cameras have a focal point at infinity! 
166+         let  ndc_to_world:  Mat4  = camera_position *  projection_matrix. inverse ( ) ; 
167+         let  cursor_pos_near:  Vec3  = ndc_to_world. project_point3 ( cursor_pos_ndc_near) ; 
168+         let  cursor_pos_far:  Vec3  = ndc_to_world. project_point3 ( cursor_pos_ndc_far) ; 
169+         let  ray_direction = cursor_pos_far - cursor_pos_near; 
170+         Line :: from_point_direction ( cursor_pos_near,  ray_direction) 
171+     } 
172+ 
173+     /// Given a position in screen space and a plane in world space, compute what point on the plane the point in screen space corresponds to. 
174+ /// In 2D, use `screen_to_point_2d`. 
175+ pub  fn  screen_to_point_on_plane ( 
176+         & self , 
177+         pos_screen :  Vec2 , 
178+         plane :  Plane , 
179+         windows :  & Windows , 
180+         images :  & Assets < Image > , 
181+         camera_transform :  & GlobalTransform , 
182+     )  -> Option < Vec3 >  { 
183+         let  world_ray = self . screen_to_world_ray ( pos_screen,  windows,  images,  camera_transform) ; 
184+         let  d = world_ray. point . dot ( plane. normal ( ) ) ; 
185+         if  d == 0.  { 
186+             None 
187+         }  else  { 
188+             let  diff = world_ray. point . extend ( 1.0 )  - plane. normal_d ( ) ; 
189+             let  p = diff. dot ( plane. normal_d ( ) ) ; 
190+             let  dist = p / d; 
191+             Some ( world_ray. point  - world_ray. direction  *  dist) 
192+         } 
193+     } 
194+ 
195+     /// Computes the world position for a given screen position. 
196+ /// The output will always be on the XY plane with Z at zero. It is designed for 2D, but also works with a 3D camera. 
197+ /// For more flexibility in 3D, consider `screen_to_point_on_plane`. 
198+ pub  fn  screen_to_point_2d ( 
199+         & self , 
200+         pos_screen :  Vec2 , 
201+         windows :  & Windows , 
202+         images :  & Assets < Image > , 
203+         camera_transform :  & GlobalTransform , 
204+     )  -> Option < Vec3 >  { 
205+         self . screen_to_point_on_plane ( 
206+             pos_screen, 
207+             Plane :: new ( Vec4 :: new ( 0. ,  0. ,  1. ,  0. ) ) , 
208+             windows, 
209+             images, 
210+             camera_transform, 
211+         ) 
212+     } 
141213} 
142214
143215#[ allow( clippy:: type_complexity) ]  
0 commit comments