git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commited33cd65164acc16c133579bc9fb782899619c2e
parent0db58d693e
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-29 20:10
fix: the point numbers keep their dimming across a scene rebuild

The 2D frame is painted before the stage pass works the dimming out, so
a rebuild that cleared it drew one frame of every number at full
strength, and a playing simulation rebuilds at every frame.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

 CLAUDE.md     |  8 +++++++-
 src/app.rs    |  8 +++++++-
 src/main.rs   | 24 ++++++++++++++++++++++++
 src/render.rs |  9 +++++++++
 4 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index d5d69ed..07b5d7a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2513,7 +2513,13 @@ stage pass, for the eye being staged; a label under 2% is not drawn, so
 behind an OPAQUE face a number is hidden, where until then every number
 showed through everything. The wires are not counted against a number: a
 line a pixel wide is not in front of a label in any way one alpha could
-show. `a_point_number_is_dimmed_by_the_fill_in_front_of_it` is the test;
+show. **A scene rebuild works the dimming out itself**, for the view the
+scene was last staged from (`last_scene_mvp`, `last_scene_eye`). The 2D
+frame is painted BEFORE the stage pass, so a rebuild that only cleared
+the alphas drew one frame of every number at full strength; a playing
+simulation rebuilds at every frame, and the numbers flickered
+(`a_scene_rebuild_keeps_the_point_numbers_dimmed`).
+`a_point_number_is_dimmed_by_the_fill_in_front_of_it` is the test;
 the whiskers' order has none, being a draw list only a renderer reads, and
 was checked in a shadow session before and after.
 
diff --git a/src/app.rs b/src/app.rs
index 35a13d1..b99bc07 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -2486,6 +2486,10 @@ pub struct State {
     /// The raster scene's model-view-projection and the viewport pane rect in
     /// LOGICAL px, cached at staging so the 2D pass can project 3D overlays.
     pub last_scene_mvp: Option<Mat4>,
+    /// The eye the scene was last staged for, in mesh space, beside the
+    /// matrix: what a scene rebuild dims the numbers by until the stage
+    /// pass has staged the new geometry.
+    pub last_scene_eye: Vec3,
     pub last_scene_view_rect: (f32, f32, f32, f32),
     /// The active curve viewer state (viewport point editing), if any.
     pub viewer_tool: Option<crate::viewer_state::ViewerTool>,
@@ -6989,6 +6993,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 .unwrap_or(cce_ui::units::Unit::Mm),
             pick_cache: None,
             last_scene_mvp: None,
+            last_scene_eye: Vec3::ZERO,
             last_scene_view_rect: (0.0, 0.0, 0.0, 0.0),
             viewer_tool: None,
             last_viewport_rt_mode: false,
@@ -11178,7 +11183,8 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                     self.last_scene_mvp = Some(mvp_mat);
                     self.last_scene_view_rect =
                         (sx as f32 / s, sy as f32 / s, cw as f32 / s, ch as f32 / s);
-                    self.sync_point_number_alpha(mvp_mat, (view_mat * model).inverse().transform_point3(Vec3::ZERO));
+                    self.last_scene_eye = (view_mat * model).inverse().transform_point3(Vec3::ZERO);
+                    self.sync_point_number_alpha(mvp_mat, self.last_scene_eye);
 
                     // The camera-pivot marker is WORLD-FIXED at the pivot point, like
                     // the origin gizmo. Its old yaw rotation existed to keep it glued
diff --git a/src/main.rs b/src/main.rs
index 381a946..57f19b1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1686,6 +1686,30 @@ mod tests {
         let _ = fs::remove_dir_all(dir.parent().unwrap());
     }
 
+    /// A scene rebuild leaves the numbers dimmed as they were: the 2D frame
+    /// is painted before the stage pass, so a rebuild that cleared the
+    /// dimming drew one frame of every number at full strength, and a
+    /// playing simulation rebuilds at every frame.
+    #[test]
+    fn a_scene_rebuild_keeps_the_point_numbers_dimmed() {
+        let mut state = State::new(false);
+        state.show_point_numbers = true;
+        state.rebuild_scene_geometry();
+        assert!(!state.overlay_number_labels.is_empty());
+        // The view the stage pass last staged: from +z, looking at the origin.
+        let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 6.0), Vec3::ZERO, Vec3::Y);
+        let proj = Mat4::perspective_rh(0.9, 1.5, 0.1, 100.0);
+        state.last_scene_mvp = Some(proj * view);
+        state.last_scene_eye = Vec3::new(0.0, 0.0, 6.0);
+        state.sync_point_number_alpha(proj * view, state.last_scene_eye);
+        let staged = state.overlay_number_alpha.clone();
+        assert!(staged.iter().any(|a| *a < 0.02), "the far side's numbers are hidden: {staged:?}");
+        assert!(staged.iter().any(|a| *a > 0.98), "the near side's are shown");
+
+        state.rebuild_scene_geometry();
+        assert_eq!(state.overlay_number_alpha, staged, "the rebuild left the dimming as the view has it");
+    }
+
     /// A point number is dimmed by the fill in front of its point, as a
     /// marker drawn under that fill is: whole on the near side, one layer
     /// down on the far side of a closed mesh (the faces that meet AT the
diff --git a/src/render.rs b/src/render.rs
index cb40d96..40fd455 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -1280,6 +1280,15 @@ impl State {
             cce_ui::colors::to_linear_rgb,
         ));
         self.overlay_dirty = true;
+        // The numbers' dimming, for the view the scene was last staged
+        // from. The 2D frame is painted BEFORE the stage pass, which works
+        // the dimming out: left cleared, the first frame after every
+        // rebuild drew each number at full strength, those behind the
+        // surface too, and a playing simulation rebuilds every frame — the
+        // numbers flickered between shown and dimmed.
+        if let Some(mvp) = self.last_scene_mvp {
+            self.sync_point_number_alpha(mvp, self.last_scene_eye);
+        }
         // The pull arrows measure the selected node against this new
         // geometry version; a playing simnet reaches here every frame.
         self.sync_selection_readouts();