1
1
//! 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 } ;
3
4
4
- use crate :: core:: math:: { Vector2 , Vector3 } ;
5
+ use crate :: core:: math:: { Vector2 , Vector3 , Quaternion , Matrix } ;
5
6
use crate :: core:: RaylibHandle ;
6
7
use 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
+
7
32
8
33
/// Camera, defines position/orientation in 3d space
9
34
#[ repr( C ) ]
@@ -18,7 +43,7 @@ pub struct Camera3D {
18
43
/// Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic
19
44
pub fovy : f32 ,
20
45
/// Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
21
- projection_ : ffi :: CameraProjection ,
46
+ pub projection : CameraProjection ,
22
47
}
23
48
/// Camera type fallback, defaults to Camera3D
24
49
pub type Camera = Camera3D ;
@@ -42,7 +67,7 @@ impl Into<ffi::Camera3D> for &Camera3D {
42
67
target : self . target . into ( ) ,
43
68
up : self . up . into ( ) ,
44
69
fovy : self . fovy ,
45
- projection : ( self . projection_ as u32 ) as i32 ,
70
+ projection : ( self . projection as u32 ) as i32 ,
46
71
}
47
72
}
48
73
}
@@ -87,7 +112,7 @@ impl Into<ffi::Camera2D> for &Camera2D {
87
112
impl Camera3D {
88
113
/// Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
89
114
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 ) }
91
116
}
92
117
/// Create a perspective camera.
93
118
/// fovy is in degrees
@@ -97,16 +122,322 @@ impl Camera3D {
97
122
target,
98
123
up,
99
124
fovy,
100
- projection_ : ffi :: CameraProjection :: CAMERA_PERSPECTIVE ,
125
+ projection : CameraProjection :: CAMERA_PERSPECTIVE ,
101
126
}
102
127
}
103
128
/// Create a orthographic camera.
104
129
/// fovy is in degrees
105
130
pub fn orthographic ( position : Vector3 , target : Vector3 , up : Vector3 , fovy : f32 ) -> Camera3D {
106
131
let mut c = Self :: perspective ( position, target, up, fovy) ;
107
- c. projection_ = ffi :: CameraProjection :: CAMERA_ORTHOGRAPHIC ;
132
+ c. projection = CameraProjection :: CAMERA_ORTHOGRAPHIC ;
108
133
c
109
134
}
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
+
110
441
}
111
442
112
443
impl RaylibHandle {
0 commit comments