Compare commits

...
Author SHA1 Message Date
SoftFever b67269ccc4 Fix z-fighting between feature previews and the bodies in the Design tab
The bodies now always draw over the preview on every face a feature leaves unchanged, for
all features. Since a hole's cut sits inside the body, the Hole preview now hides the
bodies and shows the result alone, as Fillet/Chamfer and Draft do.
2026-10-05 19:38:46 +08:00
5 changed files with 32 additions and 6 deletions
+9
View File
@@ -189,6 +189,15 @@ degenerate edges are left out (`GeometryEngine::display_edges`), and the polylin
once per shape, keyed by its `TShape`, because a recompute that leaves a body unchanged is the
common case.
While a feature card is open, its preview ghost is the whole model the candidate would produce,
drawn translucent over the bodies, so every face the feature leaves alone is in both at the same
depth. The ghost is drawn with a depth bias that pushes it back (`GLVolume::depth_bias`), so on a shared face
the body always wins instead of the two copies z-fighting, and the ghost shows only where the
result reaches past the bodies. Material a feature removes lies inside the old solid and would not
show at all, so the tools whose result mostly coincides with the body — Fillet/Chamfer, Draft,
Hole and the Mate hover — hide the bodies once the preview is valid and draw the result alone,
opaque.
## Showing what is selected
A selection is drawn on the faces it names, never as a tint over the body: a translucent
+7
View File
@@ -310,6 +310,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, force_native_color(false)
, force_neutral_color(false)
, force_sinking_contours(false)
, depth_bias(false)
, picking(false)
, tverts_range(0, size_t(-1))
{
@@ -1332,11 +1333,17 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
shader->set_uniform("projection_matrix", projection_matrix);
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
if (volume.first->depth_bias) {
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
glsafe(::glPolygonOffset(1.0f, 1.0f));
}
//BBS: add outline related logic
if (volume.first->selected && shader_can_outline && GUI::wxGetApp().show_outline())
volume.first->render_with_outline(cnv_size);
else
volume.first->render();
if (volume.first->depth_bias)
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
#if ENABLE_ENVIRONMENT_MAP
if (use_environment_texture)
+4
View File
@@ -232,6 +232,10 @@ public:
bool force_neutral_color : 1;
// Whether or not to force rendering of sinking contours
bool force_sinking_contours : 1;
// Orca: draw this volume with a positive depth bias (glPolygonOffset, pushed away from the
// camera), so on a surface it shares with another volume the other volume wins the depth
// test instead of z-fighting it
bool depth_bias : 1;
// Is render for picking
bool picking : 1;
// slice error
+7 -3
View File
@@ -378,10 +378,14 @@ void DesignCanvas::reload(bool keep_view)
v->set_color(c);
}
} else if (obj_idx == 1) {
// The ghost is normally a faint blue overlay on the visible body. In preview-only
// mode it IS the result (base bodies hidden), so render it opaque so it reads as a
// finished solid rather than a see-through hint.
// The ghost is the whole resulting model, normally drawn as a faint blue overlay on
// the visible bodies. Every face the feature leaves alone is in both, at the same
// depth, so the ghost is drawn with a depth bias: the bodies win on those faces instead
// of the two copies z-fighting, and the ghost shows only where the result reaches past
// the bodies. In preview-only mode it IS the result (base bodies hidden), so render it
// opaque so it reads as a finished solid rather than a see-through hint.
v->set_color(m_body_hidden ? ColorRGBA(0.40f, 0.82f, 1.0f, 1.0f) : ghost);
v->depth_bias = true;
}
}
+5 -3
View File
@@ -11185,10 +11185,12 @@ void DesignPanel::refresh_preview()
// Confirm so the user sees the gate before clicking; the red status says why.
m_candidate_ok = ok;
update_confirm_button();
// Fillet/Chamfer/Draft: once the target edge/face yields a valid result, show ONLY the
// Fillet/Chamfer/Draft/Hole: once the target edge/face yields a valid result, show ONLY the
// preview (hide the base bodies) so the user sees the finished shape, not the old solid
// doubled with the ghost. Before a valid pick the body stays visible so it can be picked.
m_viewport->set_body_hidden((m_active == Tool::Dressup || m_active == Tool::Draft) && ok);
// doubled with the ghost. A hole's cut lies inside the old solid, so without this the body
// would hide it entirely. Before a valid pick the body stays visible so it can be picked.
m_viewport->set_body_hidden((m_active == Tool::Dressup || m_active == Tool::Draft ||
m_active == Tool::Hole) && ok);
m_status->Refresh();
// Refresh the in-canvas Extrude depth arrow (self-gates: only while the Extrude card is open).