diff --git a/studio/src/interaction/gizmos.rs b/studio/src/interaction/gizmos.rs index 969f4f8..20037a3 100644 --- a/studio/src/interaction/gizmos.rs +++ b/studio/src/interaction/gizmos.rs @@ -2,7 +2,7 @@ use std::f32::consts; use bevy::{prelude::*, window::PrimaryWindow}; -use crate::design_system; +use crate::{design_system, math::Lerp}; use super::{InteractionMode, InteractionTransform, Selected, TransformSpace}; @@ -50,7 +50,7 @@ pub(super) fn draw( }; if mode.transform.contains(InteractionTransform::TRANSLATE) { - draw_translate_gizmo(&mut gizmos, variant, &xform, mode.space, size); + draw_translate_gizmo(&mut gizmos, variant, &xform, camera_xform, mode.space, size); } if mode.transform.contains(InteractionTransform::ROTATE) { draw_rotate_gizmo(&mut gizmos, variant, &xform, camera_xform, mode.space, size); @@ -67,6 +67,7 @@ fn draw_translate_gizmo( gizmos: &mut Gizmos, variant: GizmoVariant, xform: &Transform, + camera_xform: &Transform, space: TransformSpace, size: f32, ) { @@ -76,36 +77,65 @@ fn draw_translate_gizmo( TransformSpace::World => &Transform::IDENTITY, }; - match variant { - GizmoVariant::Full => { - gizmos.sphere(origin, Quat::IDENTITY, size * 0.125, Color::WHITE); + gizmos.circle(origin, camera_xform.forward(), size * 0.2, Color::WHITE); - for (direction, color) in [ - (basis.local_x(), design_system::Color::RED), - (basis.local_y(), design_system::Color::GREEN), - (basis.local_z(), design_system::Color::BLUE), - ] { - let end = origin + direction * size * 1.4; - gizmos.line(origin, end, color); + for (direction, mut color) in [ + (basis.local_x(), design_system::Color::RED), + (basis.local_y(), design_system::Color::GREEN), + (basis.local_z(), design_system::Color::BLUE), + ] { + let alpha = (1. - Vec3::dot(camera_xform.forward(), direction).abs()) + .remap((0., 0.1), (0., 1.)) + .clamp(0., 1.); - let cone_base = origin + direction * size; - let radius = size * 0.1; - let length = size * 0.4; - gizmos.cone(cone_base, direction, radius, length, color); + color = color.with_a(alpha); + + let cone_base = match variant { + GizmoVariant::Full => { + let start = origin + direction * size * 0.25; + let end = origin + direction * size * 1.6; + gizmos.line(start, end, color); + + let midpoint_self = origin + ((end - origin) / 2.); + let (ortho, mut color_ortho) = get_ortho(basis, direction); + + let ortho_alpha = (1. - Vec3::dot(camera_xform.forward(), ortho).abs()) + .remap((0., 0.1), (0., 1.)) + .clamp(0., 1.); + + color_ortho = color_ortho.with_a(f32::min(ortho_alpha, alpha)); + + let ortho_end = origin + ortho * size * 1.6; + let midpoint_ortho = origin + ((ortho_end - origin) / 2.); + gizmos.line_gradient(midpoint_self, midpoint_ortho, color, color_ortho); + + origin + direction * size * 1.2 } - } - GizmoVariant::Partial => { - for (direction, color) in [ - (basis.local_x(), design_system::Color::RED), - (basis.local_y(), design_system::Color::GREEN), - (basis.local_z(), design_system::Color::BLUE), - ] { - let cone_base = origin + direction * size * 1.4; - let radius = size * 0.1; - let length = size * 0.4; - gizmos.cone(cone_base, direction, radius, length, color); - } - } + GizmoVariant::Partial => origin + direction * size * 1.4, + }; + + let radius = size * 0.1; + let length = size * 0.4; + gizmos.cone(cone_base, direction, radius, length, color); + } +} + +#[inline] +fn get_ortho(xform: &Transform, compare: Vec3) -> (Vec3, Color) { + let x = xform.local_x(); + let y = xform.local_y(); + let z = xform.local_z(); + + let diff_x = (compare - x).length_squared(); + let diff_y = (compare - y).length_squared(); + let diff_z = (compare - z).length_squared(); + + if diff_x < diff_y && diff_x < diff_z { + (y, design_system::Color::GREEN) + } else if diff_y < diff_x && diff_y < diff_z { + (z, design_system::Color::BLUE) + } else { + (x, design_system::Color::RED) } } diff --git a/studio/src/math/interpolation.rs b/studio/src/math/interpolation.rs new file mode 100644 index 0000000..bd29f52 --- /dev/null +++ b/studio/src/math/interpolation.rs @@ -0,0 +1,54 @@ +use std::ops; + +use super::NearlyEq; + +pub trait Lerp { + fn lerp(self, a: Self, b: Self) -> Self; + fn inv_lerp(self, a: Self, b: Self) -> Self; + fn remap(self, from: (Self, Self), to: (Self, Self)) -> Self + where + Self: Sized; +} + +#[rustfmt::skip] trait One { fn one() -> Self; } +#[rustfmt::skip] impl One for f32 { fn one() -> Self { 1. } } +#[rustfmt::skip] impl One for f64 { fn one() -> Self { 1_f64 } } + +#[rustfmt::skip] trait Zero { fn zero() -> Self; } +#[rustfmt::skip] impl Zero for f32 { fn zero() -> Self { 0. } } +#[rustfmt::skip] impl Zero for f64 { fn zero() -> Self { 0_f64 } } + +// lol it would have been faster and less code to just copy/paste. At least I tried. +impl Lerp for T +where + T: ops::Add + + ops::Sub + + ops::Mul + + ops::Div + + NearlyEq + + One + + Zero + + Sized + + Copy, +{ + fn lerp(self, a: Self, b: Self) -> Self { + (::one() - self) * a + b * self + } + + fn inv_lerp(self, a: Self, b: Self) -> Self { + let dividend = b - a; + if !dividend.nearly_eq(::zero()) { + (self - a) / dividend + } else { + b + } + } + + fn remap(self, from: (Self, Self), to: (Self, Self)) -> Self + where + Self: Sized, + { + let t = self.inv_lerp(from.0, from.1); + t.lerp(to.0, to.1) + } +} diff --git a/studio/src/math/mod.rs b/studio/src/math/mod.rs index c8183a1..f2b6fb9 100644 --- a/studio/src/math/mod.rs +++ b/studio/src/math/mod.rs @@ -1,3 +1,5 @@ +mod interpolation; mod util; +pub use interpolation::*; pub use util::*; diff --git a/studio/src/math/util.rs b/studio/src/math/util.rs index c7dafe2..a539ccc 100644 --- a/studio/src/math/util.rs +++ b/studio/src/math/util.rs @@ -1,3 +1,5 @@ +use std::f32::consts; + use bevy::prelude::Vec3; pub trait NearlyEq { @@ -15,3 +17,11 @@ impl NearlyEq for Vec3 { self.x.nearly_eq(rhs.x) && self.y.nearly_eq(rhs.y) && self.z.nearly_eq(rhs.z) } } + +/// Fit a [0, 1] `t` interpolation parameter to a sine curve +/// +/// https://www.desmos.com/calculator/k9x4c4cr5y +#[inline] +pub fn curve_sin(t: f32) -> f32 { + -1. * (f32::cos(t * consts::PI) / 2.) + 0.5 +} diff --git a/studio/src/orbit_cam.rs b/studio/src/orbit_cam.rs index 57c04d1..9197f34 100644 --- a/studio/src/orbit_cam.rs +++ b/studio/src/orbit_cam.rs @@ -8,7 +8,7 @@ use bevy::{ use crate::{ input::{Action, CameraMotion, InputAction, Lifecycle}, - math::NearlyEq, + math::{self, NearlyEq}, }; pub struct OrbitCamPlugin; @@ -157,7 +157,7 @@ fn update_velocity( } else if t.0.nearly_eq(1.) { target_vel } else { - Vec3::lerp(velocity.0, target_vel, curve_sin(t.0)) + Vec3::lerp(velocity.0, target_vel, math::curve_sin(t.0)) }; } } @@ -281,11 +281,3 @@ fn do_rotate(win: &Window, delta: Vec2, xform: &mut Transform, orbit_cam: &mut O // Update focus point orbit_cam.focus = xform.translation + xform.forward() * orbit_cam.radius; } - -/// Fit a [0, 1] `t` interpolation parameter to a sine curve -/// -/// https://www.desmos.com/calculator/k9x4c4cr5y -#[inline] -fn curve_sin(t: f32) -> f32 { - -1. * (f32::cos(t * consts::PI) / 2.) + 0.5 -}