diff --git a/src/lib.rs b/src/lib.rs index 5cfbd2e..61a831f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -544,6 +544,11 @@ impl GizmoConfig { self.view_matrix.row(2).xyz() } + /// Up vector of the view camera + pub(crate) fn view_up(&self) -> DVec3 { + self.view_matrix.row(1).xyz() + } + /// Right vector of the view camera pub(crate) fn view_right(&self) -> DVec3 { self.view_matrix.row(0).xyz() diff --git a/src/math.rs b/src/math.rs index 2f4a7c1..3a61989 100644 --- a/src/math.rs +++ b/src/math.rs @@ -159,7 +159,7 @@ pub fn round_to_interval(val: f64, interval: f64) -> f64 { pub fn world_to_screen(viewport: Rect, mvp: DMat4, pos: DVec3) -> Option { let mut pos = mvp * DVec4::from((pos, 1.0)); - if pos.w < 0.0 { + if pos.w < 1e-10 { return None; } diff --git a/src/painter.rs b/src/painter.rs index 580dd4a..d40f2ab 100644 --- a/src/painter.rs +++ b/src/painter.rs @@ -30,12 +30,26 @@ impl Painter3d { end_angle: f64, stroke: impl Into, ) -> ShapeIdx { - let angle = end_angle - start_angle; - let step_count = steps(angle); + let mut closed = false; + let mut angle = end_angle - start_angle; + + if angle <= -TAU { + angle = -TAU; + closed = true; + } else if angle >= TAU { + angle = TAU; + closed = true; + } + + let mut step_count = steps(angle); let mut points = Vec::with_capacity(step_count); let step_size = angle / (step_count - 1) as f64; + if closed { + step_count -= 1; + } + for step in (0..step_count).map(|i| step_size * i as f64) { let x = f64::cos(start_angle + step) * radius; let z = f64::sin(start_angle + step) * radius; @@ -48,7 +62,11 @@ impl Painter3d { .filter_map(|point| self.vec3_to_pos2(point)) .collect::>(); - self.painter.add(Shape::line(points, stroke)) + self.painter.add(if closed { + Shape::closed_line(points, stroke) + } else { + Shape::line(points, stroke) + }) } pub fn circle(&self, radius: f64, stroke: impl Into) -> ShapeIdx { diff --git a/src/subgizmo/rotation.rs b/src/subgizmo/rotation.rs index b2ff9be..051d5e6 100644 --- a/src/subgizmo/rotation.rs +++ b/src/subgizmo/rotation.rs @@ -1,7 +1,7 @@ use std::f64::consts::{FRAC_PI_2, PI, TAU}; use egui::Ui; -use glam::{DMat4, DQuat, DVec2, DVec3}; +use glam::{DMat3, DMat4, DQuat, DVec2, DVec3}; use crate::math::{ray_to_plane_origin, rotation_align, round_to_interval, world_to_screen}; use crate::painter::Painter3d; @@ -154,38 +154,46 @@ fn arc_angle(subgizmo: &SubGizmoConfig) -> f64 { let min_dot = 0.990; let max_dot = 0.995; - f64::min(1.0, f64::max(0.0, dot - min_dot) / (max_dot - min_dot)) * FRAC_PI_2 + FRAC_PI_2 + let mut angle = + f64::min(1.0, f64::max(0.0, dot - min_dot) / (max_dot - min_dot)) * FRAC_PI_2 + FRAC_PI_2; + if (angle - PI).abs() < 1e-2 { + angle = PI; + } + angle } /// Calculates a matrix used when rendering the rotation axis. fn rotation_matrix(subgizmo: &SubGizmoConfig) -> DMat4 { + if subgizmo.direction == GizmoDirection::Screen { + let forward = subgizmo.config.view_forward(); + let right = subgizmo.config.view_right(); + let up = subgizmo.config.view_up(); + + let rotation = DQuat::from_mat3(&DMat3::from_cols(up, -forward, -right)); + + return DMat4::from_rotation_translation(rotation, subgizmo.config.translation); + } + // First rotate towards the gizmo normal let local_normal = subgizmo.local_normal(); let rotation = rotation_align(DVec3::Y, local_normal); let mut rotation = DQuat::from_mat3(&rotation); let config = subgizmo.config; - // TODO optimize this. Use same code for all axes if possible. - - if subgizmo.direction != GizmoDirection::Screen { - if config.local_space() { - rotation = config.rotation * rotation; - } - - let tangent = tangent(subgizmo); - let normal = subgizmo.normal(); - let mut forward = config.view_forward(); - if config.left_handed { - forward *= -1.0; - } - let angle = f64::atan2(tangent.cross(forward).dot(normal), tangent.dot(forward)); + if config.local_space() { + rotation = config.rotation * rotation; + } - // Rotate towards the camera, along the rotation axis. - rotation = DQuat::from_axis_angle(normal, angle) * rotation; - } else { - let angle = f64::atan2(local_normal.x, local_normal.z) + FRAC_PI_2; - rotation = DQuat::from_axis_angle(local_normal, angle) * rotation; + let tangent = tangent(subgizmo); + let normal = subgizmo.normal(); + let mut forward = config.view_forward(); + if config.left_handed { + forward *= -1.0; } + let angle = f64::atan2(tangent.cross(forward).dot(normal), tangent.dot(forward)); + + // Rotate towards the camera, along the rotation axis. + rotation = DQuat::from_axis_angle(normal, angle) * rotation; DMat4::from_rotation_translation(rotation, config.translation) }