diff --git a/src-tauri/render3d/src/gpu.rs b/src-tauri/render3d/src/gpu.rs index 7080555..e9ebee9 100644 --- a/src-tauri/render3d/src/gpu.rs +++ b/src-tauri/render3d/src/gpu.rs @@ -46,6 +46,11 @@ pub enum RenderStyle { /// Hidden-Line: flach-weisse Flaechen (fuellen den Depth-Buffer) + dunkle /// Kanten obenauf, tiefengetestet -> nur sichtbare Kanten erscheinen. Hidden, + /// "Schattiert mit Kanten" (typischer BIM-Look, z. B. Revit/ArchiCAD): wie + /// `Shaded` (echte Bauteilfarben, beleuchtet), ZUSAETZLICH die dunklen + /// Modell-Kanten obenauf (wie bei `Hidden`, aber mit Material- statt + /// Weiss-Flaechen). + ShadedEdges, } impl RenderStyle { @@ -56,18 +61,20 @@ impl RenderStyle { "textured" => RenderStyle::Textured, "wireframe" => RenderStyle::Wireframe, "hidden" => RenderStyle::Hidden, + "shaded-edges" => RenderStyle::ShadedEdges, _ => RenderStyle::Shaded, } } /// `mode.x`-Wert fuer den Mesh-Shader (siehe shaders.rs): 0 = Material/shaded - /// (auch textured), 1 = weiss/Clay, 2 = flach-weisse Hidden-Line-Flaechen. + /// (auch textured/shaded-edges), 1 = weiss/Clay, 2 = flach-weisse + /// Hidden-Line-Flaechen. fn mesh_mode(self) -> f32 { match self { RenderStyle::White => 1.0, RenderStyle::Hidden => 2.0, // Shaded, Textured, Wireframe (Flaechen werden bei Wireframe eh nicht - // gezeichnet) -> Material. + // gezeichnet), ShadedEdges -> Material. _ => 0.0, } } @@ -79,7 +86,19 @@ impl RenderStyle { /// Werden in diesem Stil Modell-Kanten gezeichnet? fn draws_edges(self) -> bool { - matches!(self, RenderStyle::Wireframe | RenderStyle::Hidden) + matches!( + self, + RenderStyle::Wireframe | RenderStyle::Hidden | RenderStyle::ShadedEdges + ) + } + + /// Braucht dieser Stil die leicht nach hinten gebiaste Flaechen-Pipeline, + /// damit die (tiefengetesteten) Kanten sauber obenauf liegen, statt mit den + /// Flaechen zu flackern (Z-Fighting)? Gilt fuer alle Stile, die BEIDES + /// zeichnen (Flaechen + Kanten) — bislang nur `Hidden`, jetzt auch + /// `ShadedEdges`. + fn needs_biased_face_pipeline(self) -> bool { + matches!(self, RenderStyle::Hidden | RenderStyle::ShadedEdges) } } @@ -1343,7 +1362,7 @@ impl Renderer { pass.draw_indexed(0..t.index_count, 0, 0..1); } } else if let Some(m) = &self.mesh { - let face_pipeline = if self.style == RenderStyle::Hidden { + let face_pipeline = if self.style.needs_biased_face_pipeline() { &self.hidden_face_pipeline } else { &self.pipeline @@ -1418,3 +1437,48 @@ impl Renderer { queue.submit(std::iter::once(encoder.finish())); } } + +#[cfg(test)] +mod render_style_tests { + use super::RenderStyle; + + #[test] + fn shaded_edges_parses_from_api_string() { + assert_eq!( + RenderStyle::from_str_or_shaded("shaded-edges"), + RenderStyle::ShadedEdges + ); + } + + #[test] + fn unknown_style_falls_back_to_shaded() { + assert_eq!(RenderStyle::from_str_or_shaded("bogus"), RenderStyle::Shaded); + } + + #[test] + fn shaded_edges_uses_material_colors_like_shaded() { + // mode.x = 0.0 -> Material/Bauteilfarben, NICHT weiss (1.0) oder + // Hidden-Line-Flaechen (2.0) — der BIM-Look behaelt echte Farben. + assert_eq!(RenderStyle::ShadedEdges.mesh_mode(), 0.0); + assert_eq!(RenderStyle::Shaded.mesh_mode(), RenderStyle::ShadedEdges.mesh_mode()); + } + + #[test] + fn shaded_edges_draws_both_faces_and_edges() { + assert!(RenderStyle::ShadedEdges.draws_faces()); + assert!(RenderStyle::ShadedEdges.draws_edges()); + } + + #[test] + fn shaded_edges_needs_biased_face_pipeline_like_hidden() { + assert!(RenderStyle::ShadedEdges.needs_biased_face_pipeline()); + assert!(RenderStyle::Hidden.needs_biased_face_pipeline()); + assert!(!RenderStyle::Shaded.needs_biased_face_pipeline()); + } + + #[test] + fn plain_shaded_style_unaffected_by_new_variant() { + assert!(!RenderStyle::Shaded.draws_edges()); + assert!(RenderStyle::Shaded.draws_faces()); + } +} diff --git a/src-tauri/render3d/src/web.rs b/src-tauri/render3d/src/web.rs index 69263b2..d5cebdc 100644 --- a/src-tauri/render3d/src/web.rs +++ b/src-tauri/render3d/src/web.rs @@ -389,6 +389,9 @@ impl WebModelRenderer { /// - `"wireframe"`— nur Modell-Kanten (Feature-Edges), keine Flaechen. /// - `"hidden"` — Hidden-Line: flach-weisse Flaechen fuellen den Depth- /// Buffer, dunkle Kanten liegen tiefengetestet obenauf (nur sichtbare). + /// - `"shaded-edges"` — "Schattiert mit Kanten" (typischer BIM-Look): wie + /// `"shaded"` (echte Bauteilfarben), ZUSAETZLICH dunkle Modell-Kanten + /// obenauf wie bei `"hidden"`. /// Unbekannte Werte fallen auf `"shaded"` zurueck. Wirkt erst beim naechsten /// `render`. Loest `set_render_mode_white` ab (das bleibt kompatibel). pub fn set_render_style(&mut self, style: &str) -> Result<(), JsValue> { diff --git a/src/i18n/de.ts b/src/i18n/de.ts index cbb6f04..707d657 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -84,6 +84,7 @@ export const de = { "display.style.color": "Farbig", "display.style.mono": "Schwarz-Weiss", "display.style.shaded": "Schattiert", + "display.style.shadedEdges": "Schattiert mit Kanten", "display.style.white": "Weiss", "display.style.textured": "Texturiert", "display.style.wireframe": "Drahtgitter", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 80a3a26..9e6cfa4 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -83,6 +83,7 @@ export const en: Record = { "display.style.color": "Color", "display.style.mono": "Black & white", "display.style.shaded": "Shaded", + "display.style.shadedEdges": "Shaded with edges", "display.style.white": "White", "display.style.textured": "Textured", "display.style.wireframe": "Wireframe", diff --git a/src/ui/TopBar.tsx b/src/ui/TopBar.tsx index 3a778f9..013b345 100644 --- a/src/ui/TopBar.tsx +++ b/src/ui/TopBar.tsx @@ -50,15 +50,17 @@ export type DetailLevel = "grob" | "mittel" | "fein"; /** * Render-/Anzeigemodus der 3D-Ansicht: schattiert, einheitlich weiss (Clay-Look), * texturiert (echte PBR-Materialien je Bauteil), Drahtgitter, Verdeckte Kanten - * (Hidden-Line). Wirkt nur in der Perspektive. Der Slot „section" (Schnitt- - * schraffur, Welle B) ist bewusst freigelassen. + * (Hidden-Line), Schattiert mit Kanten (typischer BIM-Look: Bauteilfarben + + * Modell-Kanten obenauf). Wirkt nur in der Perspektive. Der Slot „section" + * (Schnittschraffur, Welle B) ist bewusst freigelassen. */ export type RenderMode = | "shaded" | "white" | "textured" | "wireframe" - | "hidden"; + | "hidden" + | "shaded-edges"; /** * Kanonischer Blickwinkel der 3D-Ansicht (Vectorworks/DOSSIER-Stil). „front"/ @@ -1591,6 +1593,7 @@ export function ViewRibbonTab({ title={t("display.style.hint")} options={[ { value: "shaded", label: t("display.style.shaded") }, + { value: "shaded-edges", label: t("display.style.shadedEdges") }, { value: "white", label: t("display.style.white") }, { value: "textured", label: t("display.style.textured") }, { value: "wireframe", label: t("display.style.wireframe") }, @@ -1796,6 +1799,7 @@ export function Ribbon3dTab({ title={t("display.style.hint")} options={[ { value: "shaded", label: t("display.style.shaded") }, + { value: "shaded-edges", label: t("display.style.shadedEdges") }, { value: "white", label: t("display.style.white") }, { value: "textured", label: t("display.style.textured") }, { value: "wireframe", label: t("display.style.wireframe") },