2019-03-25 18:21:41 +08:00
|
|
|
use na::{self, RealField, U3};
|
2018-09-22 19:18:59 +08:00
|
|
|
|
2019-03-23 21:29:07 +08:00
|
|
|
use crate::aliases::{TMat4, TVec2, TVec3, TVec4};
|
2018-09-22 19:18:59 +08:00
|
|
|
|
|
|
|
/// Define a picking region.
|
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `center` - Specify the center of a picking region in window coordinates.
|
|
|
|
/// * `delta` - Specify the width and height, respectively, of the picking region in window coordinates.
|
|
|
|
/// * `viewport` - Rendering viewport.
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn pick_matrix<N: RealField>(center: &TVec2<N>, delta: &TVec2<N>, viewport: &TVec4<N>) -> TMat4<N> {
|
2018-09-23 20:41:56 +08:00
|
|
|
let shift = TVec3::new(
|
2018-09-22 19:18:59 +08:00
|
|
|
(viewport.z - (center.x - viewport.x) * na::convert(2.0)) / delta.x,
|
|
|
|
(viewport.w - (center.y - viewport.y) * na::convert(2.0)) / delta.y,
|
2018-10-22 04:11:27 +08:00
|
|
|
N::zero(),
|
2018-09-22 19:18:59 +08:00
|
|
|
);
|
|
|
|
|
2018-09-23 20:41:56 +08:00
|
|
|
let result = TMat4::new_translation(&shift);
|
2018-10-22 04:11:27 +08:00
|
|
|
result.prepend_nonuniform_scaling(&TVec3::new(
|
|
|
|
viewport.z / delta.x,
|
|
|
|
viewport.w / delta.y,
|
|
|
|
N::one(),
|
|
|
|
))
|
2018-09-22 19:18:59 +08:00
|
|
|
}
|
|
|
|
|
2018-12-19 11:01:09 +08:00
|
|
|
/// Map the specified object coordinates `(obj.x, obj.y, obj.z)` into window coordinates with a
|
|
|
|
/// depth range of -1 to 1
|
2018-09-22 19:18:59 +08:00
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `obj` - Specify the object coordinates.
|
|
|
|
/// * `model` - Specifies the current modelview matrix.
|
|
|
|
/// * `proj` - Specifies the current projection matrix.
|
|
|
|
/// * `viewport` - Specifies the current viewport.
|
2018-10-08 10:30:16 +08:00
|
|
|
///
|
|
|
|
/// # See also:
|
|
|
|
///
|
|
|
|
/// * [`project_no`](fn.project_no.html)
|
|
|
|
/// * [`project_zo`](fn.project_zo.html)
|
|
|
|
/// * [`unproject`](fn.unproject.html)
|
|
|
|
/// * [`unproject_no`](fn.unproject_no.html)
|
|
|
|
/// * [`unproject_zo`](fn.unproject_zo.html)
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn project<N: RealField>(
|
2018-10-22 04:11:27 +08:00
|
|
|
obj: &TVec3<N>,
|
|
|
|
model: &TMat4<N>,
|
|
|
|
proj: &TMat4<N>,
|
|
|
|
viewport: TVec4<N>,
|
2018-10-22 13:00:10 +08:00
|
|
|
) -> TVec3<N>
|
|
|
|
{
|
2018-12-19 11:01:09 +08:00
|
|
|
project_no(obj, model, proj, viewport)
|
2018-09-22 19:18:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates.
|
|
|
|
///
|
|
|
|
/// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
|
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `obj` - Specify the object coordinates.
|
|
|
|
/// * `model` - Specifies the current modelview matrix.
|
|
|
|
/// * `proj` - Specifies the current projection matrix.
|
|
|
|
/// * `viewport` - Specifies the current viewport.
|
2018-10-08 10:30:16 +08:00
|
|
|
///
|
|
|
|
/// # See also:
|
|
|
|
///
|
|
|
|
/// * [`project`](fn.project.html)
|
|
|
|
/// * [`project_zo`](fn.project_zo.html)
|
|
|
|
/// * [`unproject`](fn.unproject.html)
|
|
|
|
/// * [`unproject_no`](fn.unproject_no.html)
|
|
|
|
/// * [`unproject_zo`](fn.unproject_zo.html)
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn project_no<N: RealField>(
|
2018-10-22 04:11:27 +08:00
|
|
|
obj: &TVec3<N>,
|
|
|
|
model: &TMat4<N>,
|
|
|
|
proj: &TMat4<N>,
|
|
|
|
viewport: TVec4<N>,
|
2018-10-22 13:00:10 +08:00
|
|
|
) -> TVec3<N>
|
|
|
|
{
|
2018-09-22 19:18:59 +08:00
|
|
|
let proj = project_zo(obj, model, proj, viewport);
|
2018-09-23 20:41:56 +08:00
|
|
|
TVec3::new(proj.x, proj.y, proj.z * na::convert(0.5) + na::convert(0.5))
|
2018-09-22 19:18:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates.
|
|
|
|
///
|
|
|
|
/// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
|
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `obj` - Specify the object coordinates.
|
|
|
|
/// * `model` - Specifies the current modelview matrix.
|
|
|
|
/// * `proj` - Specifies the current projection matrix.
|
|
|
|
/// * `viewport` - Specifies the current viewport.
|
2018-10-08 10:30:16 +08:00
|
|
|
///
|
|
|
|
/// # See also:
|
|
|
|
///
|
|
|
|
/// * [`project`](fn.project.html)
|
|
|
|
/// * [`project_no`](fn.project_no.html)
|
|
|
|
/// * [`unproject`](fn.unproject.html)
|
|
|
|
/// * [`unproject_no`](fn.unproject_no.html)
|
|
|
|
/// * [`unproject_zo`](fn.unproject_zo.html)
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn project_zo<N: RealField>(
|
2018-10-22 04:11:27 +08:00
|
|
|
obj: &TVec3<N>,
|
|
|
|
model: &TMat4<N>,
|
|
|
|
proj: &TMat4<N>,
|
|
|
|
viewport: TVec4<N>,
|
2018-10-22 13:00:10 +08:00
|
|
|
) -> TVec3<N>
|
|
|
|
{
|
2018-09-23 20:41:56 +08:00
|
|
|
let normalized = proj * model * TVec4::new(obj.x, obj.y, obj.z, N::one());
|
2018-09-22 19:18:59 +08:00
|
|
|
let scale = N::one() / normalized.w;
|
|
|
|
|
2018-09-23 20:41:56 +08:00
|
|
|
TVec3::new(
|
2018-09-22 19:18:59 +08:00
|
|
|
viewport.x + (viewport.z * (normalized.x * scale + N::one()) * na::convert(0.5)),
|
|
|
|
viewport.y + (viewport.w * (normalized.y * scale + N::one()) * na::convert(0.5)),
|
|
|
|
normalized.z * scale,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-12-19 11:01:09 +08:00
|
|
|
/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using a
|
|
|
|
/// depth range of -1 to 1
|
2018-09-22 19:18:59 +08:00
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `obj` - Specify the window coordinates to be mapped.
|
|
|
|
/// * `model` - Specifies the current modelview matrix.
|
|
|
|
/// * `proj` - Specifies the current projection matrix.
|
|
|
|
/// * `viewport` - Specifies the current viewport.
|
2018-10-08 10:30:16 +08:00
|
|
|
///
|
|
|
|
/// # See also:
|
|
|
|
///
|
|
|
|
/// * [`project`](fn.project.html)
|
|
|
|
/// * [`project_no`](fn.project_no.html)
|
|
|
|
/// * [`project_zo`](fn.project_zo.html)
|
|
|
|
/// * [`unproject_no`](fn.unproject_no.html)
|
|
|
|
/// * [`unproject_zo`](fn.unproject_zo.html)
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn unproject<N: RealField>(
|
2018-10-22 04:11:27 +08:00
|
|
|
win: &TVec3<N>,
|
|
|
|
model: &TMat4<N>,
|
|
|
|
proj: &TMat4<N>,
|
|
|
|
viewport: TVec4<N>,
|
2018-10-22 13:00:10 +08:00
|
|
|
) -> TVec3<N>
|
|
|
|
{
|
2018-12-19 11:01:09 +08:00
|
|
|
unproject_no(win, model, proj, viewport)
|
2018-09-22 19:18:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates.
|
|
|
|
///
|
|
|
|
/// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition)
|
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `obj` - Specify the window coordinates to be mapped.
|
|
|
|
/// * `model` - Specifies the current modelview matrix.
|
|
|
|
/// * `proj` - Specifies the current projection matrix.
|
|
|
|
/// * `viewport` - Specifies the current viewport.
|
2018-10-08 10:30:16 +08:00
|
|
|
///
|
|
|
|
/// # See also:
|
|
|
|
///
|
|
|
|
/// * [`project`](fn.project.html)
|
|
|
|
/// * [`project_no`](fn.project_no.html)
|
|
|
|
/// * [`project_zo`](fn.project_zo.html)
|
|
|
|
/// * [`unproject`](fn.unproject.html)
|
|
|
|
/// * [`unproject_zo`](fn.unproject_zo.html)
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn unproject_no<N: RealField>(
|
2018-10-22 04:11:27 +08:00
|
|
|
win: &TVec3<N>,
|
|
|
|
model: &TMat4<N>,
|
|
|
|
proj: &TMat4<N>,
|
|
|
|
viewport: TVec4<N>,
|
2018-10-22 13:00:10 +08:00
|
|
|
) -> TVec3<N>
|
|
|
|
{
|
2018-09-22 19:18:59 +08:00
|
|
|
let _2: N = na::convert(2.0);
|
2018-10-05 11:21:38 +08:00
|
|
|
let transform = (proj * model).try_inverse().unwrap_or_else(TMat4::zeros);
|
2018-09-23 20:41:56 +08:00
|
|
|
let pt = TVec4::new(
|
2018-09-22 19:18:59 +08:00
|
|
|
_2 * (win.x - viewport.x) / viewport.z - N::one(),
|
|
|
|
_2 * (win.y - viewport.y) / viewport.w - N::one(),
|
|
|
|
_2 * win.z - N::one(),
|
|
|
|
N::one(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let result = transform * pt;
|
|
|
|
result.fixed_rows::<U3>(0) / result.w
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates.
|
|
|
|
///
|
|
|
|
/// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition)
|
|
|
|
///
|
2018-10-08 10:42:02 +08:00
|
|
|
/// # Parameters:
|
|
|
|
///
|
|
|
|
/// * `obj` - Specify the window coordinates to be mapped.
|
|
|
|
/// * `model` - Specifies the current modelview matrix.
|
|
|
|
/// * `proj` - Specifies the current projection matrix.
|
|
|
|
/// * `viewport` - Specifies the current viewport.
|
2018-10-08 10:30:16 +08:00
|
|
|
///
|
|
|
|
/// # See also:
|
|
|
|
///
|
|
|
|
/// * [`project`](fn.project.html)
|
|
|
|
/// * [`project_no`](fn.project_no.html)
|
|
|
|
/// * [`project_zo`](fn.project_zo.html)
|
|
|
|
/// * [`unproject`](fn.unproject.html)
|
|
|
|
/// * [`unproject_no`](fn.unproject_no.html)
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn unproject_zo<N: RealField>(
|
2018-10-22 04:11:27 +08:00
|
|
|
win: &TVec3<N>,
|
|
|
|
model: &TMat4<N>,
|
|
|
|
proj: &TMat4<N>,
|
|
|
|
viewport: TVec4<N>,
|
2018-10-22 13:00:10 +08:00
|
|
|
) -> TVec3<N>
|
|
|
|
{
|
2018-09-22 19:18:59 +08:00
|
|
|
let _2: N = na::convert(2.0);
|
2018-10-05 11:21:38 +08:00
|
|
|
let transform = (proj * model).try_inverse().unwrap_or_else(TMat4::zeros);
|
2018-09-23 20:41:56 +08:00
|
|
|
let pt = TVec4::new(
|
2018-09-22 19:18:59 +08:00
|
|
|
_2 * (win.x - viewport.x) / viewport.z - N::one(),
|
|
|
|
_2 * (win.y - viewport.y) / viewport.w - N::one(),
|
|
|
|
win.z,
|
|
|
|
N::one(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let result = transform * pt;
|
|
|
|
result.fixed_rows::<U3>(0) / result.w
|
2018-09-24 12:48:42 +08:00
|
|
|
}
|