Something went wrong. Try again.
HEXWORK - A Lantern in the Deep: 3D ASCII terminal roguelike RPG in Rust.
Entirely designed and built by Muse Spark 1.3 to evaluate if I like the model.
Something went wrong. Try again.
2.5 kB · 71 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172//! Shared perspective basis. Physical terminal aspect is W/(2H).use crate::camera::View;#[derive(Clone, Copy, Debug)]pub struct Projection { pub forward: [f32; 3], pub right: [f32; 3], pub up: [f32; 3], eye: [f32; 3], w: usize, h2: usize, vertical: f32, horizontal: f32,}pub const NEAR: f32 = 0.05;fn dot(a: [f32; 3], b: [f32; 3]) -> f32 { a[0] * b[0] + a[1] * b[1] + a[2] * b[2]}impl Projection { pub fn new(view: &View, w: usize, h: usize) -> Option<Self> { if w == 0 || h == 0 || h > usize::MAX / 2 || view.is_orthographic() || !view.eye.iter().all(|v| v.is_finite()) || !view.yaw.is_finite() || !view.pitch.is_finite() || !view.fov_deg.is_finite() || !(1.0..=75.0).contains(&view.fov_deg) { return None; } let (sy, cy) = view.yaw.sin_cos(); let (sp, cp) = view.pitch.sin_cos(); let vertical = (view.fov_deg.to_radians() / 2.0).tan(); Some(Self { forward: [cp * cy, cp * sy, sp], right: [-sy, cy, 0.0], up: [-sp * cy, -sp * sy, cp], eye: view.eye, w, h2: h * 2, vertical, horizontal: vertical * w as f32 / (h * 2) as f32, }) } pub fn ray(&self, x: usize, y: usize) -> [f32; 3] { let u = ((x as f32 + 0.5) / self.w as f32 * 2.0 - 1.0) * self.horizontal; let v = (1.0 - (y as f32 + 0.5) / self.h2 as f32 * 2.0) * self.vertical; let d = std::array::from_fn(|i| self.forward[i] + u * self.right[i] + v * self.up[i]); let norm = dot(d, d).sqrt(); d.map(|v| v / norm) } /// Continuous column/half-row and forward depth. Offscreen points are clipped. pub fn project(&self, point: [f32; 3]) -> Option<[f32; 3]> { if !point.iter().all(|v| v.is_finite()) { return None; } let d = std::array::from_fn(|i| point[i] - self.eye[i]); let depth = dot(d, self.forward); if depth < NEAR { return None; } let x = (dot(d, self.right) / (depth * self.horizontal) + 1.0) * 0.5 * self.w as f32; let y = (1.0 - dot(d, self.up) / (depth * self.vertical)) * 0.5 * self.h2 as f32; if x < 0.0 || y < 0.0 || x >= self.w as f32 || y >= self.h2 as f32 { None } else { Some([x, y, depth]) } }}