diff --git a/src/app/commands/layerprops.rs b/src/app/commands/layerprops.rs index 03b810729..4bc57aa64 100644 --- a/src/app/commands/layerprops.rs +++ b/src/app/commands/layerprops.rs @@ -909,6 +909,7 @@ impl OpenCADStudio { let found = self.tabs[i].scene.document.views.get(&name).cloned(); if let Some(v) = found { self.tabs[i].scene.restore_named_view(&v); + self.apply_view_render_mode(i, &v); self.command_line.push_output( crate::tf!("View '{}' restored.", v.name).as_ref(), ); @@ -932,6 +933,7 @@ impl OpenCADStudio { let found = self.tabs[i].scene.document.views.get(&name).cloned(); if let Some(v) = found { self.tabs[i].scene.restore_named_view(&v); + self.apply_view_render_mode(i, &v); self.command_line .push_output(crate::tf!("View '{}' restored.", v.name).as_ref()); } else { diff --git a/src/app/commands/view_presets.rs b/src/app/commands/view_presets.rs index 901deddd1..93fce787f 100644 --- a/src/app/commands/view_presets.rs +++ b/src/app/commands/view_presets.rs @@ -144,6 +144,7 @@ impl OpenCADStudio { }; let before = self.tabs[i].scene.active_gaze_dir(); self.tabs[i].scene.restore_named_view(&view); + self.apply_view_render_mode(i, &view); if view.ucs_associated && self.tabs[i].active_block_edit.is_none() { let world = view.ucs_x_axis == Vector3::new(1.0, 0.0, 0.0) && view.ucs_y_axis == Vector3::new(0.0, 1.0, 0.0) @@ -543,3 +544,33 @@ impl OpenCADStudio { Task::none() } } + +impl OpenCADStudio { + /// A restored view's visual style becomes the model tab's render mode + /// (the closest of OCS's modes); a view without one leaves it. + pub(crate) fn apply_view_render_mode(&mut self, i: usize, view: &codec::tables::View) { + use codec::entities::ViewportRenderMode as M; + if self.tabs[i].scene.current_layout != "Model" { + return; + } + let Some(ObjectType::VisualStyle(style)) = + self.tabs[i].scene.document.objects.get(&view.visual_style_handle) + else { + return; + }; + let (mode, label) = match style.description.as_str() { + "2dWireframe" => (M::Wireframe2D, "Wireframe 2D"), + "Wireframe" => (M::Wireframe3D, "Wireframe 3D"), + "Hidden" => (M::HiddenLine, "Hidden Line"), + "Flat" => (M::FlatShaded, "Flat Shaded"), + "FlatWithEdges" => (M::FlatShadedWithEdges, "Flat Shaded + Edges"), + "GouraudWithEdges" | "Shaded with edges" => { + (M::GouraudShadedWithEdges, "Gouraud Shaded + Edges") + } + "Gouraud" | "Realistic" | "Shaded" | "Conceptual" | "Shades of Gray" | "Sketchy" + | "X-Ray" => (M::GouraudShaded, "Gouraud Shaded"), + _ => return, + }; + self.apply_render_mode(i, mode, label); + } +} diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 2f81f6abe..2fdb1be62 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -1090,6 +1090,24 @@ impl OpenCADStudio { .push_output(crate::tf!("Viewport visual style: {label}").as_ref()); return Task::none(); } + self.apply_render_mode(i, mode, label); + self.command_line + .push_output(crate::tf!("Visual style: {label}").as_ref()); + // The point cloud move gizmo follows the visual style. + if i == self.active_tab { + self.refresh_selected_grips(); + } + Task::none() + } + + /// The model tab's visual style, without a message. + pub(crate) fn apply_render_mode( + &mut self, + i: usize, + mode: codec::entities::ViewportRenderMode, + label: &str, + ) { + use codec::entities::ViewportRenderMode as M; self.tabs[i].render_mode = mode; // Write the style onto the active Model tile alone so it // sticks when that tile loses focus and the other tiles keep @@ -1105,13 +1123,6 @@ impl OpenCADStudio { // pipeline keys its upload cache off `geometry_epoch`. self.tabs[i].scene.bump_geometry_no_blocks(); self.tabs[i].dirty = true; - self.command_line - .push_output(crate::tf!("Visual style: {label}").as_ref()); - // The point cloud move gizmo follows the visual style. - if i == self.active_tab { - self.refresh_selected_grips(); - } - Task::none() } pub(super) fn on_cursor_moved( diff --git a/src/scene/camera_ops.rs b/src/scene/camera_ops.rs index a26d85274..0338d4970 100644 --- a/src/scene/camera_ops.rs +++ b/src/scene/camera_ops.rs @@ -219,6 +219,13 @@ impl Scene { if self.current_layout == "Model" { let background = copy_background(&mut self.document, view.background_handle); self.set_active_background(background); + if view.visual_style_handle.is_valid() { + if let Some(handle) = self.active_tile_vport_handle() { + if let Some(value) = self.document.vports.iter_mut().find(|v| v.handle == handle) { + value.visual_style_handle = view.visual_style_handle; + } + } + } } let cam = &mut *self.camera.borrow_mut(); // The stored direction points from the target toward the eye. diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index d1bdc2ae9..34c45ff39 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -2571,11 +2571,15 @@ impl Scene { } else if self.current_layout == "Model" { let index = viewport.tile_idx.unwrap_or(0); if let Some(value) = self.model_tile_vport(index) { + // A 2D wireframe view shows no background, as in the reference. + let flat = self.model_tiles.borrow().get(index).is_some_and(|tile| { + tile.render_mode == codec::entities::ViewportRenderMode::Wireframe2D + }); return build( value.visual_style_handle, value.brightness, value.contrast, - value.background_handle, + if flat { Handle::NULL } else { value.background_handle }, ); } }