graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: vertex markers and vertex normals
A vertex's marker and the foot of its normal stand where its number
does, inset from its point toward its primitive's centroid. The normal
is the vertex's own N where the detail carries one, its primitive's
where it does not. The markers share the point markers' mesh and are
re-sized with them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CLAUDE.md | 18 +++++++-
src/app.rs | 45 ++++++++++++++++--
src/command.rs | 2 +
src/dialog.rs | 2 +
src/main.rs | 48 +++++++++++++++++--
src/render.rs | 140 +++++++++++++++++++++++++++++++++++++++-----------------
src/shortcut.rs | 2 +
7 files changed, 205 insertions(+), 52 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index faa5af2..c53de60 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2184,7 +2184,7 @@ opacity and Show Occluded) and **Markers** (what is drawn on it: Show
Points, Point Size, Group Marker Scale and Pull Arrow Scale; then the
overlays a class at a time — Show Point Markers and its size, Show Point
Numbers, Show Point Normals; Show Primitive Numbers, Show Primitive
-Normals; Show Vertex Numbers).
+Normals; Show Vertex Markers, Show Vertex Numbers, Show Vertex Normals).
**The submenus are cce-ui's** (`context_menu::set_row_submenu`, see its
CLAUDE.md, "A row can open a submenu"): a second menu in its own popup
@@ -2220,6 +2220,22 @@ labels a class, and dimmed by the fill in front of them as the point
numbers are (one `point_transmittance` call over all three lists).
`primitives_and_vertices_are_numbered_where_they_are` is the test.
+**Vertex markers and vertex normals** (`toggle_vertex_markers`,
+`toggle_vertex_normals`, later the same day) stand where the vertex's
+number does, so all three of a vertex's overlays name one place. The
+markers are the point markers' spheres at `VERTEX_MARKER_SCALE` (0.6) of
+Point Marker Size, in the vertex green, smaller so a point's marker is not
+lost among the markers of the vertices around it; they share the point
+markers' mesh, and `rebuild_overlay_marker_verts` builds both lists, so
+the size slider re-sizes both without an evaluation. **A vertex's normal
+is its `N` attribute where the detail carries a Float3 one on its
+vertices, and its primitive's normal where it does not** — the normal of
+this corner of this face, where a point's is the average over the faces
+around it. No node here writes vertex normals yet, so today the whiskers
+show the faceting: the corners of one face agree, and a shared point
+wears a fan of them. `scene_element_overlays` takes an `ElementOverlays`
+of what is wanted and returns an `ElementOverlayGeometry`.
+
`the_viewport_menu_groups_its_display_rows` holds the
order of all three. The rows: the Show Wireframe switch (its registry command), **Flat
Shading / Smooth Shading** as a radio pair over `toggle_smooth_shading`,
diff --git a/src/app.rs b/src/app.rs
index d5c693e..c18bf75 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1283,6 +1283,10 @@ pub struct ViewportSettings {
pub show_prim_normals: bool,
#[serde(default)]
pub show_vertex_numbers: bool,
+ #[serde(default)]
+ pub show_vertex_markers: bool,
+ #[serde(default)]
+ pub show_vertex_normals: bool,
/// World-unit radius and colour of the Show Point Markers overlay.
#[serde(default = "default_point_marker_size")]
pub point_marker_size: f32,
@@ -1485,6 +1489,8 @@ impl Default for ViewportSettings {
show_prim_numbers: false,
show_prim_normals: false,
show_vertex_numbers: false,
+ show_vertex_markers: false,
+ show_vertex_normals: false,
point_marker_size: default_point_marker_size(),
point_marker_color: default_point_marker_color(),
world_unit: default_world_unit(),
@@ -2424,6 +2430,9 @@ pub struct State {
/// Marker Size slider does that on every motion of a drag
/// (`rebuild_overlay_marker_verts`).
pub overlay_marker_points: Vec<Vertex3D>,
+ /// The same for Show Vertex Markers: where each vertex's marker
+ /// stands, inset from its point as its number is.
+ pub overlay_vertex_marker_points: Vec<Vertex3D>,
pub overlay_dirty: bool,
pub overlay_point_count: u32,
pub overlay_number_labels: Vec<([f32; 3], u32)>,
@@ -2454,6 +2463,8 @@ pub struct State {
pub show_prim_numbers: bool,
pub show_prim_normals: bool,
pub show_vertex_numbers: bool,
+ pub show_vertex_markers: bool,
+ pub show_vertex_normals: bool,
/// The visible scene's own edges for the wire pass (LINE_LIST pairs),
/// rebuilt with the scene while Show Wireframe is on and empty while it
/// is off. Topological — see `render::scene_edge_verts`.
@@ -2636,6 +2647,8 @@ impl State {
show_prim_numbers: self.show_prim_numbers,
show_prim_normals: self.show_prim_normals,
show_vertex_numbers: self.show_vertex_numbers,
+ show_vertex_markers: self.show_vertex_markers,
+ show_vertex_normals: self.show_vertex_normals,
point_marker_size: self.point_marker_size,
point_marker_color: self.point_marker_color,
world_unit: self.world_unit.suffix().to_string(),
@@ -2714,6 +2727,8 @@ impl State {
self.show_prim_numbers = v.show_prim_numbers;
self.show_prim_normals = v.show_prim_normals;
self.show_vertex_numbers = v.show_vertex_numbers;
+ self.show_vertex_markers = v.show_vertex_markers;
+ self.show_vertex_normals = v.show_vertex_normals;
self.point_marker_size = v.point_marker_size;
self.point_marker_color = v.point_marker_color;
if let Some(u) = cce_ui::units::Unit::parse(&v.world_unit) {
@@ -5387,7 +5402,9 @@ impl State {
toggle(&mut options, &mut actions, "toggle_prim_numbers");
toggle(&mut options, &mut actions, "toggle_prim_normals");
row(&mut options, &mut actions, "-".into(), sep);
+ toggle(&mut options, &mut actions, "toggle_vertex_markers");
toggle(&mut options, &mut actions, "toggle_vertex_numbers");
+ toggle(&mut options, &mut actions, "toggle_vertex_normals");
return (options, actions);
}
None => {}
@@ -6267,16 +6284,23 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
/// current size — the cheap half of the markers, with no evaluation, so
/// a size change can run it on every motion of a drag. The Highlight
/// bake's warm accent, so the markers and the tint read as one feature.
- /// Build the Show Point Markers overlay's spheres from the kept scene
- /// positions at the current Point Marker Size — the same call
- /// `render::scene_point_overlays` makes, without the evaluation that
- /// produced the positions.
+ /// Build the marker overlays' spheres from the kept scene positions at
+ /// the current Point Marker Size, without the evaluation that produced
+ /// the positions: the points' in the marker colour, and the vertices'
+ /// in the vertex overlays' green at `VERTEX_MARKER_SCALE` of the size —
+ /// smaller, so that a point's marker is not lost among the markers of
+ /// the vertices around it.
pub(crate) fn rebuild_overlay_marker_verts(&mut self) {
self.overlay_marker_verts = crate::geometry::points_vertices(
&self.overlay_marker_points,
self.point_marker_size,
cce_ui::colors::to_linear_rgb(self.point_marker_color),
);
+ self.overlay_marker_verts.extend(crate::geometry::points_vertices(
+ &self.overlay_vertex_marker_points,
+ self.point_marker_size * crate::render::VERTEX_MARKER_SCALE,
+ cce_ui::colors::to_linear_rgb(crate::render::VERTEX_LABEL_COLOR.map(|c| c as f32 / 255.0)),
+ ));
self.overlay_dirty = true;
}
@@ -6711,6 +6735,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
last_group_marker_size: 0.0,
overlay_marker_verts: Vec::new(),
overlay_marker_points: Vec::new(),
+ overlay_vertex_marker_points: Vec::new(),
overlay_dirty: false,
overlay_point_count: 0,
overlay_number_labels: Vec::new(),
@@ -6727,6 +6752,8 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
show_prim_numbers: settings.viewport.show_prim_numbers,
show_prim_normals: settings.viewport.show_prim_normals,
show_vertex_numbers: settings.viewport.show_vertex_numbers,
+ show_vertex_markers: settings.viewport.show_vertex_markers,
+ show_vertex_normals: settings.viewport.show_vertex_normals,
scene_edge_verts: Vec::new(),
point_marker_size: settings.viewport.point_marker_size,
point_marker_color: settings.viewport.point_marker_color,
@@ -8371,6 +8398,16 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
self.rebuild_scene_geometry();
settings_changed = true;
}
+ Action::ToggleVertexMarkers => {
+ self.show_vertex_markers = !self.show_vertex_markers;
+ self.rebuild_scene_geometry();
+ settings_changed = true;
+ }
+ Action::ToggleVertexNormals => {
+ self.show_vertex_normals = !self.show_vertex_normals;
+ self.rebuild_scene_geometry();
+ settings_changed = true;
+ }
Action::ToggleVertexNumbers => {
self.show_vertex_numbers = !self.show_vertex_numbers;
self.rebuild_scene_geometry();
diff --git a/src/command.rs b/src/command.rs
index 9b32993..5c19447 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -214,7 +214,9 @@ pub const COMMANDS: &[Command] = &[
Command { id: "toggle_point_normals", label: "Show Point Normals", context: Context::Viewport, run: Run::Key(Action::TogglePointNormals), default_chord: None },
Command { id: "toggle_prim_numbers", label: "Show Primitive Numbers", context: Context::Viewport, run: Run::Key(Action::TogglePrimNumbers), default_chord: None },
Command { id: "toggle_prim_normals", label: "Show Primitive Normals", context: Context::Viewport, run: Run::Key(Action::TogglePrimNormals), default_chord: None },
+ Command { id: "toggle_vertex_markers", label: "Show Vertex Markers", context: Context::Viewport, run: Run::Key(Action::ToggleVertexMarkers), default_chord: None },
Command { id: "toggle_vertex_numbers", label: "Show Vertex Numbers", context: Context::Viewport, run: Run::Key(Action::ToggleVertexNumbers), default_chord: None },
+ Command { id: "toggle_vertex_normals", label: "Show Vertex Normals", context: Context::Viewport, run: Run::Key(Action::ToggleVertexNormals), default_chord: None },
Command { id: "toggle_render_points", label: "Show Points", context: Context::Viewport, run: Run::Key(Action::ToggleRenderPoints), default_chord: None },
Command { id: "toggle_wire_single_color", label: "Wireframe Single Color", context: Context::Viewport, run: Run::Key(Action::ToggleWireSingleColor), default_chord: None },
Command { id: "toggle_ray_traced_preview", label: "Ray Traced Preview", context: Context::Viewport, run: Run::Key(Action::ToggleRayTracedPreview), default_chord: None },
diff --git a/src/dialog.rs b/src/dialog.rs
index a009d60..7eae6b9 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -1611,6 +1611,8 @@ impl State {
"toggle_prim_numbers" => self.show_prim_numbers,
"toggle_prim_normals" => self.show_prim_normals,
"toggle_vertex_numbers" => self.show_vertex_numbers,
+ "toggle_vertex_markers" => self.show_vertex_markers,
+ "toggle_vertex_normals" => self.show_vertex_normals,
"toggle_render_points" => self.render_points,
"toggle_wire_single_color" => self.wire_single_color,
"toggle_ray_traced_preview" => self.viewport().rt_mode,
diff --git a/src/main.rs b/src/main.rs
index 3949b7c..3b0551e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2064,7 +2064,11 @@ mod tests {
A::Command("toggle_point_normals"),
],
vec![A::Command("toggle_prim_numbers"), A::Command("toggle_prim_normals")],
- vec![A::Command("toggle_vertex_numbers")],
+ vec![
+ A::Command("toggle_vertex_markers"),
+ A::Command("toggle_vertex_numbers"),
+ A::Command("toggle_vertex_normals"),
+ ],
]
);
}
@@ -2144,10 +2148,13 @@ mod tests {
.collect();
d.add_prim(&[p[0], p[1], p[2], p[3]]);
d.add_prim(&[p[1], p[4], p[2]]);
- let (prims, verts, normals) = crate::render::scene_element_overlays(&d, false, false, false, 1.0);
- assert!(prims.is_empty() && verts.is_empty() && normals.is_empty());
+ use crate::render::ElementOverlays as Want;
+ let none = crate::render::scene_element_overlays(&d, Want::default(), 1.0);
+ assert!(none.prim_labels.is_empty() && none.vertex_labels.is_empty() && none.normals.is_empty() && none.vertex_markers.is_empty());
- let (prims, verts, normals) = crate::render::scene_element_overlays(&d, true, true, true, 0.5);
+ let want = Want { prim_numbers: true, prim_normals: true, vertex_numbers: true, ..Want::default() };
+ let got = crate::render::scene_element_overlays(&d, want, 0.5);
+ let (prims, verts, normals) = (got.prim_labels, got.vertex_labels, got.normals);
assert_eq!(prims.len(), 2);
assert_eq!(prims[0], ([1.0, 1.0, 0.0], 0));
assert_eq!(prims[1].1, 1);
@@ -2161,6 +2168,39 @@ mod tests {
let n = Vec3::from_array(normals[1].position) - Vec3::from_array(normals[0].position);
assert!((n - Vec3::new(0.0, 0.0, 0.5)).length() < 1e-6, "{n:?}");
+ // A vertex's marker stands where its number does, and its normal
+ // is its primitive's: on a mesh that carries no vertex normals the
+ // corners of one face agree and the faces around a point do not.
+ let got = crate::render::scene_element_overlays(&d, Want { vertex_markers: true, vertex_normals: true, ..Want::default() }, 0.5);
+ assert_eq!(got.vertex_markers.len(), 7);
+ assert_eq!(got.vertex_markers.iter().map(|m| m.position).collect::<Vec<_>>(), verts.iter().map(|(p, _)| *p).collect::<Vec<_>>());
+ assert_eq!(got.normals.len(), 14, "a whisker a vertex");
+ assert_eq!(got.normals[2].position, verts[1].0, "from where the vertex stands");
+ let n = Vec3::from_array(got.normals[3].position) - Vec3::from_array(got.normals[2].position);
+ assert!((n - Vec3::new(0.0, 0.0, 0.5 * crate::render::VERTEX_MARKER_SCALE)).length() < 1e-6, "{n:?}");
+ // Its own N, where the detail carries one on its vertices.
+ let mut tilted = d.clone();
+ tilted.verts_mut().create("N", crate::detail::AttribValue::Float3([1.0, 0.0, 0.0]));
+ let got = crate::render::scene_element_overlays(&tilted, Want { vertex_normals: true, ..Want::default() }, 1.0);
+ let n = Vec3::from_array(got.normals[1].position) - Vec3::from_array(got.normals[0].position);
+ assert!((n.normalize() - Vec3::X).length() < 1e-6, "{n:?}");
+
+ // The markers are built with the points', and re-sized with them.
+ let mut state = State::new(false);
+ state.show_point_markers = false;
+ state.show_vertex_markers = false;
+ state.rebuild_scene_geometry();
+ assert!(state.overlay_marker_verts.is_empty());
+ state.run_command("toggle_vertex_markers");
+ let built = state.overlay_marker_verts.clone();
+ assert!(!built.is_empty() && !state.overlay_vertex_marker_points.is_empty());
+ state.point_marker_size *= 2.0;
+ state.rebuild_overlay_marker_verts();
+ assert_eq!(state.overlay_marker_verts.len(), built.len());
+ assert_ne!(state.overlay_marker_verts[0].position, built[0].position, "re-sized");
+ state.run_command("toggle_vertex_markers");
+ assert!(state.overlay_marker_verts.is_empty(), "and gone with the switch");
+
// And the app collects them by its switches, and persists those.
let mut state = State::new(false);
state.show_prim_numbers = false;
diff --git a/src/render.rs b/src/render.rs
index e6d6e80..47a3dcb 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -1247,18 +1247,27 @@ impl State {
self.overlay_number_labels = labels;
self.overlay_number_alpha.clear();
self.overlay_normal_verts = normals;
- let (prim_labels, vertex_labels, prim_normals) = scene_element_overlays(
+ let elements = scene_element_overlays(
&geom,
- self.show_prim_numbers,
- self.show_vertex_numbers,
- self.show_prim_normals,
+ ElementOverlays {
+ prim_numbers: self.show_prim_numbers,
+ prim_normals: self.show_prim_normals,
+ vertex_numbers: self.show_vertex_numbers,
+ vertex_markers: self.show_vertex_markers,
+ vertex_normals: self.show_vertex_normals,
+ },
self.point_marker_size * 4.0,
);
- self.overlay_prim_labels = prim_labels;
+ self.overlay_prim_labels = elements.prim_labels;
self.overlay_prim_alpha.clear();
- self.overlay_vertex_labels = vertex_labels;
+ self.overlay_vertex_labels = elements.vertex_labels;
self.overlay_vertex_alpha.clear();
- self.overlay_normal_verts.extend(prim_normals);
+ self.overlay_normal_verts.extend(elements.normals);
+ self.overlay_vertex_marker_points = elements.vertex_markers;
+ if !self.overlay_vertex_marker_points.is_empty() {
+ // Both marker lists, by the one builder the size slider uses.
+ self.rebuild_overlay_marker_verts();
+ }
// The wire pass's edges, likewise — topological, and only while the
// wireframe is actually on.
self.scene_edge_verts =
@@ -1428,35 +1437,62 @@ pub(crate) const VERTEX_LABEL_COLOR: [u8; 3] = [0xa0, 0xf0, 0xb0];
/// near enough to say which corner each is.
pub(crate) const VERTEX_LABEL_INSET: f32 = 0.3;
+/// A vertex marker's radius as a fraction of Point Marker Size.
+pub(crate) const VERTEX_MARKER_SCALE: f32 = 0.6;
+
+/// Which of the primitive and vertex overlays to collect.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
+pub(crate) struct ElementOverlays {
+ pub prim_numbers: bool,
+ pub prim_normals: bool,
+ pub vertex_numbers: bool,
+ pub vertex_markers: bool,
+ pub vertex_normals: bool,
+}
+
+/// What `scene_element_overlays` collected: `(position, index)` labels for
+/// the two kinds of number, the places the vertex markers stand, and the
+/// LINE_LIST whiskers of both kinds of normal.
+#[derive(Debug, Clone, Default)]
+pub(crate) struct ElementOverlayGeometry {
+ pub prim_labels: Vec<([f32; 3], u32)>,
+ pub vertex_labels: Vec<([f32; 3], u32)>,
+ pub vertex_markers: Vec<crate::geometry::Vertex3D>,
+ pub normals: Vec<crate::geometry::Vertex3D>,
+}
+
/// The overlays of the other two element classes, read off the same scene
-/// `Detail` as the points': `(position, index)` labels for Show Primitive
-/// Numbers, at each primitive's centroid; the same for Show Vertex Numbers,
-/// each vertex inset from its point toward its primitive's centroid; and
-/// LINE_LIST whiskers for Show Primitive Normals, from the centroid along
-/// the primitive's own normal, `len` long.
+/// `Detail` as the points'. A PRIMITIVE's number stands at its centroid and
+/// its normal is a whisker from there, `len` long. A VERTEX stands inset
+/// from its point toward its primitive's centroid — its number, its marker
+/// and the foot of its normal all there — so the vertices that share a
+/// point are apart, each inside its own primitive.
///
/// A vertex's number is its index in the detail, which is its row in the
-/// spreadsheet, as a point's is. The normal is Newell's, so a quad that is
-/// not quite flat still has one; a primitive of fewer than three points has
-/// a number and no normal. Each list of labels is capped as the points' is.
+/// spreadsheet, as a point's is. A primitive's normal is Newell's, so a
+/// quad that is not quite flat still has one; a primitive of fewer than
+/// three points has a number and no normal. **A vertex's normal is its `N`
+/// attribute where the detail carries one on its vertices, and its
+/// primitive's normal where it does not** — which is what a vertex normal
+/// is for: the normal of this corner of this face, where a point's is the
+/// average over every face around it. So on a mesh with no vertex normals
+/// the whiskers show the faceting, a fan of them at every shared point.
+/// Each list of labels is capped as the points' is.
pub(crate) fn scene_element_overlays(
geom: &crate::detail::Detail,
- prim_numbers: bool,
- vertex_numbers: bool,
- prim_normals: bool,
+ want: ElementOverlays,
len: f32,
-) -> (
- Vec<([f32; 3], u32)>,
- Vec<([f32; 3], u32)>,
- Vec<crate::geometry::Vertex3D>,
-) {
+) -> ElementOverlayGeometry {
use glam::Vec3;
const MAX_LABELS: usize = 2000;
- let (mut prims, mut verts, mut normals) = (Vec::new(), Vec::new(), Vec::new());
- if !(prim_numbers || vertex_numbers || prim_normals) {
- return (prims, verts, normals);
+ let mut out = ElementOverlayGeometry::default();
+ if want == ElementOverlays::default() {
+ return out;
}
- let color = cce_ui::colors::to_linear_rgb(PRIM_LABEL_COLOR.map(|c| c as f32 / 255.0));
+ let to_linear = |c: [u8; 3]| cce_ui::colors::to_linear_rgb(c.map(|c| c as f32 / 255.0));
+ let (prim_color, vertex_color) = (to_linear(PRIM_LABEL_COLOR), to_linear(VERTEX_LABEL_COLOR));
+ let own_normals = geom.verts().get("N").filter(|n| n.ty().components() == 3);
+ let by_vertex = want.vertex_numbers || want.vertex_markers || want.vertex_normals;
for prim in 0..geom.num_prims() {
let pts = geom.prim_points(prim);
if pts.is_empty() {
@@ -1464,31 +1500,49 @@ pub(crate) fn scene_element_overlays(
}
let at: Vec<Vec3> = pts.iter().map(|&p| geom.pos(p as usize)).collect();
let centroid = at.iter().copied().sum::<Vec3>() / at.len() as f32;
- if prim_numbers && prims.len() < MAX_LABELS {
- prims.push((centroid.to_array(), prim as u32));
- }
- if vertex_numbers {
- for (v, &p) in geom.prim_verts(prim).zip(&at) {
- if verts.len() >= MAX_LABELS {
- break;
- }
- verts.push((p.lerp(centroid, VERTEX_LABEL_INSET).to_array(), v as u32));
- }
- }
- if prim_normals && at.len() >= 3 {
+ let normal = if (want.prim_normals || want.vertex_normals) && at.len() >= 3 {
let mut n = Vec3::ZERO;
for (k, &a) in at.iter().enumerate() {
let b = at[(k + 1) % at.len()];
n += Vec3::new((a.y - b.y) * (a.z + b.z), (a.z - b.z) * (a.x + b.x), (a.x - b.x) * (a.y + b.y));
}
- let n = n.normalize_or_zero();
+ n.normalize_or_zero()
+ } else {
+ Vec3::ZERO
+ };
+ let mut whisker = |from: Vec3, n: Vec3, len: f32, color: [f32; 3]| {
if n != Vec3::ZERO {
- normals.push(crate::geometry::Vertex3D { position: centroid.to_array(), color });
- normals.push(crate::geometry::Vertex3D { position: (centroid + n * len).to_array(), color });
+ out.normals.push(crate::geometry::Vertex3D { position: from.to_array(), color });
+ out.normals.push(crate::geometry::Vertex3D { position: (from + n * len).to_array(), color });
+ }
+ };
+ if want.prim_normals {
+ whisker(centroid, normal, len, prim_color);
+ }
+ if by_vertex {
+ for (v, &p) in geom.prim_verts(prim).zip(&at) {
+ let stands = p.lerp(centroid, VERTEX_LABEL_INSET);
+ if want.vertex_normals {
+ let n = own_normals
+ .and_then(|n| n.get(v))
+ .map(|n| n.as_vec3().normalize_or_zero())
+ .filter(|n| *n != Vec3::ZERO)
+ .unwrap_or(normal);
+ whisker(stands, n, len * VERTEX_MARKER_SCALE, vertex_color);
+ }
+ if want.vertex_markers {
+ out.vertex_markers.push(crate::geometry::Vertex3D { position: stands.to_array(), color: [0.0; 3] });
+ }
+ if want.vertex_numbers && out.vertex_labels.len() < MAX_LABELS {
+ out.vertex_labels.push((stands.to_array(), v as u32));
+ }
}
}
+ if want.prim_numbers && out.prim_labels.len() < MAX_LABELS {
+ out.prim_labels.push((centroid.to_array(), prim as u32));
+ }
}
- (prims, verts, normals)
+ out
}
/// The scene's own edges as LINE_LIST pairs for the wire pass, carrying the
diff --git a/src/shortcut.rs b/src/shortcut.rs
index 6a40238..3e82278 100644
--- a/src/shortcut.rs
+++ b/src/shortcut.rs
@@ -31,6 +31,8 @@ pub enum Action {
TogglePrimNumbers,
TogglePrimNormals,
ToggleVertexNumbers,
+ ToggleVertexMarkers,
+ ToggleVertexNormals,
/// The path-traced preview, the wireframe's single-colour mode, and the
/// point display. All three were toggles on the root meta node's utility
/// subnets and reachable ONLY there; with those nodes retired they are