wormhole-engine 0.1.0

A portable, no-editor game engine with Rust core and Crystal scripting
Documentation
// Camera System - Handles view and projection matrices

use cgmath::*;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct Camera {
    // Position in world space
    position: Point3<f32>,
    
    // Rotation (pitch, yaw, roll in radians)
    pitch: f32,  // Rotation around X axis (up/down)
    yaw: f32,    // Rotation around Y axis (left/right)
    
    // Projection settings
    fov: f32,              // Field of view in degrees
    aspect_ratio: f32,     // Width / height
    near: f32,             // Near clipping plane
    far: f32,              // Far clipping plane
    
    // Computed matrices (cached, recalculated every frame)
    view_matrix: Matrix4<f32>,
    projection_matrix: Matrix4<f32>,
}

impl Camera {
    pub fn new(width: u32, height: u32) -> Self {
        let aspect = width as f32 / height as f32;
        let mut camera = Self {
            position: Point3::new(0.0, 0.0, 5.0),
            pitch: 0.0,
            yaw: -std::f32::consts::PI / 2.0,  // Start looking along -Z (toward origin/cube)
            fov: 45.0,
            aspect_ratio: aspect,
            near: 0.1,
            far: 100.0,
            view_matrix: Matrix4::identity(),
            projection_matrix: Matrix4::identity(),
        };
        camera.update_matrices();
        camera
    }

    /// Update camera matrices (always recalculates)
    fn update_matrices(&mut self) {
        // Calculate forward vector from pitch and yaw
        // Yaw rotates around Y axis (left/right)
        // Pitch rotates around X axis (up/down)
        let forward = Vector3::new(
            self.yaw.cos() * self.pitch.cos(),
            self.pitch.sin(),
            self.yaw.sin() * self.pitch.cos(),
        ).normalize();
        
        // Calculate target point (camera position + forward direction)
        let target = self.position + forward;
        
        // Calculate up vector (perpendicular to forward, preferring world up)
        // For simplicity, use world up (Y axis) for now
        // In a more advanced system, we might want to handle roll
        let up = Vector3::unit_y();
        
        // Calculate view matrix using look_at
        self.view_matrix = Matrix4::look_at_rh(
            self.position,
            target,
            up,
        );

        // Calculate projection matrix
        self.projection_matrix = perspective(
            Deg(self.fov),
            self.aspect_ratio,
            self.near,
            self.far,
        );
    }

    /// Get the view matrix (updates matrices if needed)
    pub fn view_matrix(&mut self) -> Matrix4<f32> {
        self.update_matrices();
        self.view_matrix
    }

    /// Get the projection matrix (assumes view_matrix was called first to update matrices)
    pub fn projection_matrix(&mut self) -> Matrix4<f32> {
        // Matrices are already updated by view_matrix() call, just return cached value
        self.projection_matrix
    }

    /// Set camera position
    pub fn set_position(&mut self, x: f32, y: f32, z: f32) {
        self.position = Point3::new(x, y, z);
    }

    /// Get camera position
    pub fn position(&self) -> (f32, f32, f32) {
        (self.position.x, self.position.y, self.position.z)
    }

    /// Set camera rotation (pitch and yaw in radians)
    pub fn set_rotation(&mut self, pitch: f32, yaw: f32) {
        self.pitch = pitch;
        self.yaw = yaw;
    }

    /// Get camera rotation
    pub fn rotation(&self) -> (f32, f32) {
        (self.pitch, self.yaw)
    }

    /// Set field of view (in degrees)
    pub fn set_fov(&mut self, fov: f32) {
        self.fov = fov;
    }

    /// Get field of view
    pub fn fov(&self) -> f32 {
        self.fov
    }

    /// Update aspect ratio (call when window is resized)
    pub fn set_aspect_ratio(&mut self, width: u32, height: u32) {
        self.aspect_ratio = width as f32 / height as f32;
    }

    /// Move camera relative to its current orientation
    pub fn translate(&mut self, dx: f32, dy: f32, dz: f32) {
        // Calculate forward vector from pitch and yaw (same as in update_matrices)
        let forward = Vector3::new(
            self.yaw.cos() * self.pitch.cos(),
            self.pitch.sin(),
            self.yaw.sin() * self.pitch.cos(),
        ).normalize();
        
        // Calculate right vector (forward cross world up)
        let right = forward.cross(Vector3::unit_y()).normalize();
        
        // Calculate up vector (right cross forward)
        let up = right.cross(forward).normalize();
        
        // Translate in camera space
        // dx = left/right, dy = up/down, dz = forward/back
        let movement = right * dx + up * dy + forward * dz;
        self.position += movement;
    }
}

// Thread-safe camera for FFI
pub type CameraState = Arc<Mutex<Camera>>;