Skip to content

Commit e41f9d8

Browse files
added rcamera functions into core::camera (#185)
* added rcamera functions into core::camera * minor formatting * formatting and docs
1 parent ccc0827 commit e41f9d8

File tree

2 files changed

+346
-7
lines changed

2 files changed

+346
-7
lines changed

raylib/src/core/camera.rs

Lines changed: 338 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
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};
56
use crate::core::RaylibHandle;
67
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+
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
2449
pub 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 {
87112
impl 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

112443
impl RaylibHandle {

0 commit comments

Comments
 (0)