mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-22 00:12:34 +00:00
Add X-Ray view mode to 3D viewport (#15770)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
#version 110
|
||||
|
||||
// X-Ray view: each surface is a thin translucent layer of its own color, covering more the more
|
||||
// edge-on it is seen. The caller draws it with depth writes off, so every surface a view ray crosses
|
||||
// composites and overlapping geometry reads as denser. Source-alpha rather than additive blending:
|
||||
// additive would saturate to white on the light theme's near-white background.
|
||||
#define EDGE_FALLOFF 2.0
|
||||
// Coverage head-on and edge-on.
|
||||
#define FACE_DENSITY 0.12
|
||||
#define EDGE_DENSITY 0.65
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
|
||||
varying vec3 eye_normal;
|
||||
varying vec3 eye_position;
|
||||
varying vec3 clipping_planes_dots;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, vec3(0.0))))
|
||||
discard;
|
||||
|
||||
// abs(): back faces are drawn too, and there only the angle matters.
|
||||
float facing = abs(dot(normalize(eye_normal), normalize(-eye_position)));
|
||||
float density = mix(EDGE_DENSITY, FACE_DENSITY, pow(facing, EDGE_FALLOFF));
|
||||
|
||||
gl_FragColor = vec4(uniform_color.rgb, density * uniform_color.a);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#version 110
|
||||
|
||||
uniform mat4 view_model_matrix;
|
||||
uniform mat4 projection_matrix;
|
||||
uniform mat3 view_normal_matrix;
|
||||
uniform mat4 volume_world_matrix;
|
||||
|
||||
// Clipping, as in gouraud.vs: the preview's top / bottom plane and the SLA gizmo's free one.
|
||||
uniform vec2 z_range;
|
||||
uniform vec4 clipping_plane;
|
||||
|
||||
attribute vec3 v_position;
|
||||
attribute vec3 v_normal;
|
||||
|
||||
varying vec3 eye_normal;
|
||||
varying vec3 eye_position;
|
||||
varying vec3 clipping_planes_dots;
|
||||
|
||||
void main()
|
||||
{
|
||||
eye_normal = view_normal_matrix * v_normal;
|
||||
|
||||
vec4 position = view_model_matrix * vec4(v_position, 1.0);
|
||||
eye_position = position.xyz;
|
||||
|
||||
// A component below zero means the fragment is clipped away.
|
||||
vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
|
||||
gl_Position = projection_matrix * position;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#version 140
|
||||
|
||||
// X-Ray view: each surface is a thin translucent layer of its own color, covering more the more
|
||||
// edge-on it is seen. The caller draws it with depth writes off, so every surface a view ray crosses
|
||||
// composites and overlapping geometry reads as denser. Source-alpha rather than additive blending:
|
||||
// additive would saturate to white on the light theme's near-white background.
|
||||
#define EDGE_FALLOFF 2.0
|
||||
// Coverage head-on and edge-on.
|
||||
#define FACE_DENSITY 0.12
|
||||
#define EDGE_DENSITY 0.65
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
|
||||
in vec3 eye_normal;
|
||||
in vec3 eye_position;
|
||||
in vec3 clipping_planes_dots;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, vec3(0.0))))
|
||||
discard;
|
||||
|
||||
// abs(): back faces are drawn too, and there only the angle matters.
|
||||
float facing = abs(dot(normalize(eye_normal), normalize(-eye_position)));
|
||||
float density = mix(EDGE_DENSITY, FACE_DENSITY, pow(facing, EDGE_FALLOFF));
|
||||
|
||||
out_color = vec4(uniform_color.rgb, density * uniform_color.a);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#version 140
|
||||
|
||||
uniform mat4 view_model_matrix;
|
||||
uniform mat4 projection_matrix;
|
||||
uniform mat3 view_normal_matrix;
|
||||
uniform mat4 volume_world_matrix;
|
||||
|
||||
// Clipping, as in gouraud.vs: the preview's top / bottom plane and the SLA gizmo's free one.
|
||||
uniform vec2 z_range;
|
||||
uniform vec4 clipping_plane;
|
||||
|
||||
in vec3 v_position;
|
||||
in vec3 v_normal;
|
||||
|
||||
out vec3 eye_normal;
|
||||
out vec3 eye_position;
|
||||
out vec3 clipping_planes_dots;
|
||||
|
||||
void main()
|
||||
{
|
||||
eye_normal = view_normal_matrix * v_normal;
|
||||
|
||||
vec4 position = view_model_matrix * vec4(v_position, 1.0);
|
||||
eye_position = position.xyz;
|
||||
|
||||
// A component below zero means the fragment is clipped away.
|
||||
vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
|
||||
gl_Position = projection_matrix * position;
|
||||
}
|
||||
@@ -1157,6 +1157,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
|
||||
const float support_normal_z = get_selection_support_normal_z();
|
||||
|
||||
// The outline passes below are driven by is_outline, which only the object shaders have; with an
|
||||
// overlay one (wireframe, x-ray) bound they would just draw the volume again.
|
||||
const bool shader_can_outline = shader->get_uniform_location("is_outline") >= 0;
|
||||
|
||||
// Prime depth_tex on every frame so non-outline draws do not keep the
|
||||
// default sampler unit 0, which can conflict with other sampler types.
|
||||
shader->set_uniform("depth_tex", OUTLINE_DEPTH_TEX_UNIT);
|
||||
@@ -1257,7 +1261,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
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);
|
||||
//BBS: add outline related logic
|
||||
if (volume.first->selected && GUI::wxGetApp().show_outline())
|
||||
if (volume.first->selected && shader_can_outline && GUI::wxGetApp().show_outline())
|
||||
volume.first->render_with_outline(cnv_size);
|
||||
else
|
||||
volume.first->render();
|
||||
|
||||
@@ -8758,6 +8758,15 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
else
|
||||
m_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances());
|
||||
|
||||
// Orca: X-Ray replaces both shaded passes with a single one, driven from the opaque call, and
|
||||
// reuses the volume state (print volume, z range, clipping plane) set up above.
|
||||
if (_is_xray_view_active()) {
|
||||
if (type == GLVolumeCollection::ERenderType::Opaque)
|
||||
_render_xray_volumes();
|
||||
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
|
||||
return;
|
||||
}
|
||||
|
||||
const bool realistic_mode = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE);
|
||||
const bool realistic_phong = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_PHONG);
|
||||
const std::string shader_name = (realistic_mode && realistic_phong) ? "phong" : "gouraud";
|
||||
@@ -8894,6 +8903,51 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_xray_view_active() const
|
||||
{
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview || !wxGetApp().plater()->is_show_xray())
|
||||
return false;
|
||||
|
||||
// A painting gizmo and the layer height editor draw the object through their own shaders, and
|
||||
// would lose what they draw if X-Ray took the pass over.
|
||||
if (dynamic_cast<const GLGizmoPainterBase*>(m_gizmos.get_current()) != nullptr)
|
||||
return false;
|
||||
return !(m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1 &&
|
||||
m_layers_editing.object_max_z() > 0.0f);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_xray_volumes()
|
||||
{
|
||||
if (m_volumes.empty())
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("xray");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
const ECanvasType canvas_type = m_canvas_type;
|
||||
|
||||
// Depth writes off so every surface along a view ray composites, not just the nearest one, and
|
||||
// back faces kept for the far wall of a hollow part. The low, near-uniform coverage the shader
|
||||
// emits saturates towards the volume color whatever the order, so the volumes are not sorted.
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
shader->start_using();
|
||||
m_volumes.render(GLVolumeCollection::ERenderType::All, true, camera.get_view_matrix(), camera.get_projection_matrix(),
|
||||
get_canvas_size(), [this, canvas_type](const GLVolume& volume) {
|
||||
if (canvas_type == ECanvasType::CanvasAssembleView)
|
||||
return !volume.is_modifier && !volume.is_wipe_tower;
|
||||
return m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0;
|
||||
});
|
||||
shader->stop_using();
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_wireframe_overlay()
|
||||
{
|
||||
if (!wxGetApp().plater()->is_show_wireframe())
|
||||
@@ -10020,6 +10074,12 @@ void GLCanvas3D::_render_canvas_toolbar()
|
||||
[this, p]{p->toggle_show_wireframe(); m_dirty = true;}
|
||||
);
|
||||
|
||||
create_menu_item( _utf8(L("X-Ray")),
|
||||
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
|
||||
p->is_show_xray(),
|
||||
[this, p]{p->toggle_show_xray(); m_dirty = true;}
|
||||
);
|
||||
|
||||
create_menu_item( _utf8(L("Realistic View")),
|
||||
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
|
||||
cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE),
|
||||
|
||||
@@ -1333,6 +1333,8 @@ private:
|
||||
//BBS: add outline drawing logic
|
||||
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
|
||||
void _render_wireframe_overlay();
|
||||
bool _is_xray_view_active() const;
|
||||
void _render_xray_volumes();
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
void _render_gcode(int canvas_width, int canvas_height);
|
||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||
|
||||
@@ -90,6 +90,8 @@ std::pair<bool, std::string> GLShadersManager::init()
|
||||
, { "ENABLE_ENVIRONMENT_MAP"sv }
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
);
|
||||
// used to render objects as translucent, edge weighted surfaces in the X-Ray view
|
||||
valid &= append_shader("xray", { prefix + "xray.vs", prefix + "xray.fs" });
|
||||
// used to render variable layers heights in 3d editor
|
||||
valid &= append_shader("variable_layer_height", { prefix + "variable_layer_height.vs", prefix + "variable_layer_height.fs" });
|
||||
// used to render highlight contour around selected triangles inside the multi-material gizmo
|
||||
|
||||
@@ -6827,6 +6827,7 @@ struct Plater::priv
|
||||
bool show_render_statistic_dialog{ false };
|
||||
bool show_wireframe{ false };
|
||||
bool wireframe_enabled{ true };
|
||||
bool show_xray{ false };
|
||||
|
||||
static const std::regex pattern_bundle;
|
||||
static const std::regex pattern_3mf;
|
||||
@@ -22399,6 +22400,16 @@ bool Plater::is_wireframe_enabled() const
|
||||
return p->wireframe_enabled;
|
||||
}
|
||||
|
||||
void Plater::toggle_show_xray()
|
||||
{
|
||||
p->show_xray = !p->show_xray;
|
||||
}
|
||||
|
||||
bool Plater::is_show_xray() const
|
||||
{
|
||||
return p->show_xray;
|
||||
}
|
||||
|
||||
|
||||
/*Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name)
|
||||
: TakeSnapshot(plater, from_u8(snapshot_name)) {}
|
||||
|
||||
@@ -956,6 +956,9 @@ public:
|
||||
void enable_wireframe(bool status);
|
||||
bool is_wireframe_enabled() const;
|
||||
|
||||
void toggle_show_xray();
|
||||
bool is_show_xray() const;
|
||||
|
||||
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
|
||||
bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition);
|
||||
bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); }
|
||||
|
||||
Reference in New Issue
Block a user