use crate::mesh3d::Vec3;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Camera3D {
pub position: Vec3,
pub target: Vec3,
pub up: Vec3,
pub fov: f32,
pub near: f32,
pub far: f32,
pub aspect_ratio: f32,
}
impl Camera3D {
pub fn new(width: u32, height: u32) -> Self {
Self {
position: Vec3::new(0.0, 0.0, 5.0),
target: Vec3::zero(),
up: Vec3::new(0.0, 1.0, 0.0),
fov: 60.0,
near: 0.1,
far: 100.0,
aspect_ratio: width as f32 / height as f32,
}
}
pub fn look_at(&mut self, target: Vec3) {
self.target = target;
}
pub fn move_forward(&mut self, distance: f32) {
let forward = Vec3::new(
self.target.x - self.position.x,
self.target.y - self.position.y,
self.target.z - self.position.z,
)
.normalize();
self.position.x += forward.x * distance;
self.position.y += forward.y * distance;
self.position.z += forward.z * distance;
}
pub fn rotate_around_target(&mut self, angle_x: f32, angle_y: f32) {
let dx = self.position.x - self.target.x;
let dy = self.position.y - self.target.y;
let dz = self.position.z - self.target.z;
let cos_y = angle_y.cos();
let sin_y = angle_y.sin();
let new_x = dx * cos_y - dz * sin_y;
let new_z = dx * sin_y + dz * cos_y;
let cos_x = angle_x.cos();
let sin_x = angle_x.sin();
let new_y = dy * cos_x - new_z * sin_x;
let final_z = dy * sin_x + new_z * cos_x;
self.position.x = self.target.x + new_x;
self.position.y = self.target.y + new_y;
self.position.z = self.target.z + final_z;
}
pub fn project(&self, point: &Vec3, screen_width: u32, screen_height: u32) -> Option<(i32, i32, f32)> {
let dx = point.x - self.position.x;
let dy = point.y - self.position.y;
let dz = point.z - self.position.z;
if dz <= 0.0 {
return None;
}
let fov_rad = self.fov.to_radians();
let scale = (fov_rad / 2.0).tan();
let x = dx / (dz * scale * self.aspect_ratio);
let y = dy / (dz * scale);
let screen_x = ((x + 1.0) * 0.5 * screen_width as f32) as i32;
let screen_y = ((1.0 - y) * 0.5 * screen_height as f32) as i32;
Some((screen_x, screen_y, dz))
}
pub fn get_forward(&self) -> Vec3 {
(self.target - self.position).normalize()
}
pub fn get_right(&self) -> Vec3 {
self.get_forward().cross(&self.up).normalize()
}
pub fn get_up(&self) -> Vec3 {
self.get_right().cross(&self.get_forward()).normalize()
}
pub fn set_position(&mut self, position: Vec3) {
self.position = position;
}
}