From 647d1dcc9c4f6705657aed5595671d005de6ed57 Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 May 2019 17:17:57 +0200 Subject: [PATCH 1/2] Smoother and terrain-aware camera Former-commit-id: 46b4e90d03792c9efb7d6b728c0d0cd60b1f5058 --- common/src/ray.rs | 14 +++++++--- voxygen/src/scene/camera.rs | 54 +++++++++++++++++++++++++++++++++---- voxygen/src/scene/mod.rs | 7 +++-- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/common/src/ray.rs b/common/src/ray.rs index 1191639816..219b5a2c0e 100644 --- a/common/src/ray.rs +++ b/common/src/ray.rs @@ -38,22 +38,28 @@ impl<'a, V: ReadVol, F: RayUntil> Ray<'a, V, F> { let mut dist = 0.0; let dir = (self.to - self.from).normalized(); + let max = (self.to - self.from).magnitude(); let mut pos = self.from; - let mut ipos = pos.map(|e| e as i32); + let mut ipos = pos.map(|e| e.floor() as i32); for _ in 0..self.max_iter { pos = self.from + dir * dist; - ipos = pos.map(|e| e as i32); + ipos = pos.map(|e| e.floor() as i32); match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) { Ok((vox, true)) => return (dist, Ok(Some(vox))), - Ok((_, false)) => {} + Ok((_, false)) => {}, Err(err) => return (dist, Err(err)), } + // Allow one iteration above max + if dist > max { + break; + } + let deltas = - (dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) - pos.map(|e| e.fract())) / dir; + (dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) - pos.map(|e| e.abs().fract())) / dir; dist += deltas.reduce(f32::min).max(PLANCK); } diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index 9ad1eef44f..e592746b22 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -1,3 +1,7 @@ +use client::Client; + +use common::vol::{ReadVol, SampleVol}; + // Standard use std::f32::consts::PI; @@ -7,31 +11,59 @@ use vek::*; const NEAR_PLANE: f32 = 0.1; const FAR_PLANE: f32 = 10000.0; +const INTERP_TIME: f32 = 0.2; + pub struct Camera { + tgt_focus: Vec3, focus: Vec3, ori: Vec3, + tgt_dist: f32, dist: f32, fov: f32, aspect: f32, + + last_time: Option, } impl Camera { /// Create a new `Camera` with default parameters. pub fn new(aspect: f32) -> Self { Self { + tgt_focus: Vec3::unit_z() * 10.0, focus: Vec3::unit_z() * 10.0, ori: Vec3::zero(), + tgt_dist: 10.0, dist: 10.0, fov: 1.3, aspect, + last_time: None, } } /// Compute the transformation matrices (view matrix and projection matrix) and position of the /// camera. - pub fn compute_dependents(&self) -> (Mat4, Mat4, Vec3) { + pub fn compute_dependents(&self, client: &Client) -> (Mat4, Mat4, Vec3) { + let dist = { + let (start, end) = ( + self.focus, + self.focus + + (Vec3::new( + -f32::sin(self.ori.x) * f32::cos(self.ori.y), + -f32::cos(self.ori.x) * f32::cos(self.ori.y), + f32::sin(self.ori.y), + ) * self.dist), + ); + + match client.state().terrain().ray(start, end).cast() { + (d, Ok(Some(_))) => f32::min(d - 1.0, self.dist), + (_, Ok(None)) => self.dist, + (_, Err(_)) => self.dist, + }.max(0.0) + }; + + let view_mat = Mat4::::identity() - * Mat4::translation_3d(-Vec3::unit_z() * self.dist) + * Mat4::translation_3d(-Vec3::unit_z() * dist) * Mat4::rotation_z(self.ori.z) * Mat4::rotation_x(self.ori.y) * Mat4::rotation_y(self.ori.x) @@ -59,16 +91,28 @@ impl Camera { /// Zoom the camera by the given delta, limiting the input accordingly. pub fn zoom_by(&mut self, delta: f32) { // Clamp camera dist to the 0 <= x <= infinity range - self.dist = (self.dist + delta).max(0.0); + self.tgt_dist = (self.tgt_dist + delta).max(0.0); + } + + pub fn update(&mut self, time: f64) { + // This is horribly frame time dependent, but so is most of the game + let delta = self.last_time.replace(time).map_or(0.0, |t| time - t); + if (self.dist - self.tgt_dist).abs() > 0.01 { + self.dist = f32::lerp(self.dist, self.tgt_dist, (delta as f32) / INTERP_TIME); + } + + if (self.focus - self.tgt_focus).magnitude() > 0.01 { + self.focus = Vec3::lerp(self.focus, self.tgt_focus, (delta as f32) / INTERP_TIME); + } } /// Get the focus position of the camera. pub fn get_focus_pos(&self) -> Vec3 { - self.focus + self.tgt_focus } /// Set the focus position of the camera. pub fn set_focus_pos(&mut self, focus: Vec3) { - self.focus = focus; + self.tgt_focus = focus; } /// Get the aspect ratio of the camera. diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 2cb1779de5..95c875c845 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -103,10 +103,13 @@ impl Scene { .unwrap_or(Vec3::zero()); // Alter camera position to match player - self.camera.set_focus_pos(player_pos + Vec3::unit_z() * 1.5); + self.camera.set_focus_pos(player_pos + Vec3::unit_z() * 3.5); + + // Tick camera for interpolation + self.camera.update(client.state().get_time()); // Compute camera matrices - let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(); + let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client); // Update global constants renderer From b2c2d8944772237cc31c7273e79024c47ad12dc1 Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 May 2019 17:54:08 +0200 Subject: [PATCH 2/2] Run cargo fmt Former-commit-id: c038eee51ee522fc170f7dcee94c533ad2708df2 --- common/src/ray.rs | 2 +- voxygen/src/scene/camera.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/ray.rs b/common/src/ray.rs index 219b5a2c0e..c577980f54 100644 --- a/common/src/ray.rs +++ b/common/src/ray.rs @@ -49,7 +49,7 @@ impl<'a, V: ReadVol, F: RayUntil> Ray<'a, V, F> { match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) { Ok((vox, true)) => return (dist, Ok(Some(vox))), - Ok((_, false)) => {}, + Ok((_, false)) => {} Err(err) => return (dist, Err(err)), } diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index e592746b22..7dae0f5a52 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -58,10 +58,10 @@ impl Camera { (d, Ok(Some(_))) => f32::min(d - 1.0, self.dist), (_, Ok(None)) => self.dist, (_, Err(_)) => self.dist, - }.max(0.0) + } + .max(0.0) }; - let view_mat = Mat4::::identity() * Mat4::translation_3d(-Vec3::unit_z() * dist) * Mat4::rotation_z(self.ori.z)