1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
use nalgebra::{convert, Matrix4, Point2, RealField};

/// Scene wrt enclosing viewing frustum.
///
/// Implements [`Default`] and can be created with `Scene::default()`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Scene<N: RealField> {
	/// Field of view y-axis.
	///
	/// Angle in yz-plane. Default is [`RealField::frac_pi_4()`].
	fov: N,
	/// Clip plane distances.
	///
	/// Near and far clip plane distances from either target or eye whether [`Self::oim`]. Defaults
	/// to `(1e-1, 1e+6)` measured from eye.
	zcp: (N, N),
	/// Object inspection mode.
	///
	/// Scales clip plane distances by measuring from target instead of eye. Default is `false`.
	oim: bool,
	/// Orthographic projection mode.
	///
	/// Computes scale-identical orthographic instead of perspective projection. Default is `false`.
	opm: bool,
}

impl<N: RealField> Default for Scene<N> {
	fn default() -> Self {
		Self {
			fov: N::frac_pi_4(),
			zcp: (convert(1e-1), convert(1e+6)),
			oim: false,
			opm: false,
		}
	}
}

impl<N: RealField> Scene<N> {
	/// Field of view y-axis.
	///
	/// Angle in yz-plane. Default is [`RealField::frac_pi_4()`].
	pub fn fov(&self) -> N {
		self.fov
	}
	/// Sets field of view y-axis.
	///
	/// Angle in yz-plane. Default is [`RealField::frac_pi_4()`].
	pub fn set_fov(&mut self, fov: N) {
		self.fov = fov;
	}
	/// Clip plane distances from eye regardless of [`Self::scale()`].
	///
	/// Defaults to `(1e-1, 1e+6)` measured from eye.
	pub fn clip_planes(&self, zat: N) -> (N, N) {
		if self.oim {
			let (znear, zfar) = self.zcp;
			(zat - znear, zat + zfar)
		} else {
			self.zcp
		}
	}
	/// Sets clip plane distances from target or eye whether [`Self::scale()`].
	///
	/// Defaults to `(1e-1, 1e+6)` measured from eye.
	pub fn set_clip_planes(&mut self, znear: N, zfar: N) {
		self.zcp = (znear, zfar);
	}
	/// Object inspection mode.
	///
	/// Scales clip plane distances by measuring from target instead of eye. Default is `false`.
	pub fn scale(&self) -> bool {
		self.oim
	}
	/// Sets object inspection mode.
	///
	/// Scales clip plane distances by measuring from target instead of eye. Default is `false`.
	pub fn set_scale(&mut self, oim: bool) {
		self.oim = oim
	}
	/// Orthographic projection mode.
	///
	/// Computes scale-identical orthographic instead of perspective projection. Default is `false`.
	pub fn ortho(&self) -> bool {
		self.opm
	}
	/// Sets orthographic projection mode.
	///
	/// Computes scale-identical orthographic instead of perspective projection. Default is `false`.
	pub fn set_ortho(&mut self, opm: bool) {
		self.opm = opm
	}
	/// Projection transformation and unit per pixel on focus plane.
	pub fn projection(&self, zat: N, max: &Point2<N>) -> (Matrix4<N>, N) {
		let (znear, zfar) = self.clip_planes(zat);
		let aspect = max.x / max.y;
		let two = N::one() + N::one();
		let top = zat * (self.fov / two).tan();
		let right = aspect * top;
		let upp = right * two / max.x;
		let mat = if self.opm {
			Matrix4::new_orthographic(-right, right, -top, top, znear, zfar)
		} else {
			Matrix4::new_perspective(aspect, self.fov, znear, zfar)
		};
		(mat, upp)
	}
}