ranim_core/primitives/
camera_frame.rs1use glam::{DMat4, DVec3, dvec2};
4
5use crate::{
6 prelude::{Alignable, Interpolatable},
7 primitives::{Primitive, Primitives},
8};
9
10#[derive(Clone, Debug, PartialEq)]
15pub struct CameraFrame {
16 pub pos: DVec3,
18 pub up: DVec3,
20 pub facing: DVec3,
22 pub scale: f64,
24 pub fovy: f64,
26 pub near: f64,
29 pub far: f64,
31 pub perspective_blend: f64,
33}
34
35impl Primitive for CameraFrame {
36 fn build_primitives<T: IntoIterator<Item = Self>>(iter: T) -> super::Primitives {
37 Primitives::CameraFrame(iter.into_iter().collect())
38 }
39}
40
41impl Interpolatable for CameraFrame {
42 fn lerp(&self, target: &Self, t: f64) -> Self {
43 Self {
44 pos: self.pos.lerp(target.pos, t),
45 up: self.up.lerp(target.up, t),
46 facing: self.facing.lerp(target.facing, t),
47 scale: self.scale.lerp(&target.scale, t),
48 fovy: self.fovy.lerp(&target.fovy, t),
49 near: self.near.lerp(&target.near, t),
50 far: self.far.lerp(&target.far, t),
51 perspective_blend: self
52 .perspective_blend
53 .lerp(&target.perspective_blend, t)
54 .clamp(0.0, 1.0),
55 }
56 }
57}
58
59impl Alignable for CameraFrame {
60 fn is_aligned(&self, _other: &Self) -> bool {
61 true
62 }
63 fn align_with(&mut self, _other: &mut Self) {}
64}
65
66impl Default for CameraFrame {
67 fn default() -> Self {
68 Self {
69 pos: DVec3::ZERO,
70 up: DVec3::Y,
71 facing: DVec3::NEG_Z,
72
73 scale: 1.0,
74 fovy: std::f64::consts::PI / 2.0,
75
76 near: -1000.0,
77 far: 1000.0,
78 perspective_blend: 0.0,
79 }
80 }
81}
82
83impl CameraFrame {
84 pub fn new() -> Self {
86 Self::default()
87 }
88}
89
90impl CameraFrame {
91 pub fn view_matrix(&self) -> DMat4 {
93 DMat4::look_at_rh(self.pos, self.pos + self.facing, self.up)
94 }
95
96 pub fn orthographic_mat(&self, frame_height: f64, aspect_ratio: f64) -> DMat4 {
98 let frame_size = dvec2(frame_height * aspect_ratio, frame_height);
99 let frame_size = frame_size * self.scale;
100 DMat4::orthographic_rh(
101 -frame_size.x / 2.0,
102 frame_size.x / 2.0,
103 -frame_size.y / 2.0,
104 frame_size.y / 2.0,
105 self.near,
106 self.far,
107 )
108 }
109
110 pub fn perspective_mat(&self, aspect_ratio: f64) -> DMat4 {
112 let near = self.near.max(0.1);
113 let far = self.far.max(near);
114 DMat4::perspective_rh(self.fovy, aspect_ratio, near, far)
115 }
116
117 pub fn projection_matrix(&self, frame_height: f64, aspect_ratio: f64) -> DMat4 {
119 self.orthographic_mat(frame_height, aspect_ratio)
120 .lerp(&self.perspective_mat(aspect_ratio), self.perspective_blend)
121 }
122
123 pub fn view_projection_matrix(&self, frame_height: f64, aspect_ratio: f64) -> DMat4 {
125 self.projection_matrix(frame_height, aspect_ratio) * self.view_matrix()
126 }
127}
128
129impl CameraFrame {
130 pub fn center_canvas_in_frame(
132 &mut self,
133 center: DVec3,
134 width: f64,
135 height: f64,
136 up: DVec3,
137 normal: DVec3,
138 aspect_ratio: f64,
139 ) -> &mut Self {
140 let canvas_ratio = height / width;
141 let up = up.normalize();
142 let normal = normal.normalize();
143
144 let height = if aspect_ratio > canvas_ratio {
145 height
146 } else {
147 width / aspect_ratio
148 };
149
150 let distance = height * 0.5 / (0.5 * self.fovy).tan();
151
152 self.up = up;
153 self.pos = center + normal * distance;
154 self.facing = -normal;
155 self
156 }
157}