EAGL.Camera (eagl v0.9.0)
View SourceCamera system for 3D navigation and control.
This module provides a comprehensive camera implementation equivalent to the LearnOpenGL camera class, offering first-person style camera controls with mouse look, keyboard movement, and scroll zoom functionality.
Original C++ Source
This camera implementation is based on the original LearnOpenGL C++ camera class: https://github.com/JoeyDeVries/LearnOpenGL/tree/master/includes/learnopengl/camera.h
The design and functionality closely follow the C++ camera class tutorial from LearnOpenGL Chapter 7 - Camera, providing equivalent behaviour and API in Elixir.
Framework Adaptation Notes
Mouse Sensitivity Adaptation: The original LearnOpenGL tutorial uses a mouse sensitivity of 0.1f, which works well with GLFW's small mouse delta values. However, EAGL's windowing system reports pixel-level mouse movements, requiring a much smaller sensitivity (0.005) to achieve natural first-person camera feel.
This adaptation addresses the "world rotation" feeling that students might experience when following camera tutorials - the camera should feel like natural first-person movement, not like rotating the entire world coordinate system.
Features
- Euler Angle Camera: Uses yaw and pitch for orientation
- WASD Movement: Standard FPS-style keyboard controls
- Mouse Look: Mouse movement for camera rotation with natural sensitivity
- Scroll Zoom: Field of view adjustment via scroll wheel
- Pitch Constraints: Prevents camera flipping at extreme angles
- Delta Time Support: Frame-rate independent movement
Usage
# Create a camera at the origin looking down negative Z-axis
camera = EAGL.Camera.new()
# Create a camera at specific position
camera = EAGL.Camera.new(position: vec3(0.0, 0.0, 5.0))
# Process keyboard input (typically in handle_event/2)
camera = EAGL.Camera.process_keyboard(camera, :forward, delta_time)
# Process mouse movement (typically in handle_event/2)
camera = EAGL.Camera.process_mouse_movement(camera, x_offset, y_offset)
# Process scroll wheel (typically in handle_event/2)
camera = EAGL.Camera.process_mouse_scroll(camera, y_offset)
# Get view matrix for rendering
view_matrix = EAGL.Camera.get_view_matrix(camera)
Movement Directions
:forward
- Move in the direction the camera is facing (-Z by default):backward
- Move opposite to camera direction (+Z by default):left
- Strafe left (perpendicular to front vector):right
- Strafe right (perpendicular to front vector)
Euler Angles
- Yaw: Rotation around Y-axis (left/right look)
- Pitch: Rotation around X-axis (up/down look)
- Roll: Not used (always 0 for FPS-style camera)
Default yaw of -90° makes the camera look down negative Z-axis initially.
Educational Context
This camera implementation serves as the foundation for LearnOpenGL camera examples 7.4-7.6, demonstrating the progression from manual camera implementation to well-designed camera abstractions. The mouse sensitivity adaptation ensures that students experience natural first-person camera controls throughout the tutorial series.
Summary
Functions
Get the view matrix for this camera.
Create a new camera with default or custom parameters.
Process all keyboard input for FPS camera movement using direct key state checking.
Process keyboard input for camera movement.
Process all keyboard input for camera movement using direct key state checking.
Process mouse movement for camera look around.
Process mouse scroll wheel input for zoom control.
Types
@type mat4() :: EAGL.Math.mat4()
@type movement_direction() :: :forward | :backward | :left | :right
@type vec3() :: EAGL.Math.vec3()
Functions
Get the view matrix for this camera.
The view matrix transforms world coordinates to camera/view space, effectively moving the world to simulate camera movement.
Examples
camera = EAGL.Camera.new(position: vec3(0.0, 0.0, 3.0))
view = EAGL.Camera.get_view_matrix(camera)
# Use in shader
set_uniform(program, "view", view)
Create a new camera with default or custom parameters.
Options
:position
- Camera position (default: {0.0, 0.0, 0.0}):world_up
- World up vector (default: {0.0, 1.0, 0.0}):yaw
- Initial yaw angle in degrees (default: -90.0):pitch
- Initial pitch angle in degrees (default: 0.0):movement_speed
- Movement speed in units/second (default: 2.5):mouse_sensitivity
- Mouse sensitivity multiplier (default: 0.005, adapted from LearnOpenGL's 0.1 for natural feel):zoom
- Field of view in degrees (default: 45.0)
Examples
# Default camera at origin
camera = EAGL.Camera.new()
# Camera at specific position
camera = EAGL.Camera.new(position: vec3(5.0, 2.0, 5.0))
# Custom camera configuration
camera = EAGL.Camera.new(
position: vec3(0.0, 5.0, 10.0),
yaw: 180.0,
pitch: -30.0,
movement_speed: 5.0,
mouse_sensitivity: 0.01
)
Process all keyboard input for FPS camera movement using direct key state checking.
This function provides the same simplified approach as process_keyboard_input/2
but
constrains movement to the XZ plane for realistic first-person shooter camera behavior.
Movement vectors are projected horizontally to prevent "flying" when looking up/down.
Parameters
camera
- The camera structdelta_time
- Time since last frame in secondsground_level
- Y-coordinate to constrain camera position to
Key Mappings
W
(119) - Move forward (horizontally)A
(97) - Strafe left (horizontally)S
(115) - Move backward (horizontally)D
(100) - Strafe right (horizontally)
Examples
# Process FPS keyboard input at ground level 1.5
camera = EAGL.Camera.process_fps_keyboard_input(camera, delta_time, 1.5)
This approach maintains the Y-coordinate at the specified ground level regardless of camera pitch, providing natural ground-based navigation.
Process keyboard input for camera movement.
Moves the camera based on the specified direction and delta time. Movement is frame-rate independent when delta time is provided.
Parameters
camera
- The camera structdirection
- Movement direction (:forward
,:backward
,:left
,:right
)delta_time
- Time since last frame in seconds
Examples
# Forward movement (W key)
camera = EAGL.Camera.process_keyboard(camera, :forward, delta_time)
# Backward movement (S key)
camera = EAGL.Camera.process_keyboard(camera, :backward, delta_time)
# Strafe left (A key)
camera = EAGL.Camera.process_keyboard(camera, :left, delta_time)
# Strafe right (D key)
camera = EAGL.Camera.process_keyboard(camera, :right, delta_time)
Process all keyboard input for camera movement using direct key state checking.
This function checks the current state of WASD keys and applies movement
accordingly. It uses :wx_misc.getKeyState()
for reliable input detection,
providing the same approach as examples 7.2 and 7.3.
Parameters
camera
- The camera structdelta_time
- Time since last frame in seconds
Key Mappings
W
(119) - Move forwardA
(97) - Strafe leftS
(115) - Move backwardD
(100) - Strafe right
Examples
# Process all keyboard input at once
camera = EAGL.Camera.process_keyboard_input(camera, delta_time)
This approach is simpler and more reliable than individual key event handling, matching the proven pattern from examples 7.2 and 7.3.
Process mouse movement for camera look around.
Updates camera orientation based on mouse movement offsets. Constrains pitch to prevent camera flipping.
Parameters
camera
- The camera structx_offset
- Horizontal mouse movement offsety_offset
- Vertical mouse movement offsetconstrain_pitch
- Whether to constrain pitch (default: true)
Examples
# Typical mouse movement processing
camera = EAGL.Camera.process_mouse_movement(camera, x_offset, y_offset)
# Allow full pitch rotation (can cause gimbal lock)
camera = EAGL.Camera.process_mouse_movement(camera, x_offset, y_offset, false)
Process mouse scroll wheel input for zoom control.
Adjusts field of view based on scroll wheel movement. Constrains zoom to reasonable range (1.0 to 45.0 degrees).
Parameters
camera
- The camera structy_offset
- Scroll wheel offset (positive = zoom in, negative = zoom out)
Examples
# Zoom in (positive offset, smaller FOV)
camera = EAGL.Camera.process_mouse_scroll(camera, 1.0)
# Zoom out (negative offset, larger FOV)
camera = EAGL.Camera.process_mouse_scroll(camera, -1.0)