forked from 1b-agent/opencadstudio-upstream
feat(render): phase 0 — render-mode pick_list + 3-flag pipeline plumbing
Replaces the binary Wireframe/Solid toggle with a 7-mode dropdown that maps onto acadrust's `ViewportRenderMode` enum: Wireframe 2D / Wireframe 3D / Hidden Line / Flat Shaded / Gouraud Shaded / Flat Shaded + Edges / Gouraud Shaded + Edges. Per-tab state lives on `DocumentTab::render_mode`; the old `SetWireframe` message forwards to `SetRenderMode` so the ribbon + WIREFRAME / SOLID command line keep working. `scene::render::render_mode_flags()` translates the mode into three pipeline-facing booleans (`face3d_fill`, `mesh_fill`, `show_3d_edges`); `mesh_fill` / `show_3d_edges` are stubs that the upcoming phases will consume. `Primitive` carries the derived `view_wireframe` plus the two new fields through to `prepare()`. `Pipeline::upload_face3d` takes a `wireframe_only` flag that drops 3DFACE quad fills (built from `key_vertices`) while keeping the `all_wires.fill_tris` channel — that channel carries text-LOD greek rectangles and PolyfaceMesh face triangles which must stay visible in wireframe mode. 3DFACE edges stay on regardless of mode. Solid3D mesh wireframe rendering, Hidden Line z-prepass, and Flat / Gouraud shader uniforms come in phases 1–4. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
5ea996d5fe
commit
2145ab8a66
8 files changed
+273
-90
No files matched your search
+3
-1
@@ -27,6 +27,7 @@ pub(super) struct DocumentTab {
|
||||
pub(super) selected_grips: Vec<GripDef>,
|
||||
pub(super) selected_handle: Option<Handle>,
|
||||
pub(super) wireframe: bool,
|
||||
pub(super) render_mode: acadrust::entities::ViewportRenderMode,
|
||||
pub(super) visual_style: String,
|
||||
pub(super) last_cursor_world: glam::Vec3,
|
||||
pub(super) last_cursor_screen: iced::Point,
|
||||
@@ -80,7 +81,8 @@ impl DocumentTab {
|
||||
selected_grips: vec![],
|
||||
selected_handle: None,
|
||||
wireframe: false,
|
||||
visual_style: "Shaded".into(),
|
||||
render_mode: acadrust::entities::ViewportRenderMode::Wireframe2D,
|
||||
visual_style: "Wireframe 2D".into(),
|
||||
last_cursor_world: glam::Vec3::ZERO,
|
||||
last_cursor_screen: iced::Point::ORIGIN,
|
||||
history: HistoryState::default(),
|
||||
|
||||
@@ -308,6 +308,10 @@ pub enum Message {
|
||||
SaveDialogCancel,
|
||||
ClearScene,
|
||||
SetWireframe(bool),
|
||||
/// Set the active tab's render mode (one of acadrust's seven visual
|
||||
/// styles). Replaces the binary `SetWireframe` over time; the older
|
||||
/// message stays for ribbon/CLI back-compat and forwards.
|
||||
SetRenderMode(acadrust::entities::ViewportRenderMode),
|
||||
/// Switch camera projection: true = Orthographic, false = Perspective.
|
||||
SetProjection(bool),
|
||||
/// Select a ribbon module tab by index.
|
||||
|
||||
+33
-11
@@ -654,19 +654,41 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::SetWireframe(w) => {
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].wireframe = w;
|
||||
self.ribbon.set_wireframe(w);
|
||||
self.tabs[i].visual_style = if w {
|
||||
"Wireframe".into()
|
||||
// Back-compat shim: forward to the new render-mode path so
|
||||
// the ribbon button + WIREFRAME / SOLID command line still
|
||||
// work without duplicating the rendering plumbing.
|
||||
let mode = if w {
|
||||
acadrust::entities::ViewportRenderMode::Wireframe2D
|
||||
} else {
|
||||
"Shaded".into()
|
||||
acadrust::entities::ViewportRenderMode::FlatShaded
|
||||
};
|
||||
self.command_line.push_output(if w {
|
||||
"Visual style: Wireframe"
|
||||
} else {
|
||||
"Visual style: Shaded"
|
||||
});
|
||||
Task::done(Message::SetRenderMode(mode))
|
||||
}
|
||||
|
||||
Message::SetRenderMode(mode) => {
|
||||
use acadrust::entities::ViewportRenderMode as M;
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].render_mode = mode;
|
||||
// Keep the legacy `wireframe` bool synced — both wireframe
|
||||
// modes set it, everything else clears it.
|
||||
let wf = matches!(mode, M::Wireframe2D | M::Wireframe3D);
|
||||
self.tabs[i].wireframe = wf;
|
||||
self.ribbon.set_wireframe(wf);
|
||||
let label = match mode {
|
||||
M::Wireframe2D => "Wireframe 2D",
|
||||
M::Wireframe3D => "Wireframe 3D",
|
||||
M::HiddenLine => "Hidden Line",
|
||||
M::FlatShaded => "Flat Shaded",
|
||||
M::GouraudShaded => "Gouraud Shaded",
|
||||
M::FlatShadedWithEdges => "Flat Shaded + Edges",
|
||||
M::GouraudShadedWithEdges => "Gouraud Shaded + Edges",
|
||||
};
|
||||
self.tabs[i].visual_style = label.into();
|
||||
// Re-upload face3d fills on the next frame — the render
|
||||
// pipeline keys its upload cache off `geometry_epoch`.
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.command_line
|
||||
.push_output(&format!("Visual style: {label}"));
|
||||
Task::none()
|
||||
}
|
||||
|
||||
|
||||
+91
-13
@@ -16,6 +16,27 @@ use iced::{keyboard, Background, Border, Color, Element, Fill, Subscription, Tas
|
||||
|
||||
const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX;
|
||||
|
||||
/// `pick_list` requires its items to implement `Display`; acadrust's
|
||||
/// `ViewportRenderMode` enum carries the raw DXF integers, not a label,
|
||||
/// so wrap it locally with a friendly name renderer.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(super) struct RenderModeChoice(pub acadrust::entities::ViewportRenderMode);
|
||||
|
||||
impl std::fmt::Display for RenderModeChoice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use acadrust::entities::ViewportRenderMode as M;
|
||||
f.write_str(match self.0 {
|
||||
M::Wireframe2D => "Wireframe 2D",
|
||||
M::Wireframe3D => "Wireframe 3D",
|
||||
M::HiddenLine => "Hidden Line",
|
||||
M::FlatShaded => "Flat Shaded",
|
||||
M::GouraudShaded => "Gouraud Shaded",
|
||||
M::FlatShadedWithEdges => "Flat Shaded + Edges",
|
||||
M::GouraudShadedWithEdges => "Gouraud Shaded + Edges",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub fn view(&self, window_id: window::Id) -> Element<'_, Message> {
|
||||
// ── Floating panel windows ─────────────────────────────────────────
|
||||
@@ -236,7 +257,11 @@ impl OpenCADStudio {
|
||||
} else if is_paper {
|
||||
paper_canvas_view(tab)
|
||||
} else {
|
||||
shader(ViewportPane::model(&tab.scene, self.show_viewcube))
|
||||
shader(ViewportPane::model(
|
||||
&tab.scene,
|
||||
self.show_viewcube,
|
||||
tab.render_mode,
|
||||
))
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
@@ -357,15 +382,53 @@ impl OpenCADStudio {
|
||||
)
|
||||
};
|
||||
|
||||
let info = container(overlay::info_bar(
|
||||
if is_paper {
|
||||
&tab.scene.current_layout
|
||||
} else {
|
||||
"Custom View"
|
||||
// Render-mode picker, top-left of the viewport. Drives whether
|
||||
// 3D face / mesh fills and edges are rendered for the active
|
||||
// tab. Hatch fills are deliberately *not* gated by this — the
|
||||
// document's FILLMODE still owns 2D fill state.
|
||||
let render_modes: Vec<RenderModeChoice> = vec![
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::Wireframe2D),
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::Wireframe3D),
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::HiddenLine),
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::FlatShaded),
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::GouraudShaded),
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::FlatShadedWithEdges),
|
||||
RenderModeChoice(acadrust::entities::ViewportRenderMode::GouraudShadedWithEdges),
|
||||
];
|
||||
let info = container(
|
||||
iced::widget::pick_list(
|
||||
render_modes,
|
||||
Some(RenderModeChoice(tab.render_mode)),
|
||||
|c| Message::SetRenderMode(c.0),
|
||||
)
|
||||
.text_size(11),
|
||||
)
|
||||
.style(|_: &Theme| iced::widget::container::Style {
|
||||
background: Some(iced::Background::Color(Color {
|
||||
r: 0.10,
|
||||
g: 0.10,
|
||||
b: 0.10,
|
||||
a: 0.75,
|
||||
})),
|
||||
border: iced::Border {
|
||||
color: Color {
|
||||
r: 0.35,
|
||||
g: 0.35,
|
||||
b: 0.35,
|
||||
a: 1.0,
|
||||
},
|
||||
width: 1.0,
|
||||
radius: 4.0.into(),
|
||||
},
|
||||
&tab.visual_style,
|
||||
))
|
||||
.padding([4, 6]);
|
||||
text_color: Some(Color {
|
||||
r: 0.85,
|
||||
g: 0.85,
|
||||
b: 0.85,
|
||||
a: 1.0,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.padding([4, 8]);
|
||||
|
||||
let viewport_mouse = mouse_area(container(
|
||||
iced::widget::Space::new().width(Fill).height(Fill),
|
||||
@@ -443,20 +506,35 @@ impl OpenCADStudio {
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill),
|
||||
container(info).width(Fill).height(Fill),
|
||||
selection_overlay,
|
||||
viewport_mouse,
|
||||
// Toggle sits ABOVE the viewport mouse_area so clicks
|
||||
// inside the toggle's bounds reach it instead of the
|
||||
// shader behind it; `opaque` blocks the same clicks from
|
||||
// bubbling further down. Outside the toggle's footprint
|
||||
// the outer Fill container is transparent — viewport
|
||||
// drawing / selection stays unaffected.
|
||||
container(iced::widget::opaque(info))
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.align_x(iced::alignment::Horizontal::Left)
|
||||
.align_y(iced::alignment::Vertical::Top),
|
||||
]
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
};
|
||||
|
||||
if self.show_navbar && !tab.is_start {
|
||||
// Anchor the nav toolbar to the vertical centre of the
|
||||
// viewport area, flush against the right edge with a small
|
||||
// gutter, so it stays aligned no matter how tall the
|
||||
// window is.
|
||||
let nav = container(overlay::nav_toolbar())
|
||||
.align_right(Fill)
|
||||
.align_top(Fill)
|
||||
.align_y(iced::alignment::Vertical::Center)
|
||||
.height(Fill)
|
||||
.padding(iced::Padding {
|
||||
top: 148.0,
|
||||
top: 0.0,
|
||||
right: 8.0,
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
@@ -805,7 +883,7 @@ fn paper_canvas_view<'a>(tab: &'a super::document::DocumentTab) -> Element<'a, M
|
||||
let w = rect.width.clamp(1.0, canvas_w - x);
|
||||
let h = rect.height.clamp(1.0, canvas_h - y);
|
||||
|
||||
let vp_widget = shader(PaperViewportPane::new(scene, vp_handle))
|
||||
let vp_widget = shader(PaperViewportPane::new(scene, vp_handle, tab.render_mode))
|
||||
.width(iced::Length::Fixed(w))
|
||||
.height(iced::Length::Fixed(h));
|
||||
|
||||
|
||||
Reference in new issue
Block a user