use std::ops::{Deref, DerefMut};
pub use ecolor::Color32;
use emath::Rect;
use enumset::{enum_set, EnumSet, EnumSetType};
use crate::math::{
screen_to_world, world_to_screen, DMat4, DQuat, DVec3, DVec4, Transform, Vec4Swizzles,
};
pub const DEFAULT_SNAP_ANGLE: f32 = std::f32::consts::PI / 32.0;
pub const DEFAULT_SNAP_DISTANCE: f32 = 0.1;
pub const DEFAULT_SNAP_SCALE: f32 = 0.1;
#[derive(Debug, Copy, Clone)]
pub struct GizmoConfig {
pub view_matrix: mint::RowMatrix4<f64>,
pub projection_matrix: mint::RowMatrix4<f64>,
pub viewport: Rect,
pub modes: EnumSet<GizmoMode>,
pub orientation: GizmoOrientation,
pub snapping: bool,
pub snap_angle: f32,
pub snap_distance: f32,
pub snap_scale: f32,
pub visuals: GizmoVisuals,
pub pixels_per_point: f32,
}
impl Default for GizmoConfig {
fn default() -> Self {
Self {
view_matrix: DMat4::IDENTITY.into(),
projection_matrix: DMat4::IDENTITY.into(),
viewport: Rect::NOTHING,
modes: enum_set!(GizmoMode::Rotate),
orientation: GizmoOrientation::Global,
snapping: false,
snap_angle: DEFAULT_SNAP_ANGLE,
snap_distance: DEFAULT_SNAP_DISTANCE,
snap_scale: DEFAULT_SNAP_SCALE,
visuals: GizmoVisuals::default(),
pixels_per_point: 1.0,
}
}
}
impl GizmoConfig {
pub(crate) fn view_forward(&self) -> DVec3 {
DVec4::from(self.view_matrix.z).xyz()
}
pub(crate) fn view_up(&self) -> DVec3 {
DVec4::from(self.view_matrix.y).xyz()
}
pub(crate) fn view_right(&self) -> DVec3 {
DVec4::from(self.view_matrix.x).xyz()
}
pub(crate) fn local_space(&self) -> bool {
self.orientation == GizmoOrientation::Local || self.modes.contains(GizmoMode::Scale)
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct PreparedGizmoConfig {
config: GizmoConfig,
pub(crate) rotation: DQuat,
pub(crate) translation: DVec3,
pub(crate) scale: DVec3,
pub(crate) view_projection: DMat4,
pub(crate) mvp: DMat4,
pub(crate) scale_factor: f32,
pub(crate) focus_distance: f32,
pub(crate) left_handed: bool,
pub(crate) eye_to_model_dir: DVec3,
}
impl Deref for PreparedGizmoConfig {
type Target = GizmoConfig;
fn deref(&self) -> &Self::Target {
&self.config
}
}
impl DerefMut for PreparedGizmoConfig {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.config
}
}
impl PreparedGizmoConfig {
pub(crate) fn from_config(config: GizmoConfig) -> Self {
let projection_matrix = DMat4::from(config.projection_matrix);
let view_matrix = DMat4::from(config.view_matrix);
let view_projection = projection_matrix * view_matrix;
let left_handed = if projection_matrix.z_axis.w == 0.0 {
projection_matrix.z_axis.z > 0.0
} else {
projection_matrix.z_axis.w > 0.0
};
Self {
config,
rotation: DQuat::IDENTITY,
translation: DVec3::ZERO,
scale: DVec3::ONE,
view_projection,
mvp: view_projection,
eye_to_model_dir: DVec3::ZERO,
scale_factor: 1.0,
focus_distance: 1.0,
left_handed,
}
}
pub(crate) fn update_for_targets(&mut self, targets: &[Transform]) {
let mut scale = DVec3::ZERO;
let mut translation = DVec3::ZERO;
let mut rotation = DQuat::IDENTITY;
let mut target_count = 0;
for target in targets {
scale += DVec3::from(target.scale);
translation += DVec3::from(target.translation);
rotation = DQuat::from(target.rotation);
target_count += 1;
}
if target_count == 0 {
scale = DVec3::ONE;
} else {
translation /= target_count as f64;
scale /= target_count as f64;
}
let model_matrix = DMat4::from_scale_rotation_translation(scale, rotation, translation);
self.mvp = self.view_projection * model_matrix;
let gizmo_screen_pos =
world_to_screen(self.config.viewport, self.mvp, translation).unwrap_or_default();
let gizmo_view_near = screen_to_world(
self.config.viewport,
self.view_projection.inverse(),
gizmo_screen_pos,
-1.0,
);
self.scale_factor = self.mvp.as_ref()[15] as f32
/ self.projection_matrix.x.x as f32
/ self.config.viewport.width()
* 2.0;
self.focus_distance = self.scale_factor * (self.config.visuals.stroke_width / 2.0 + 5.0);
self.rotation = rotation;
self.translation = translation;
self.scale = scale;
self.eye_to_model_dir = (gizmo_view_near - translation).normalize_or_zero();
}
}
#[derive(Debug, EnumSetType)]
pub enum GizmoMode {
Rotate,
Translate,
Scale,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum GizmoOrientation {
Global,
Local,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GizmoDirection {
X,
Y,
Z,
View,
}
#[derive(Debug, Copy, Clone)]
pub struct GizmoVisuals {
pub x_color: Color32,
pub y_color: Color32,
pub z_color: Color32,
pub s_color: Color32,
pub inactive_alpha: f32,
pub highlight_alpha: f32,
pub highlight_color: Option<Color32>,
pub stroke_width: f32,
pub gizmo_size: f32,
}
impl Default for GizmoVisuals {
fn default() -> Self {
Self {
x_color: Color32::from_rgb(255, 0, 125),
y_color: Color32::from_rgb(0, 255, 125),
z_color: Color32::from_rgb(0, 125, 255),
s_color: Color32::from_rgb(255, 255, 255),
inactive_alpha: 0.7,
highlight_alpha: 1.0,
highlight_color: None,
stroke_width: 4.0,
gizmo_size: 75.0,
}
}
}