1use std::ops::{Deref, DerefMut};
2
3pub use ecolor::Color32;
4
5use emath::Rect;
6use enumset::{EnumSet, EnumSetType, enum_set};
7
8use crate::math::{
9 DMat4, DQuat, DVec3, DVec4, Transform, Vec4Swizzles, screen_to_world, world_to_screen,
10};
11
12pub const DEFAULT_SNAP_ANGLE: f32 = std::f32::consts::PI / 32.0;
14pub const DEFAULT_SNAP_DISTANCE: f32 = 0.1;
16pub const DEFAULT_SNAP_SCALE: f32 = 0.1;
18
19#[derive(Debug, Copy, Clone)]
24pub struct GizmoConfig {
25 pub view_matrix: mint::RowMatrix4<f64>,
27 pub projection_matrix: mint::RowMatrix4<f64>,
29 pub viewport: Rect,
31 pub modes: EnumSet<GizmoMode>,
33 pub mode_override: Option<GizmoMode>,
35 pub orientation: GizmoOrientation,
37 pub pivot_point: TransformPivotPoint,
39 pub snapping: bool,
41 pub snap_angle: f32,
43 pub snap_distance: f32,
45 pub snap_scale: f32,
47 pub visuals: GizmoVisuals,
49 pub pixels_per_point: f32,
51}
52
53impl Default for GizmoConfig {
54 fn default() -> Self {
55 Self {
56 view_matrix: DMat4::IDENTITY.into(),
57 projection_matrix: DMat4::IDENTITY.into(),
58 viewport: Rect::NOTHING,
59 modes: GizmoMode::all(),
60 mode_override: None,
61 orientation: GizmoOrientation::default(),
62 pivot_point: TransformPivotPoint::default(),
63 snapping: false,
64 snap_angle: DEFAULT_SNAP_ANGLE,
65 snap_distance: DEFAULT_SNAP_DISTANCE,
66 snap_scale: DEFAULT_SNAP_SCALE,
67 visuals: GizmoVisuals::default(),
68 pixels_per_point: 1.0,
69 }
70 }
71}
72
73impl GizmoConfig {
74 pub(crate) fn view_forward(&self) -> DVec3 {
76 DVec4::from(self.view_matrix.z).xyz()
77 }
78
79 pub(crate) fn view_up(&self) -> DVec3 {
81 DVec4::from(self.view_matrix.y).xyz()
82 }
83
84 pub(crate) fn view_right(&self) -> DVec3 {
86 DVec4::from(self.view_matrix.x).xyz()
87 }
88
89 pub(crate) fn local_space(&self) -> bool {
91 self.orientation() == GizmoOrientation::Local
92 }
93
94 pub(crate) fn orientation(&self) -> GizmoOrientation {
96 self.orientation
97 }
98
99 pub(crate) fn modes_changed(&self, other: &Self) -> bool {
101 (self.modes != other.modes && self.mode_override.is_none())
102 || (self.mode_override != other.mode_override)
103 }
104}
105
106#[derive(Debug, Copy, Clone, Default)]
107pub(crate) struct PreparedGizmoConfig {
108 config: GizmoConfig,
109 pub(crate) rotation: DQuat,
111 pub(crate) translation: DVec3,
113 pub(crate) scale: DVec3,
115 pub(crate) view_projection: DMat4,
117 pub(crate) model_matrix: DMat4,
119 pub(crate) mvp: DMat4,
121 pub(crate) scale_factor: f32,
123 pub(crate) focus_distance: f32,
125 pub(crate) left_handed: bool,
127 pub(crate) eye_to_model_dir: DVec3,
129}
130
131impl Deref for PreparedGizmoConfig {
132 type Target = GizmoConfig;
133
134 fn deref(&self) -> &Self::Target {
135 &self.config
136 }
137}
138
139impl DerefMut for PreparedGizmoConfig {
140 fn deref_mut(&mut self) -> &mut Self::Target {
141 &mut self.config
142 }
143}
144
145impl PreparedGizmoConfig {
146 pub(crate) fn update_for_config(&mut self, config: GizmoConfig) {
147 let projection_matrix = DMat4::from(config.projection_matrix);
148 let view_matrix = DMat4::from(config.view_matrix);
149
150 let view_projection = projection_matrix * view_matrix;
151
152 let left_handed = if projection_matrix.z_axis.w == 0.0 {
153 projection_matrix.z_axis.z > 0.0
154 } else {
155 projection_matrix.z_axis.w > 0.0
156 };
157
158 self.config = config;
159 self.view_projection = view_projection;
160 self.left_handed = left_handed;
161
162 self.update_transform(Transform {
163 scale: self.scale.into(),
164 rotation: self.rotation.into(),
165 translation: self.translation.into(),
166 });
167 }
168
169 pub(crate) fn update_for_targets(&mut self, targets: &[Transform]) {
170 let mut scale = DVec3::ZERO;
171 let mut translation = DVec3::ZERO;
172 let mut rotation = DQuat::IDENTITY;
173
174 let mut target_count = 0;
175 for target in targets {
176 scale += DVec3::from(target.scale);
177 translation += DVec3::from(target.translation);
178 rotation = DQuat::from(target.rotation);
179
180 target_count += 1;
181 }
182
183 if target_count == 0 {
184 scale = DVec3::ONE;
185 } else {
186 translation /= target_count as f64;
187 scale /= target_count as f64;
188 }
189
190 self.update_transform(Transform {
191 scale: scale.into(),
192 rotation: rotation.into(),
193 translation: translation.into(),
194 });
195 }
196
197 pub(crate) fn update_transform(&mut self, transform: Transform) {
198 self.translation = transform.translation.into();
199 self.rotation = transform.rotation.into();
200 self.scale = transform.scale.into();
201 self.model_matrix =
202 DMat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation);
203 self.mvp = self.view_projection * self.model_matrix;
204
205 self.scale_factor = self.mvp.as_ref()[15] as f32
206 / self.projection_matrix.x.x as f32
207 / self.config.viewport.width()
208 * 2.0;
209
210 let gizmo_screen_pos =
211 world_to_screen(self.config.viewport, self.mvp, self.translation).unwrap_or_default();
212
213 let gizmo_view_near = screen_to_world(
214 self.config.viewport,
215 self.view_projection.inverse(),
216 gizmo_screen_pos,
217 -1.0,
218 );
219
220 self.focus_distance = self.scale_factor * (self.config.visuals.stroke_width / 2.0 + 5.0);
221
222 self.eye_to_model_dir = (gizmo_view_near - self.translation).normalize_or_zero();
223 }
224
225 pub(crate) fn as_transform(&self) -> Transform {
226 Transform {
227 scale: self.scale.into(),
228 rotation: self.rotation.into(),
229 translation: self.translation.into(),
230 }
231 }
232}
233
234#[derive(Debug, EnumSetType, Hash)]
236pub enum GizmoMode {
237 RotateView,
239 RotateX,
241 RotateY,
243 RotateZ,
245 TranslateView,
247 TranslateX,
249 TranslateY,
251 TranslateZ,
253 TranslateXY,
255 TranslateXZ,
257 TranslateYZ,
259 ScaleUniform,
261 ScaleX,
263 ScaleY,
265 ScaleZ,
267 ScaleXY,
269 ScaleXZ,
271 ScaleYZ,
273 Arcball,
275}
276
277impl GizmoMode {
278 pub fn all() -> EnumSet<Self> {
280 EnumSet::all()
281 }
282
283 pub const fn all_rotate() -> EnumSet<Self> {
285 enum_set!(Self::RotateX | Self::RotateY | Self::RotateZ | Self::RotateView)
286 }
287
288 pub const fn all_translate() -> EnumSet<Self> {
290 enum_set!(
291 Self::TranslateX
292 | Self::TranslateY
293 | Self::TranslateZ
294 | Self::TranslateXY
295 | Self::TranslateXZ
296 | Self::TranslateYZ
297 | Self::TranslateView
298 )
299 }
300
301 pub const fn all_scale() -> EnumSet<Self> {
303 enum_set!(
304 Self::ScaleX
305 | Self::ScaleY
306 | Self::ScaleZ
307 | Self::ScaleXY
308 | Self::ScaleXZ
309 | Self::ScaleYZ
310 | Self::ScaleUniform
311 )
312 }
313
314 pub fn is_rotate(&self) -> bool {
316 self.kind() == GizmoModeKind::Rotate
317 }
318
319 pub fn is_translate(&self) -> bool {
321 self.kind() == GizmoModeKind::Translate
322 }
323
324 pub fn is_scale(&self) -> bool {
326 self.kind() == GizmoModeKind::Scale
327 }
328
329 pub fn axes(&self) -> EnumSet<GizmoDirection> {
331 match self {
332 Self::RotateX | Self::TranslateX | Self::ScaleX => {
333 enum_set!(GizmoDirection::X)
334 }
335 Self::RotateY | Self::TranslateY | Self::ScaleY => {
336 enum_set!(GizmoDirection::Y)
337 }
338 Self::RotateZ | Self::TranslateZ | Self::ScaleZ => {
339 enum_set!(GizmoDirection::Z)
340 }
341 Self::RotateView | Self::TranslateView => {
342 enum_set!(GizmoDirection::View)
343 }
344 Self::ScaleUniform | Self::Arcball => {
345 enum_set!(GizmoDirection::X | GizmoDirection::Y | GizmoDirection::Z)
346 }
347 Self::TranslateXY | Self::ScaleXY => {
348 enum_set!(GizmoDirection::X | GizmoDirection::Y)
349 }
350 Self::TranslateXZ | Self::ScaleXZ => {
351 enum_set!(GizmoDirection::X | GizmoDirection::Z)
352 }
353 Self::TranslateYZ | Self::ScaleYZ => {
354 enum_set!(GizmoDirection::Y | GizmoDirection::Z)
355 }
356 }
357 }
358
359 pub fn all_from_axes(axes: EnumSet<GizmoDirection>) -> EnumSet<Self> {
361 EnumSet::<Self>::all()
362 .iter()
363 .filter(|mode| mode.axes() == axes)
364 .collect()
365 }
366
367 pub fn kind(&self) -> GizmoModeKind {
368 match self {
369 Self::RotateX | Self::RotateY | Self::RotateZ | Self::RotateView => {
370 GizmoModeKind::Rotate
371 }
372 Self::TranslateX
373 | Self::TranslateY
374 | Self::TranslateZ
375 | Self::TranslateXY
376 | Self::TranslateXZ
377 | Self::TranslateYZ
378 | Self::TranslateView => GizmoModeKind::Translate,
379 Self::ScaleX
380 | Self::ScaleY
381 | Self::ScaleZ
382 | Self::ScaleXY
383 | Self::ScaleXZ
384 | Self::ScaleYZ
385 | Self::ScaleUniform => GizmoModeKind::Scale,
386 Self::Arcball => GizmoModeKind::Arcball,
387 }
388 }
389
390 pub fn all_from_kind(kind: GizmoModeKind) -> EnumSet<Self> {
391 EnumSet::<Self>::all()
392 .iter()
393 .filter(|mode| mode.kind() == kind)
394 .collect()
395 }
396
397 pub fn from_kind_and_axes(kind: GizmoModeKind, axes: EnumSet<GizmoDirection>) -> Option<Self> {
398 EnumSet::<Self>::all()
399 .iter()
400 .find(|mode| mode.kind() == kind && mode.axes() == axes)
401 }
402}
403
404#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
405pub enum GizmoModeKind {
406 Rotate,
407 Translate,
408 Scale,
409 Arcball,
410}
411
412#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
414pub enum TransformPivotPoint {
415 #[default]
417 MedianPoint,
418 IndividualOrigins,
420}
421
422#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
424pub enum GizmoOrientation {
425 #[default]
427 Global,
428 Local,
430}
431
432#[derive(Debug, EnumSetType, Hash)]
433pub enum GizmoDirection {
434 X,
436 Y,
438 Z,
440 View,
442}
443
444#[derive(Debug, Copy, Clone)]
446pub struct GizmoVisuals {
447 pub x_color: Color32,
449 pub y_color: Color32,
451 pub z_color: Color32,
453 pub s_color: Color32,
455 pub inactive_alpha: f32,
457 pub highlight_alpha: f32,
459 pub highlight_color: Option<Color32>,
461 pub stroke_width: f32,
463 pub gizmo_size: f32,
465}
466
467impl Default for GizmoVisuals {
468 fn default() -> Self {
469 Self {
470 x_color: Color32::from_rgb(255, 0, 125),
471 y_color: Color32::from_rgb(0, 255, 125),
472 z_color: Color32::from_rgb(0, 125, 255),
473 s_color: Color32::from_rgb(255, 255, 255),
474 inactive_alpha: 0.7,
475 highlight_alpha: 1.0,
476 highlight_color: None,
477 stroke_width: 4.0,
478 gizmo_size: 75.0,
479 }
480 }
481}