graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: Render-node Opacity slider; retire the Wireframe Overlay toggle
Opacity (0-1, default 1) blends the node geometry — the spheres mesh
and rendered points — over the viewport; scene furniture stays opaque.
The Wireframe Overlay toggle is gone: param dropped from the Render
node (purged from older saves on load), overlay draw branch and state
removed. Show Wireframe now always renders the full cage.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/app.rs | 48 +++++++++++++++++++-----------------------------
src/project.rs | 12 ++++++++----
src/vk_smoke.rs | 6 +++---
3 files changed, 30 insertions(+), 36 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 9d38793..ec9f839 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -991,8 +991,10 @@ pub struct State {
/// Draw the wireframe OVER the shaded geometry (the Render node's
/// "Wireframe Overlay" toggle) — edges visualized without giving up the
/// filled primitives. Wins over `wireframe` when both are set.
- pub wireframe_overlay: bool,
- pub last_viewport_wireframe_overlay: bool,
+ /// Opacity of the rendered node geometry (the Render node's "Opacity"
+ /// slider): 1.0 opaque, straight-alpha blended toward the viewport bg.
+ pub geo_opacity: f32,
+ pub last_viewport_geo_opacity: f32,
/// Point display of the node geometry (the Render node's "Render Points"
/// toggle): one small octahedron per distinct vertex, sized by
/// "Point Size" and tinted by "Point Color".
@@ -2820,8 +2822,8 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
last_viewport_show_viewport: false,
wireframe: false,
last_viewport_wireframe: false,
- wireframe_overlay: false,
- last_viewport_wireframe_overlay: false,
+ geo_opacity: 1.0,
+ last_viewport_geo_opacity: 1.0,
render_points: false,
point_size: 0.02,
point_color: [1.0, 1.0, 1.0],
@@ -5352,7 +5354,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
|| self.last_viewport_active_camera != self.active_camera
|| self.last_viewport_show_viewport != self.show_viewport
|| self.last_viewport_wireframe != self.wireframe
- || self.last_viewport_wireframe_overlay != self.wireframe_overlay
+ || self.last_viewport_geo_opacity != self.geo_opacity
|| self.last_viewport_render_points != self.render_points
|| self.last_viewport_point_size != self.point_size
|| self.last_viewport_point_color != self.point_color;
@@ -5372,41 +5374,29 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
let mvp_pivot = (proj * view_mat * model_pivot).to_cols_array_2d();
// Same draw order as the wgpu pass: bg quad, grid, origin,
- // pivot, cube, spheres.
+ // pivot, cube, spheres. The node geometry (points +
+ // spheres) carries the Render node's Opacity; scene
+ // furniture stays opaque.
const NO_TINT: [f32; 4] = [0.0; 4];
- let mut draws = vec![SceneDraw { mesh: meshes.viewport_bg, mvp, wireframe: false, wire_tint: NO_TINT }];
+ let geo_opacity = self.geo_opacity.clamp(0.0, 1.0);
+ let mut draws = vec![SceneDraw { mesh: meshes.viewport_bg, mvp, wireframe: false, wire_tint: NO_TINT, opacity: 1.0 }];
if self.viewport().show_grid {
- draws.push(SceneDraw { mesh: meshes.grid, mvp, wireframe: false, wire_tint: NO_TINT });
+ draws.push(SceneDraw { mesh: meshes.grid, mvp, wireframe: false, wire_tint: NO_TINT, opacity: 1.0 });
}
if self.viewport().show_origin {
- draws.push(SceneDraw { mesh: meshes.origin, mvp, wireframe: false, wire_tint: NO_TINT });
+ draws.push(SceneDraw { mesh: meshes.origin, mvp, wireframe: false, wire_tint: NO_TINT, opacity: 1.0 });
}
if self.viewport().show_camera_pivot {
- draws.push(SceneDraw { mesh: meshes.pivot, mvp: mvp_pivot, wireframe: false, wire_tint: NO_TINT });
+ draws.push(SceneDraw { mesh: meshes.pivot, mvp: mvp_pivot, wireframe: false, wire_tint: NO_TINT, opacity: 1.0 });
}
if self.viewport().show_cube {
- draws.push(SceneDraw { mesh: meshes.cube, mvp, wireframe: false, wire_tint: NO_TINT });
+ draws.push(SceneDraw { mesh: meshes.cube, mvp, wireframe: false, wire_tint: NO_TINT, opacity: 1.0 });
}
if self.render_points && self.point_vertex_count > 0 {
- draws.push(SceneDraw { mesh: meshes.points, mvp, wireframe: false, wire_tint: NO_TINT });
+ draws.push(SceneDraw { mesh: meshes.points, mvp, wireframe: false, wire_tint: NO_TINT, opacity: geo_opacity });
}
if self.vertex_count_spheres > 0 {
- if self.wireframe_overlay {
- // Shaded + wireframe: the filled mesh, then its
- // wire pass darkened so the edges separate from
- // the identical fill (the wire pipeline's depth
- // bias keeps the lines above the coplanar fill).
- // The tint stays PARTIAL: wires keep a share of
- // their world-anchored vertex colors — a flat
- // black lattice is azimuthally unmarked (a UV
- // sphere's wires self-map every segment step), so
- // it perceptually glues the mesh to the camera
- // during orbits, dominating the fill's cues.
- draws.push(SceneDraw { mesh: meshes.spheres, mvp, wireframe: false, wire_tint: NO_TINT });
- draws.push(SceneDraw { mesh: meshes.spheres, mvp, wireframe: true, wire_tint: [0.0, 0.0, 0.0, 0.45] });
- } else {
- draws.push(SceneDraw { mesh: meshes.spheres, mvp, wireframe: self.wireframe, wire_tint: NO_TINT });
- }
+ draws.push(SceneDraw { mesh: meshes.spheres, mvp, wireframe: self.wireframe, wire_tint: NO_TINT, opacity: geo_opacity });
}
renderer.stage_scene((sx, sy, cw, ch), draws);
}
@@ -5430,7 +5420,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
self.last_viewport_active_camera = self.active_camera.clone();
self.last_viewport_show_viewport = self.show_viewport;
self.last_viewport_wireframe = self.wireframe;
- self.last_viewport_wireframe_overlay = self.wireframe_overlay;
+ self.last_viewport_geo_opacity = self.geo_opacity;
self.last_viewport_render_points = self.render_points;
self.last_viewport_point_size = self.point_size;
self.last_viewport_point_color = self.point_color;
diff --git a/src/project.rs b/src/project.rs
index c2ad3b4..b2326a1 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -270,7 +270,7 @@ impl State {
let show_spreadsheet = self.show_spreadsheet;
let show_playbar = self.show_playbar;
let wireframe = self.wireframe;
- let wireframe_overlay = self.wireframe_overlay;
+ let geo_opacity = self.geo_opacity;
let render_points = self.render_points;
let point_size = self.point_size;
let point_color = self.point_color;
@@ -520,9 +520,13 @@ impl State {
let render_node = find_or_create_subnet(&mut self.fs_root, "Render", "utility", (0.0, 1.0));
render_node.children.clear();
+ // The overlay toggle is retired — drop it from older saves so the
+ // pane doesn't resurrect it.
+ render_node.params.retain(|p| p.name != "Wireframe Overlay");
+
ensure_param(render_node, "Render Settings", "section", "", &[], None, None, None);
ensure_param(render_node, "Show Wireframe", "toggle", bool_str(wireframe), &[], None, None, None);
- ensure_param(render_node, "Wireframe Overlay", "toggle", bool_str(wireframe_overlay), &[], None, None, None);
+ ensure_param(render_node, "Opacity", "slider:0.00:1.00", &format!("{:.2}", geo_opacity), &[], Some(0.0), Some(1.0), None);
ensure_param(render_node, "Render Points", "toggle", bool_str(render_points), &[], None, None, None);
ensure_param(render_node, "Point Size", "slider:0.01:0.30", &format!("{:.2}", point_size), &[], Some(0.01), Some(0.30), None);
ensure_param(render_node, "Point Color", "color", &color_to_hex(point_color), &[], None, None, None);
@@ -530,7 +534,7 @@ impl State {
for p in render_node.params.iter_mut() {
match p.name.as_str() {
"Show Wireframe" => set_toggle(p, wireframe),
- "Wireframe Overlay" => set_toggle(p, wireframe_overlay),
+ "Opacity" => p.default = format!("{:.2}", geo_opacity),
"Render Points" => set_toggle(p, render_points),
"Point Size" => p.default = format!("{:.2}", point_size),
"Point Color" => p.default = color_to_hex(point_color),
@@ -669,7 +673,7 @@ impl State {
for p in ¶ms {
match p.name.as_str() {
"Show Wireframe" => if let Ok(val) = p.default.parse::<bool>() { self.wireframe = val; }
- "Wireframe Overlay" => if let Ok(val) = p.default.parse::<bool>() { self.wireframe_overlay = val; }
+ "Opacity" => if let Ok(val) = p.default.parse::<f32>() { self.geo_opacity = val.clamp(0.0, 1.0); }
"Render Points" => if let Ok(val) = p.default.parse::<bool>() { self.render_points = val; }
"Point Size" => if let Ok(val) = p.default.parse::<f32>() { self.point_size = val.clamp(0.005, 1.0); }
"Point Color" => if let Some(col) = hex_to_color(&p.default) { self.point_color = col; }
diff --git a/src/vk_smoke.rs b/src/vk_smoke.rs
index 8f40f0e..a884233 100644
--- a/src/vk_smoke.rs
+++ b/src/vk_smoke.rs
@@ -474,9 +474,9 @@ fn main() {
renderer.stage_scene(
pane,
vec![
- SceneDraw { mesh: bg_mesh, mvp: Mat4::IDENTITY.to_cols_array_2d(), wireframe: false, wire_tint: [0.0; 4] },
- SceneDraw { mesh: grid_mesh, mvp, wireframe: false, wire_tint: [0.0; 4] },
- SceneDraw { mesh: cube_mesh, mvp, wireframe: false, wire_tint: [0.0; 4] },
+ SceneDraw { mesh: bg_mesh, mvp: Mat4::IDENTITY.to_cols_array_2d(), wireframe: false, wire_tint: [0.0; 4], opacity: 1.0 },
+ SceneDraw { mesh: grid_mesh, mvp, wireframe: false, wire_tint: [0.0; 4], opacity: 1.0 },
+ SceneDraw { mesh: cube_mesh, mvp, wireframe: false, wire_tint: [0.0; 4], opacity: 1.0 },
],
);
}