diff --git a/src/camera.rs b/src/camera.rs
index 4d3311c..cda1641 100644
--- a/src/camera.rs
+++ b/src/camera.rs
@@ -16,6 +16,39 @@ pub(crate) fn projection(fovy: f32, aspect: f32, base_distance: f32) -> Mat4 {
Mat4::perspective_rh(fovy, aspect, 0.1 * base_distance, 10.0 * base_distance)
}
+pub(crate) struct Camera {
+ pub y_frac: f32,
+ pub y_angle: f32,
+ pub y_range: f32,
+ pub z_min: f32,
+ pub z_max: f32,
+}
+
+impl Camera {
+ pub fn clamp_z_min(&mut self, min_z: f32) -> &mut Self {
+ let (s, c) = (0.5 * self.y_angle).sin_cos();
+ // let distance = 0.5 * self.y_len * c / s;
+ let distance_scaled = 0.5 * self.y_range * c;
+ if distance_scaled + self.z_min * s < min_z * s {
+ self.z_min = min_z - distance_scaled / s;
+ }
+ self
+ }
+
+ pub fn to_mat4(&self) -> Mat4 {
+ let tan_fov = (0.5 * self.y_angle).tan();
+ let z_range = self.z_max - self.z_min;
+ let y_far = 0.5 * self.y_range + self.z_max * tan_fov;
+ let u = -y_far / z_range;
+ Mat4::from_cols_array_2d(&[
+ [self.y_frac, 0., 0., 0.],
+ [0., 1., 0., 0.],
+ [0., 0., u, -tan_fov],
+ [0., 0., u * self.z_min, 0.5 * self.y_range],
+ ])
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -76,4 +109,33 @@ mod tests {
panic!();
}
}
+
+ #[test]
+ fn test_proj() {
+ for (y_range, y_angle_deg) in [
+ (10.000000, 53.130102f32),
+ (11.547005, 60.000000f32),
+ (20.000000, 90.000000f32),
+ (34.641016, 120.000000f32),
+ (40.000000, 126.869898f32),
+ ] {
+ let y_angle = y_angle_deg.to_radians();
+ let camera = Camera {
+ y_frac: 0.5,
+ y_angle,
+ y_range,
+ z_min: -9.0,
+ z_max: 90.0,
+ };
+ let actual = camera.to_mat4();
+ let expected = projection(y_angle, 1.0 / camera.y_frac, 10.0)
+ * Affine3A::from_translation(vec3(0., 0., -10.0));
+ let c = expected.x_axis.x / actual.x_axis.x;
+ assert!(
+ (c * actual).abs_diff_eq(expected, 1e-3),
+ "projection at {y_angle_deg}d is wrong\nexpected: {expected}\nexpected: {}\ncomputed: {actual}",
+ expected / c,
+ );
+ }
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 1eae7df..11b45d5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -108,15 +108,22 @@ impl Core {
}
fn render(&self, output: &wgpu::Texture, args: &RedrawArgs) {
- let aspect = {
+ let y_frac = {
let size = output.size();
let w = size.width as f32;
let h = size.height as f32;
- w / h
+ h / w
};
- let distance = 0.5 * args.cam_view_y / (0.5 * args.cam_view_angle).tan();
- let view = Mat4::from(camera::view(args.cam_yaw, args.cam_pitch, distance));
- let proj = camera::projection(args.cam_view_angle, aspect, distance);
+ let proj = camera::Camera {
+ y_frac,
+ y_angle: args.cam_view_angle,
+ y_range: args.cam_view_y,
+ z_min: -0.5 * args.cam_view_y,
+ z_max: 0.5 * args.cam_view_y,
+ }
+ .clamp_z_min(0.01 * args.cam_view_y)
+ .to_mat4();
+ let view = Mat4::from_mat3(camera::rotation(-args.cam_yaw, -args.cam_pitch));
self.mesh_pipeline.set_params(
&self.queue,
render::mesh::Params {
diff --git a/ui/src/main_window.ui b/ui/src/main_window.ui
index 19d28a8..748c5e2 100644
--- a/ui/src/main_window.ui
+++ b/ui/src/main_window.ui
@@ -143,7 +143,7 @@
1
- 15.000000000000000
+ 0.000000000000000
150.000000000000000