11//! Utility code for using Raylib [`Camera3D`] and [`Camera2D`]
2- use raylib_sys:: CameraMode ;
2+ use raylib_sys:: { CameraMode , KeyboardKey , MouseButton , GamepadAxis } ;
3+ use raylib_sys:: { RL_CULL_DISTANCE_NEAR , RL_CULL_DISTANCE_FAR , DEG2RAD } ;
34
4- use crate :: core:: math:: { Vector2 , Vector3 } ;
5+ use crate :: core:: math:: { Vector2 , Vector3 , Quaternion , Matrix } ;
56use crate :: core:: RaylibHandle ;
67use crate :: ffi;
8+ pub use ffi:: CameraProjection ;
9+
10+ const CAMERA_CULL_DISTANCE_NEAR : f64 = RL_CULL_DISTANCE_NEAR ;
11+ const CAMERA_CULL_DISTANCE_FAR : f64 = RL_CULL_DISTANCE_FAR ;
12+
13+ const CAMERA_MOVE_SPEED : f32 = 0.09 ;
14+ const CAMERA_ROTATION_SPEED : f32 = 0.03 ;
15+ const CAMERA_PAN_SPEED : f32 = 0.2 ;
16+
17+ // Camera mouse movement sensitivity
18+ const CAMERA_MOUSE_MOVE_SENSITIVITY : f32 = 0.003 ;
19+ const CAMERA_MOUSE_SCROLL_SENSITIVITY : f32 = 1.5 ;
20+
21+ // Radians per second
22+ const CAMERA_ORBITAL_SPEED : f32 = 0.5 ;
23+
24+
25+ const CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER : f32 = 8.0 ;
26+ const CAMERA_FIRST_PERSON_STEP_DIVIDER : f32 = 30.0 ;
27+ const CAMERA_FIRST_PERSON_WAVING_DIVIDER : f32 = 200.0 ;
28+
29+ // PLAYER (used by camera)
30+ const PLAYER_MOVEMENT_SENSITIVITY : f32 = 20.0 ;
31+
732
833/// Camera, defines position/orientation in 3d space
934#[ repr( C ) ]
@@ -18,7 +43,7 @@ pub struct Camera3D {
1843 /// Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic
1944 pub fovy : f32 ,
2045 /// Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
21- projection_ : ffi :: CameraProjection ,
46+ pub projection : CameraProjection ,
2247}
2348/// Camera type fallback, defaults to Camera3D
2449pub type Camera = Camera3D ;
@@ -42,7 +67,7 @@ impl Into<ffi::Camera3D> for &Camera3D {
4267 target : self . target . into ( ) ,
4368 up : self . up . into ( ) ,
4469 fovy : self . fovy ,
45- projection : ( self . projection_ as u32 ) as i32 ,
70+ projection : ( self . projection as u32 ) as i32 ,
4671 }
4772 }
4873}
@@ -87,7 +112,7 @@ impl Into<ffi::Camera2D> for &Camera2D {
87112impl Camera3D {
88113 /// Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
89114 pub const fn camera_type ( & self ) -> crate :: consts:: CameraProjection {
90- unsafe { std:: mem:: transmute_copy ( & self . projection_ ) }
115+ unsafe { std:: mem:: transmute_copy ( & self . projection ) }
91116 }
92117 /// Create a perspective camera.
93118 /// fovy is in degrees
@@ -97,16 +122,322 @@ impl Camera3D {
97122 target,
98123 up,
99124 fovy,
100- projection_ : ffi :: CameraProjection :: CAMERA_PERSPECTIVE ,
125+ projection : CameraProjection :: CAMERA_PERSPECTIVE ,
101126 }
102127 }
103128 /// Create a orthographic camera.
104129 /// fovy is in degrees
105130 pub fn orthographic ( position : Vector3 , target : Vector3 , up : Vector3 , fovy : f32 ) -> Camera3D {
106131 let mut c = Self :: perspective ( position, target, up, fovy) ;
107- c. projection_ = ffi :: CameraProjection :: CAMERA_ORTHOGRAPHIC ;
132+ c. projection = CameraProjection :: CAMERA_ORTHOGRAPHIC ;
108133 c
109134 }
135+ /// Returns the cameras forward vector (normalized)
136+ pub fn get_forward ( & self ) -> Vector3 {
137+ ( self . target - self . position ) . normalized ( )
138+ }
139+
140+ /// Returns the cameras up vector (normalized)
141+ /// Note: The up vector might not be perpendicular to the forward vector
142+ pub fn get_up ( & self ) -> Vector3 {
143+ self . up . normalized ( )
144+ }
145+
146+ /// Returns the cameras right vector (normalized)
147+ pub fn get_right ( & self ) -> Vector3 {
148+ let forward = self . get_forward ( ) ;
149+ let up = self . get_up ( ) ;
150+
151+ forward. cross ( up)
152+ }
153+
154+ /// Moves the camera in its forward direction
155+ pub fn move_forward ( & mut self , distance : f32 , move_in_world_plane : bool ) {
156+ let mut forward = self . get_forward ( ) ;
157+
158+ if move_in_world_plane
159+ {
160+ // Project vector onto world plane
161+ forward. y = 0. ;
162+ forward. normalize ( ) ;
163+ }
164+
165+ // Scale by distance
166+ forward. scale ( distance) ;
167+
168+ // Move position and target
169+ self . position = self . position + forward;
170+ self . target = self . target + forward;
171+ }
172+
173+ /// Moves the camera in its up direction
174+ pub fn move_up ( & mut self , distance : f32 ) {
175+ let mut up = self . get_up ( ) ;
176+
177+ // Scale by distance
178+ up. scale ( distance) ;
179+
180+ // Move position and target
181+ self . position = self . position + up;
182+ self . target = self . target + up;
183+ }
184+
185+ /// Moves the camera target in its current right direction
186+ pub fn move_right ( & mut self , distance : f32 , move_in_world_plane : bool ) {
187+ let mut right = self . get_right ( ) ;
188+
189+ if move_in_world_plane
190+ {
191+ // Project vector onto world plane
192+ right. y = 0. ;
193+ right. normalize ( ) ;
194+ }
195+
196+ // Scale by distance
197+ right. scale ( distance) ;
198+
199+ // Move position and target
200+ self . position = self . position + right;
201+ self . target = self . target + right;
202+ }
203+
204+ /// Moves the camera position closer/farther to/from the camera target
205+ pub fn move_to_target ( & mut self , delta : f32 ) {
206+ let mut distance = self . position . distance_to ( self . target ) ;
207+
208+ // Apply delta
209+ distance += delta;
210+
211+ // Distance must be greater than 0
212+ if distance <= 0. { distance = 0.001 ; }
213+
214+ // Set new distance by moving the position along the forward vector
215+ let forward = self . get_forward ( ) ;
216+ self . position = self . target + forward. scale_by ( distance) ;
217+ }
218+
219+ /// Rotates the camera around its up vector
220+ /// Yaw is "looking left and right"
221+ /// If rotateAroundTarget is false, the camera rotates around its position
222+ /// Note: angle must be provided in radians
223+ pub fn set_yaw ( & mut self , angle : f32 , rotate_around_target : bool ) {
224+ // Rotation axis
225+ let up = self . get_up ( ) ;
226+
227+ // View vector
228+ let target_position = self . target - self . position ;
229+
230+ // Rotate view vector around up axis
231+ let target_position = target_position. rotate_by ( Quaternion :: from_axis_angle ( up, angle) ) ;
232+
233+ if rotate_around_target
234+ {
235+ // Move position relative to target
236+ self . position = self . target - target_position;
237+ }
238+ else // rotate around camera.position
239+ {
240+ // Move target relative to position
241+ self . target = self . position + target_position;
242+ }
243+ }
244+
245+ /// Rotates the camera around its right vector, pitch is "looking up and down"
246+ /// - lockView prevents camera overrotation (aka "somersaults")
247+ /// - rotateAroundTarget defines if rotation is around target or around its position
248+ /// - rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE)
249+ /// NOTE: angle must be provided in radians
250+ pub fn set_pitch ( & mut self , mut angle : f32 , lock_view : bool , rotate_around_target : bool , rotate_up : bool ) {
251+ // Up direction
252+ let up = self . get_up ( ) ;
253+
254+ // View vector
255+ let mut target_position = self . target - self . position ;
256+
257+ if lock_view {
258+ // In these camera modes we clamp the Pitch angle
259+ // to allow only viewing straight up or down.
260+
261+ // Clamp view up
262+ let mut max_angle_up = up. angle_to ( target_position) ;
263+ max_angle_up -= 0.001 ; // avoid numerical errors
264+ if angle > max_angle_up { angle = max_angle_up } ;
265+
266+ // Clamp view down
267+ let mut max_angle_down = ( -up) . angle_to ( target_position) ;
268+ max_angle_down *= -1. ; // downwards angle is negative
269+ max_angle_down += 0.001 ; // avoid numerical errors
270+ if angle < max_angle_down { angle = max_angle_down } ;
271+ }
272+
273+ // Rotation axis
274+ let right = self . get_right ( ) ;
275+
276+ // Rotate view vector around right axis
277+ target_position = target_position. rotate_by ( Quaternion :: from_axis_angle ( right, angle) ) ;
278+
279+ if rotate_around_target
280+ {
281+ // Move position relative to target
282+ self . position = self . target - target_position;
283+ }
284+ else // rotate around camera.position
285+ {
286+ // Move target relative to position
287+ self . target = self . position + target_position;
288+ }
289+
290+ if rotate_up
291+ {
292+ // Rotate up direction around right axis
293+ self . up = self . up . rotate_by ( Quaternion :: from_axis_angle ( right, angle) ) ;
294+ }
295+ }
296+
297+ /// Rotates the camera around its forward vector
298+ /// Roll is "turning your head sideways to the left or right"
299+ /// Note: angle must be provided in radians
300+ pub fn set_roll ( & mut self , angle : f32 ) {
301+ // Rotation axis
302+ let forward = self . get_forward ( ) ;
303+
304+ // Rotate up direction around forward axis
305+ self . up = self . up . rotate_by ( Quaternion :: from_axis_angle ( forward, angle) ) ;
306+ }
307+
308+ /// Returns the camera view matrix
309+ pub fn get_view_matrix ( & self ) -> Matrix {
310+ Matrix :: look_at ( self . position , self . target , self . up )
311+ }
312+
313+ /// Returns the camera projection matrix
314+ pub fn get_projection_matrix ( & self , aspect : f32 ) -> Matrix {
315+ if self . projection == CameraProjection :: CAMERA_PERSPECTIVE
316+ {
317+ return Matrix :: perspective ( self . fovy * DEG2RAD as f32 , aspect, CAMERA_CULL_DISTANCE_NEAR as f32 , CAMERA_CULL_DISTANCE_FAR as f32 ) ;
318+ }
319+ else if self . projection == CameraProjection :: CAMERA_ORTHOGRAPHIC
320+ {
321+ let top = self . fovy /2.0 ;
322+ let right = top* aspect;
323+
324+ return Matrix :: ortho ( -right, right, -top, top, CAMERA_CULL_DISTANCE_NEAR as f32 , CAMERA_CULL_DISTANCE_FAR as f32 ) ;
325+ }
326+
327+ return Matrix :: identity ( ) ;
328+ }
329+
330+ /// Update camera position for selected mode
331+ /// Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM
332+ pub fn update_camera ( & mut self , rl : & mut RaylibHandle , mode : CameraMode ) {
333+ let mouse_position_delta = rl. get_mouse_delta ( ) ;
334+
335+ let move_in_world_plane = ( mode == CameraMode :: CAMERA_FIRST_PERSON ) || ( mode == CameraMode :: CAMERA_THIRD_PERSON ) ;
336+ let rotate_around_target = ( mode == CameraMode :: CAMERA_THIRD_PERSON ) || ( mode == CameraMode :: CAMERA_ORBITAL ) ;
337+ let lock_view = ( mode == CameraMode :: CAMERA_FIRST_PERSON ) || ( mode == CameraMode :: CAMERA_THIRD_PERSON ) || ( mode == CameraMode :: CAMERA_ORBITAL ) ;
338+ let rotate_up = false ;
339+
340+ if mode == CameraMode :: CAMERA_ORBITAL
341+ {
342+ // Orbital can just orbit
343+ let rotation = Matrix :: rotate ( self . get_up ( ) , CAMERA_ORBITAL_SPEED * rl. get_frame_time ( ) ) ;
344+ let mut view = self . position - self . target ;
345+ view. transform ( rotation) ;
346+ self . position = self . target + view;
347+ }
348+ else
349+ {
350+ // Camera rotation
351+ if rl. is_key_down ( KeyboardKey :: KEY_DOWN ) { self . set_pitch ( -CAMERA_ROTATION_SPEED , lock_view, rotate_around_target, rotate_up) ; }
352+ if rl. is_key_down ( KeyboardKey :: KEY_UP ) { self . set_pitch ( CAMERA_ROTATION_SPEED , lock_view, rotate_around_target, rotate_up) ; }
353+ if rl. is_key_down ( KeyboardKey :: KEY_RIGHT ) { self . set_yaw ( -CAMERA_ROTATION_SPEED , rotate_around_target) ; }
354+ if rl. is_key_down ( KeyboardKey :: KEY_LEFT ) { self . set_yaw ( CAMERA_ROTATION_SPEED , rotate_around_target) ; }
355+ if rl. is_key_down ( KeyboardKey :: KEY_Q ) { self . set_roll ( -CAMERA_ROTATION_SPEED ) ; }
356+ if rl. is_key_down ( KeyboardKey :: KEY_E ) { self . set_roll ( CAMERA_ROTATION_SPEED ) ; }
357+
358+ // Camera movement
359+ if !rl. is_gamepad_available ( 0 )
360+ {
361+ // Camera pan (for CAMERA_FREE)
362+ if ( mode == CameraMode :: CAMERA_FREE ) && ( rl. is_mouse_button_down ( MouseButton :: MOUSE_BUTTON_MIDDLE ) )
363+ {
364+ let mouse_delta = rl. get_mouse_delta ( ) ;
365+ if mouse_delta. x > 0.0 { self . move_right ( CAMERA_PAN_SPEED , move_in_world_plane) ; }
366+ if mouse_delta. x < 0.0 { self . move_right ( -CAMERA_PAN_SPEED , move_in_world_plane) ; }
367+ if mouse_delta. y > 0.0 { self . move_up ( -CAMERA_PAN_SPEED ) ; }
368+ if mouse_delta. y < 0.0 { self . move_up ( CAMERA_PAN_SPEED ) ; }
369+ }
370+ else
371+ {
372+ // Mouse support
373+ self . set_yaw ( -mouse_position_delta. x * CAMERA_MOUSE_MOVE_SENSITIVITY , rotate_around_target) ;
374+ self . set_pitch ( -mouse_position_delta. y * CAMERA_MOUSE_MOVE_SENSITIVITY , lock_view, rotate_around_target, rotate_up) ;
375+ }
376+
377+ // Keyboard support
378+ if rl. is_key_down ( KeyboardKey :: KEY_W ) { self . move_forward ( CAMERA_MOVE_SPEED , move_in_world_plane) ; }
379+ if rl. is_key_down ( KeyboardKey :: KEY_A ) { self . move_right ( -CAMERA_MOVE_SPEED , move_in_world_plane) ; }
380+ if rl. is_key_down ( KeyboardKey :: KEY_S ) { self . move_forward ( -CAMERA_MOVE_SPEED , move_in_world_plane) ; }
381+ if rl. is_key_down ( KeyboardKey :: KEY_D ) { self . move_right ( CAMERA_MOVE_SPEED , move_in_world_plane) ; }
382+ }
383+ else
384+ {
385+ // Gamepad controller support
386+ self . set_yaw ( -( rl. get_gamepad_axis_movement ( 0 , GamepadAxis :: GAMEPAD_AXIS_RIGHT_X ) * 2. ) * CAMERA_MOUSE_MOVE_SENSITIVITY , rotate_around_target) ;
387+ self . set_pitch ( -( rl. get_gamepad_axis_movement ( 0 , GamepadAxis :: GAMEPAD_AXIS_RIGHT_Y ) * 2. ) * CAMERA_MOUSE_MOVE_SENSITIVITY , lock_view, rotate_around_target, rotate_up) ;
388+
389+ if rl. get_gamepad_axis_movement ( 0 , GamepadAxis :: GAMEPAD_AXIS_LEFT_Y ) <= -0.25 { self . move_forward ( CAMERA_MOVE_SPEED , move_in_world_plane) ; }
390+ if rl. get_gamepad_axis_movement ( 0 , GamepadAxis :: GAMEPAD_AXIS_LEFT_X ) <= -0.25 { self . move_right ( -CAMERA_MOVE_SPEED , move_in_world_plane) ; }
391+ if rl. get_gamepad_axis_movement ( 0 , GamepadAxis :: GAMEPAD_AXIS_LEFT_Y ) >= 0.25 { self . move_forward ( -CAMERA_MOVE_SPEED , move_in_world_plane) ; }
392+ if rl. get_gamepad_axis_movement ( 0 , GamepadAxis :: GAMEPAD_AXIS_LEFT_X ) >= 0.25 { self . move_right ( CAMERA_MOVE_SPEED , move_in_world_plane) ; }
393+ }
394+
395+ if mode == CameraMode :: CAMERA_FREE
396+ {
397+ if rl. is_key_down ( KeyboardKey :: KEY_SPACE ) { self . move_up ( CAMERA_MOVE_SPEED ) ; }
398+ if rl. is_key_down ( KeyboardKey :: KEY_LEFT_CONTROL ) { self . move_up ( -CAMERA_MOVE_SPEED ) ; }
399+ }
400+ }
401+
402+ if ( mode == CameraMode :: CAMERA_THIRD_PERSON ) || ( mode == CameraMode :: CAMERA_ORBITAL ) || ( mode == CameraMode :: CAMERA_FREE )
403+ {
404+ // Zoom target distance
405+ self . move_to_target ( -rl. get_mouse_wheel_move ( ) ) ;
406+ if rl. is_key_pressed ( KeyboardKey :: KEY_KP_SUBTRACT ) { self . move_to_target ( 2.0 ) ; }
407+ if rl. is_key_pressed ( KeyboardKey :: KEY_KP_ADD ) { self . move_to_target ( -2.0 ) ; }
408+ }
409+ }
410+
411+ /// Update camera movement, movement/rotation values should be provided by user
412+ pub fn update_camera_pro ( & mut self , movement : Vector3 , rotation : Vector3 , zoom : f32 ) {
413+ // Required values
414+ // movement.x - Move forward/backward
415+ // movement.y - Move right/left
416+ // movement.z - Move up/down
417+ // rotation.x - yaw
418+ // rotation.y - pitch
419+ // rotation.z - roll
420+ // zoom - Move towards target
421+
422+ let lock_view = true ;
423+ let rotate_around_target = false ;
424+ let rotate_up = false ;
425+ let move_in_world_plane = true ;
426+
427+ // Camera rotation
428+ self . set_pitch ( -rotation. y * DEG2RAD as f32 , lock_view, rotate_around_target, rotate_up) ;
429+ self . set_yaw ( -rotation. x * DEG2RAD as f32 , rotate_around_target) ;
430+ self . set_roll ( rotation. z * DEG2RAD as f32 ) ;
431+
432+ // Camera movement
433+ self . move_forward ( movement. x , move_in_world_plane) ;
434+ self . move_right ( movement. y , move_in_world_plane) ;
435+ self . move_up ( movement. z ) ;
436+
437+ // Zoom target distance
438+ self . move_to_target ( zoom) ;
439+ }
440+
110441}
111442
112443impl RaylibHandle {
0 commit comments