mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
Merge upstream/main into cad-mainline (530 commits)
Catches the fork up from449a4cf9fc(2026-06-28) tod6cb667b89(2026-07-24). Upstream touched 2326 files; 17 of them overlap the 164 this branch touches. 16 of the 17 auto-merged, including all three CMakeLists.txt, the build_all.yml CI workflow, and every GUI file. The CAD core never conflicts: CadDocument, SketchEngine, SketchSolver, McpControl and test_caddocument are files this fork adds, so upstream does not touch them. The one conflict, tests/libslic3r/test_3mf.cpp, was purely additive in all three hunks and is resolved as a union: our test pinning that store_bbs_3mf embeds the CAD recipe as Metadata/SnapOrca_cad.bin, upstream's multi-nozzle plate-metadata round-trip tests, and both sets of includes. All three were verified present after resolution rather than assumed. NOT BUILD-VERIFIED, for a reason that predates this merge and is not caused by it: this fork cannot be configured on nativedev at all. Its CMakeLists has required Eigen3 5.0.1 since before the merge (line 592 pre-merge), while the only deps image on the machine is snaporca-deps, built for snaporca's find_package(Eigen3 3.3). CMake fails at configure, so nothing compiles. That means this fork's Catch2 suite has never run. Every "suite green" figure recorded for M1-M8 was snaporca's suite; the ports were verified by patch-apply plus the CAD sources being byte-identical to snaporca's. Building an orca_cad deps image with Eigen 5.0.1 is what would finally close that gap. Pre-merge state is preserved at branch cad-mainline-pre-upstream-2026-07-25 (30d54f0074). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+378
-186
@@ -285,7 +285,7 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(GLCanvas3D&
|
||||
ImGui::SetCursorPosX(input_align);
|
||||
ImGui::BBLDragFloat("##adaptive_input", &m_adaptive_quality, 0.05f, 0.0f, 0.0f, "%.2f");
|
||||
|
||||
if (imgui.button(_L("Smooth")))
|
||||
if (imgui.button(_L_CONTEXT("Smooth", "Mesh action")))
|
||||
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params));
|
||||
ImGui::SameLine();
|
||||
text_align = std::max(text_align, ImGui::GetCursorPosX());
|
||||
@@ -1037,6 +1037,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_ORIENT_PARTPLATE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_CURR_PLATE_ALL, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_OPEN_SPEED_DIAL, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event<int>);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent);
|
||||
@@ -1234,6 +1235,14 @@ GLCanvas3D::~GLCanvas3D()
|
||||
glsafe(::glDeleteTextures(1, &m_ssao_depth_texture_id));
|
||||
m_ssao_depth_texture_id = 0;
|
||||
}
|
||||
if (m_shadow_map_texture_id != 0) {
|
||||
glsafe(::glDeleteTextures(1, &m_shadow_map_texture_id));
|
||||
m_shadow_map_texture_id = 0;
|
||||
}
|
||||
if (m_shadow_map_fbo != 0) {
|
||||
glsafe(::glDeleteFramebuffers(1, &m_shadow_map_fbo));
|
||||
m_shadow_map_fbo = 0;
|
||||
}
|
||||
m_plate_shadow_mask.reset();
|
||||
}
|
||||
m_plate_shadow_mask_key.clear();
|
||||
@@ -2046,6 +2055,9 @@ void GLCanvas3D::render(bool only_init)
|
||||
|
||||
// draw scene
|
||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
// Invalidate the shadow map each frame; only the View3D path below rebuilds it. This keeps
|
||||
// the Preview / Assemble canvases from sampling a stale map with an outdated light matrix.
|
||||
m_shadow_map_valid = false;
|
||||
_render_background();
|
||||
|
||||
//BBS add partplater rendering logic
|
||||
@@ -2070,11 +2082,13 @@ void GLCanvas3D::render(bool only_init)
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
||||
|
||||
//BBS: add outline logic
|
||||
_render_cast_shadows_on_plate(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
// Depth pass for object-on-object and self shadows; consumed by the gouraud shader below.
|
||||
_render_shadows(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
}
|
||||
/* preview render */
|
||||
else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) {
|
||||
@@ -2100,6 +2114,7 @@ void GLCanvas3D::render(bool only_init)
|
||||
//_render_selection();
|
||||
// BBS: add outline logic
|
||||
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
|
||||
_render_wireframe_overlay();
|
||||
}
|
||||
|
||||
_render_sequential_clearance();
|
||||
@@ -2344,11 +2359,6 @@ void GLCanvas3D::remove_curr_plate_all()
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_plate_thumbnails()
|
||||
{
|
||||
_update_imgui_select_plate_toolbar();
|
||||
}
|
||||
|
||||
void GLCanvas3D::select_all()
|
||||
{
|
||||
if (!m_gizmos.is_allow_select_all()) {
|
||||
@@ -3019,6 +3029,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
_set_warning_notification(EWarning::MixtureFilamentIncompatible, !filament_mixture_compatible);
|
||||
|
||||
bool model_fits = contained_min_one && !m_model->objects.empty() && !partlyOut && object_results.filaments.empty() && tpu_valid && filament_printable;
|
||||
// Honor the missing-plugin block so this geometry-only path does not re-enable slicing
|
||||
// that Plater::validate_current_plate disabled (otherwise moving an object would briefly
|
||||
// re-enable the Slice button while required plugins are still missing).
|
||||
if (wxGetApp().plater() && wxGetApp().plater()->plugins_block_slicing())
|
||||
model_fits = false;
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, model_fits));
|
||||
ppl.get_curr_plate()->update_slice_ready_status(model_fits);
|
||||
}
|
||||
@@ -3229,7 +3244,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
// BBS
|
||||
//m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
|
||||
m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
|
||||
_update_imgui_select_plate_toolbar();
|
||||
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
m_dirty |= mouse3d_controller_applied;
|
||||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
@@ -3578,6 +3592,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
break;
|
||||
}
|
||||
case '?': { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; }
|
||||
case ' ': {
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D)
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_OPEN_SPEED_DIAL));
|
||||
break;
|
||||
}
|
||||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
@@ -4931,11 +4950,6 @@ void GLCanvas3D::force_set_focus() {
|
||||
void GLCanvas3D::on_set_focus(wxFocusEvent& evt)
|
||||
{
|
||||
m_tooltip_enabled = false;
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview) {
|
||||
// update thumbnails and update plate toolbar
|
||||
wxGetApp().plater()->update_all_plate_thumbnails();
|
||||
_update_imgui_select_plate_toolbar();
|
||||
}
|
||||
_refresh_if_shown_on_screen();
|
||||
m_tooltip_enabled = true;
|
||||
m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style");
|
||||
@@ -6146,11 +6160,11 @@ void GLCanvas3D::_render_3d_navigator()
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], "Z"); // ORCA use uppercase to match text on tranform widgets
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], "X"); // ORCA use uppercase to match text on tranform widgets
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_FRONT], _utf8("Front").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BACK], _utf8("Back").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BACK], _u8L_CONTEXT("Back", "Camera View").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_TOP], _utf8("Top").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BOTTOM], _utf8("Bottom").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_LEFT], _CTX_utf8(L_CONTEXT("Left", "Camera"), "Camera").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_RIGHT], _CTX_utf8(L_CONTEXT("Right", "Camera"), "Camera").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_LEFT], _u8L_CONTEXT("Left", "Camera View").c_str());
|
||||
strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_RIGHT], _u8L_CONTEXT("Right", "Camera View").c_str());
|
||||
|
||||
float sc = get_scale();
|
||||
#ifdef WIN32
|
||||
@@ -6602,21 +6616,21 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data,
|
||||
// glsafe(::glEnable(GL_MULTISAMPLE));
|
||||
|
||||
GLint max_samples;
|
||||
glsafe(::glGetIntegerv(GL_MAX_SAMPLES, &max_samples));
|
||||
glsafe(::glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_samples));
|
||||
GLsizei num_samples = max_samples / 2;
|
||||
|
||||
GLuint render_fbo;
|
||||
glsafe(::glGenFramebuffers(1, &render_fbo));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo));
|
||||
glsafe(::glGenFramebuffersEXT(1, &render_fbo));
|
||||
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo));
|
||||
|
||||
GLuint render_tex = 0;
|
||||
GLuint render_tex_buffer = 0;
|
||||
if (multisample) {
|
||||
// use renderbuffer instead of texture to avoid the need to use glTexImage2DMultisample which is available only since OpenGL 3.2
|
||||
glsafe(::glGenRenderbuffers(1, &render_tex_buffer));
|
||||
glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_tex_buffer));
|
||||
glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_RGBA8, w, h));
|
||||
glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_tex_buffer));
|
||||
glsafe(::glGenRenderbuffersEXT(1, &render_tex_buffer));
|
||||
glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_tex_buffer));
|
||||
glsafe(::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, num_samples, GL_RGBA8, w, h));
|
||||
glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, render_tex_buffer));
|
||||
}
|
||||
else {
|
||||
glsafe(::glGenTextures(1, &render_tex));
|
||||
@@ -6624,31 +6638,31 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data,
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0));
|
||||
glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0));
|
||||
}
|
||||
|
||||
GLuint render_depth;
|
||||
glsafe(::glGenRenderbuffers(1, &render_depth));
|
||||
glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth));
|
||||
glsafe(::glGenRenderbuffersEXT(1, &render_depth));
|
||||
glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth));
|
||||
if (multisample)
|
||||
glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_DEPTH_COMPONENT24, w, h));
|
||||
glsafe(::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, num_samples, GL_DEPTH_COMPONENT24, w, h));
|
||||
else
|
||||
glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h));
|
||||
glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, w, h));
|
||||
|
||||
glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth));
|
||||
glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth));
|
||||
|
||||
GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 };
|
||||
GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0_EXT };
|
||||
glsafe(::glDrawBuffers(1, drawBufs));
|
||||
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
|
||||
if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT) {
|
||||
render_thumbnail_internal(thumbnail_data, thumbnail_params, partplate_list, model_objects, volumes, extruder_colors, shader, camera_type, camera_view_angle_type,
|
||||
for_picking,
|
||||
ban_light);
|
||||
|
||||
if (multisample) {
|
||||
GLuint resolve_fbo;
|
||||
glsafe(::glGenFramebuffers(1, &resolve_fbo));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, resolve_fbo));
|
||||
glsafe(::glGenFramebuffersEXT(1, &resolve_fbo));
|
||||
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, resolve_fbo));
|
||||
|
||||
GLuint resolve_tex;
|
||||
glsafe(::glGenTextures(1, &resolve_tex));
|
||||
@@ -6656,21 +6670,21 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data,
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolve_tex, 0));
|
||||
glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, resolve_tex, 0));
|
||||
|
||||
glsafe(::glDrawBuffers(1, drawBufs));
|
||||
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
|
||||
glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, render_fbo));
|
||||
glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo));
|
||||
glsafe(::glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR));
|
||||
if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT) {
|
||||
glsafe(::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, render_fbo));
|
||||
glsafe(::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, resolve_fbo));
|
||||
glsafe(::glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo));
|
||||
glsafe(::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, resolve_fbo));
|
||||
glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
|
||||
}
|
||||
|
||||
glsafe(::glDeleteTextures(1, &resolve_tex));
|
||||
glsafe(::glDeleteFramebuffers(1, &resolve_fbo));
|
||||
glsafe(::glDeleteFramebuffersEXT(1, &resolve_fbo));
|
||||
}
|
||||
else
|
||||
glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
|
||||
@@ -6680,13 +6694,13 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data,
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
glsafe(::glDeleteRenderbuffers(1, &render_depth));
|
||||
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
|
||||
glsafe(::glDeleteRenderbuffersEXT(1, &render_depth));
|
||||
if (render_tex_buffer != 0)
|
||||
glsafe(::glDeleteRenderbuffers(1, &render_tex_buffer));
|
||||
glsafe(::glDeleteRenderbuffersEXT(1, &render_tex_buffer));
|
||||
if (render_tex != 0)
|
||||
glsafe(::glDeleteTextures(1, &render_tex));
|
||||
glsafe(::glDeleteFramebuffers(1, &render_fbo));
|
||||
glsafe(::glDeleteFramebuffersEXT(1, &render_fbo));
|
||||
|
||||
//if (!multisample)
|
||||
// glsafe(::glDisable(GL_MULTISAMPLE));
|
||||
@@ -7018,13 +7032,28 @@ void GLCanvas3D::_update_select_plate_toolbar_stats_item(bool force_selected) {
|
||||
bool GLCanvas3D::_update_imgui_select_plate_toolbar()
|
||||
{
|
||||
bool result = true;
|
||||
if (!m_sel_plate_toolbar.is_enabled() || m_sel_plate_toolbar.is_render_finish) return false;
|
||||
if (!m_sel_plate_toolbar.is_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& p_plater = wxGetApp().plater();
|
||||
if (!p_plater) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p_plater->is_plate_toolbar_image_dirty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p_plater->is_gcode_3mf()) {
|
||||
p_plater->update_all_plate_thumbnails(true);
|
||||
}
|
||||
|
||||
_update_select_plate_toolbar_stats_item();
|
||||
|
||||
m_sel_plate_toolbar.del_all_item();
|
||||
|
||||
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
|
||||
PartPlateList& plate_list = p_plater->get_partplate_list();
|
||||
for (int i = 0; i < plate_list.get_plate_count(); i++) {
|
||||
IMToolbarItem* item = new IMToolbarItem();
|
||||
PartPlate* plate = plate_list.get_plate(i);
|
||||
@@ -7037,7 +7066,7 @@ bool GLCanvas3D::_update_imgui_select_plate_toolbar()
|
||||
}
|
||||
m_sel_plate_toolbar.m_items.push_back(item);
|
||||
}
|
||||
|
||||
p_plater->clear_plate_toolbar_image_dirty();
|
||||
m_sel_plate_toolbar.is_display_scrollbar = false;
|
||||
return result;
|
||||
}
|
||||
@@ -7465,8 +7494,8 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo));
|
||||
}
|
||||
else {
|
||||
glsafe(::glGenFramebuffers(1, &render_fbo));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo));
|
||||
glsafe(::glGenFramebuffersEXT(1, &render_fbo));
|
||||
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo));
|
||||
}
|
||||
glsafe(::glGenTextures(1, &render_tex));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex));
|
||||
@@ -7481,11 +7510,11 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
||||
glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth));
|
||||
}
|
||||
else {
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0));
|
||||
glsafe(::glGenRenderbuffers(1, &render_depth));
|
||||
glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth));
|
||||
glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height));
|
||||
glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth));
|
||||
glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0));
|
||||
glsafe(::glGenRenderbuffersEXT(1, &render_depth));
|
||||
glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth));
|
||||
glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height));
|
||||
glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth));
|
||||
}
|
||||
const GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 };
|
||||
glsafe(::glDrawBuffers(1, drawBufs));
|
||||
@@ -7494,7 +7523,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
||||
use_framebuffer = false;
|
||||
}
|
||||
else {
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
use_framebuffer = false;
|
||||
}
|
||||
}
|
||||
@@ -7605,11 +7634,11 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
||||
glsafe(::glDeleteFramebuffers(1, &render_fbo));
|
||||
}
|
||||
else if (framebuffers_type == OpenGLManager::EFramebufferType::Ext) {
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
|
||||
if (render_depth != 0)
|
||||
glsafe(::glDeleteRenderbuffers(1, &render_depth));
|
||||
glsafe(::glDeleteRenderbuffersEXT(1, &render_depth));
|
||||
if (render_fbo != 0)
|
||||
glsafe(::glDeleteFramebuffers(1, &render_fbo));
|
||||
glsafe(::glDeleteFramebuffersEXT(1, &render_fbo));
|
||||
}
|
||||
|
||||
if (render_tex != 0)
|
||||
@@ -7982,9 +8011,8 @@ void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transfo
|
||||
plate_list.set_hide_chrome(prev_hide_chrome);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||
void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||
{
|
||||
// Check if shadow rendering is enabled in configuration
|
||||
if (wxGetApp().app_config == nullptr)
|
||||
return;
|
||||
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE))
|
||||
@@ -7998,54 +8026,181 @@ void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, c
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
// Fixed light direction (pointing downward at an angle)
|
||||
// Drive shadow direction from current view angle: define light in eye-space,
|
||||
// then transform it to world-space with inverse view rotation.
|
||||
const Vec3d light_dir_eye = Vec3d(-0.4574957, 0.4574957, 0.7624929).normalized();
|
||||
const Matrix3d view_rot = view_matrix.matrix().block<3, 3>(0, 0);
|
||||
const Vec3d light_dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
|
||||
const Vec3d ray_dir = -light_dir_to_light; // Direction of shadow projection
|
||||
|
||||
if (std::abs(ray_dir.z()) < 1e-6)
|
||||
if (OpenGLManager::get_framebuffers_type() == OpenGLManager::EFramebufferType::Arb) {
|
||||
|
||||
// Light direction (same as used in shading and plate shading)
|
||||
const Vec3d light_dir_eye = Vec3d(-0.4574957, 0.4574957, 0.7624929).normalized();
|
||||
const Matrix3d view_rot = view_matrix.matrix().block<3, 3>(0, 0);
|
||||
const Vec3d dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
|
||||
|
||||
// Bounding box of the printable objects (the shadow casters).
|
||||
BoundingBoxf3 obj_bb;
|
||||
for (const GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
obj_bb.merge(volume->transformed_bounding_box());
|
||||
}
|
||||
if (!obj_bb.defined)
|
||||
return; // no objects to cast shadows
|
||||
|
||||
// Orthographic light-space basis (z points toward the light).
|
||||
const Vec3d up = (std::abs(dir_to_light.z()) > 0.99) ? Vec3d::UnitY() : Vec3d::UnitZ();
|
||||
const Vec3d z_axis = dir_to_light;
|
||||
const Vec3d x_axis = up.cross(z_axis).normalized();
|
||||
const Vec3d y_axis = z_axis.cross(x_axis).normalized();
|
||||
|
||||
// Fit the frustum to the object AABB *and* the object's shadow projected onto the plate
|
||||
// (clamped to the plate footprint). This keeps the map tight/high-res for short shadows
|
||||
// while still covering long shadows at grazing light angles, which previously fell outside
|
||||
// the map and were clipped.
|
||||
const Vec3d ray_dir = -dir_to_light; // direction the shadow travels
|
||||
const BoundingBoxf3 plate_bb = m_bed.build_volume().valid() ? m_bed.build_volume().bounding_volume() : obj_bb;
|
||||
|
||||
Vec3d lmin(DBL_MAX, DBL_MAX, DBL_MAX);
|
||||
Vec3d lmax(-DBL_MAX, -DBL_MAX, -DBL_MAX);
|
||||
auto enclose = [&](const Vec3d& p) {
|
||||
const Vec3d lp(x_axis.dot(p), y_axis.dot(p), z_axis.dot(p));
|
||||
lmin = lmin.cwiseMin(lp);
|
||||
lmax = lmax.cwiseMax(lp);
|
||||
};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const Vec3d corner((i & 1) ? obj_bb.max.x() : obj_bb.min.x(),
|
||||
(i & 2) ? obj_bb.max.y() : obj_bb.min.y(),
|
||||
(i & 4) ? obj_bb.max.z() : obj_bb.min.z());
|
||||
enclose(corner);
|
||||
// Where this corner's shadow lands on z = 0, clamped to the plate so a grazing angle
|
||||
// (t -> infinity) stays bounded.
|
||||
if (ray_dir.z() < -1e-6) {
|
||||
const double t = -corner.z() / ray_dir.z();
|
||||
Vec3d s = corner + t * ray_dir;
|
||||
s.x() = std::min(std::max(s.x(), plate_bb.min.x()), plate_bb.max.x());
|
||||
s.y() = std::min(std::max(s.y(), plate_bb.min.y()), plate_bb.max.y());
|
||||
s.z() = 0.0;
|
||||
enclose(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Light "camera" placed just past the nearest enclosed point, looking toward the scene.
|
||||
const double range = lmax.z() - lmin.z();
|
||||
const double margin = std::max(1.0, 0.05 * range);
|
||||
const double cx = 0.5 * (lmin.x() + lmax.x());
|
||||
const double cy = 0.5 * (lmin.y() + lmax.y());
|
||||
const Vec3d eye = x_axis * cx + y_axis * cy + z_axis * (lmax.z() + margin);
|
||||
|
||||
Matrix4d light_view = Matrix4d::Identity();
|
||||
light_view.block<1, 3>(0, 0) = x_axis.transpose();
|
||||
light_view.block<1, 3>(1, 0) = y_axis.transpose();
|
||||
light_view.block<1, 3>(2, 0) = z_axis.transpose();
|
||||
light_view(0, 3) = -x_axis.dot(eye);
|
||||
light_view(1, 3) = -y_axis.dot(eye);
|
||||
light_view(2, 3) = -z_axis.dot(eye);
|
||||
|
||||
// Ortho fit to the light-space extent (symmetric in X/Y around cx,cy; +2% edge padding).
|
||||
const double halfx = std::max(0.5 * (lmax.x() - lmin.x()), 1.0) * 1.02;
|
||||
const double halfy = std::max(0.5 * (lmax.y() - lmin.y()), 1.0) * 1.02;
|
||||
const double near_z = margin * 0.5;
|
||||
const double far_z = range + margin * 1.5;
|
||||
Matrix4d light_proj = Matrix4d::Identity();
|
||||
light_proj(0, 0) = 1.0 / halfx;
|
||||
light_proj(1, 1) = 1.0 / halfy;
|
||||
light_proj(2, 2) = -2.0 / (far_z - near_z);
|
||||
light_proj(2, 3) = -(far_z + near_z) / (far_z - near_z);
|
||||
|
||||
m_shadow_light_vp = Transform3d(light_proj * light_view);
|
||||
|
||||
// Create / resize the depth texture and FBO
|
||||
const unsigned int size = 2048;
|
||||
if (m_shadow_map_texture_id == 0) {
|
||||
glsafe(::glGenTextures(1, &m_shadow_map_texture_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER));
|
||||
const float border[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
glsafe(::glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border));
|
||||
m_shadow_map_size = 0;
|
||||
}
|
||||
if (m_shadow_map_size != size) {
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, size, size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
|
||||
m_shadow_map_size = size;
|
||||
}
|
||||
if (m_shadow_map_fbo == 0)
|
||||
glsafe(::glGenFramebuffers(1, &m_shadow_map_fbo));
|
||||
|
||||
// Save OpenGL state that we will modify
|
||||
GLint prev_viewport[4] = { 0, 0, 0, 0 };
|
||||
glsafe(::glGetIntegerv(GL_VIEWPORT, prev_viewport));
|
||||
GLint prev_fbo = 0;
|
||||
glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo));
|
||||
GLboolean prev_color_mask[4] = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
|
||||
glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, prev_color_mask));
|
||||
const GLboolean prev_cull = ::glIsEnabled(GL_CULL_FACE);
|
||||
GLint prev_depth_func = GL_LESS;
|
||||
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
||||
GLboolean prev_depth_mask = GL_TRUE;
|
||||
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, m_shadow_map_fbo));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadow_map_texture_id, 0));
|
||||
glsafe(::glDrawBuffer(GL_NONE));
|
||||
glsafe(::glReadBuffer(GL_NONE));
|
||||
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
m_shadow_map_valid = false;
|
||||
} else {
|
||||
glsafe(::glViewport(0, 0, size, size));
|
||||
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
glsafe(::glDepthFunc(GL_LESS));
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(4.0f, 4.0f));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", Transform3d(light_proj));
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
const Transform3d view_model = Transform3d(light_view) * volume->world_matrix();
|
||||
shader->set_uniform("view_model_matrix", view_model);
|
||||
volume->model.render(shader);
|
||||
}
|
||||
shader->stop_using();
|
||||
|
||||
// Restore state
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glColorMask(prev_color_mask[0], prev_color_mask[1], prev_color_mask[2], prev_color_mask[3]));
|
||||
if (prev_cull)
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
else
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
glsafe(::glViewport(prev_viewport[0], prev_viewport[1], prev_viewport[2], prev_viewport[3]));
|
||||
|
||||
m_shadow_map_valid = true;
|
||||
}
|
||||
} else {
|
||||
m_shadow_map_valid = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// Unified plate shadow: draw the build-plate footprint and darken it wherever the same
|
||||
// depth shadow map (built above) says the light is occluded. This replaces the old planar
|
||||
// stencil projection so plate, object and self shadows all come from one technique.
|
||||
// ----------------------------------------------------------------------------------
|
||||
if (!m_shadow_map_valid)
|
||||
return;
|
||||
|
||||
// Shadow projection matrix - flattens geometry onto Z=0 plane along light direction
|
||||
Matrix4d shadow_proj = Matrix4d::Identity();
|
||||
shadow_proj(0, 2) = -ray_dir.x() / ray_dir.z();
|
||||
shadow_proj(1, 2) = -ray_dir.y() / ray_dir.z();
|
||||
shadow_proj(2, 0) = 0.0;
|
||||
shadow_proj(2, 1) = 0.0;
|
||||
shadow_proj(2, 2) = 0.0;
|
||||
shadow_proj(2, 3) = 0.01; // Bias to prevent shadow acne
|
||||
GLShaderProgram* plate_shader = wxGetApp().get_shader("printbed_shadow");
|
||||
if (plate_shader == nullptr)
|
||||
return;
|
||||
|
||||
// Save OpenGL state
|
||||
GLint prev_depth_func = GL_LESS;
|
||||
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
||||
GLboolean prev_depth_mask = GL_TRUE;
|
||||
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
||||
GLint prev_stencil_mask = 0xFF;
|
||||
glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &prev_stencil_mask));
|
||||
GLboolean prev_stencil_test = GL_FALSE;
|
||||
glsafe(::glGetBooleanv(GL_STENCIL_TEST, &prev_stencil_test));
|
||||
|
||||
// ============================================================
|
||||
// PASS 0: Create stencil mask for the build plate (value = 1)
|
||||
// ============================================================
|
||||
glsafe(::glEnable(GL_STENCIL_TEST));
|
||||
glsafe(::glStencilMask(0xFF));
|
||||
glsafe(::glClearStencil(0));
|
||||
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
|
||||
|
||||
glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
|
||||
|
||||
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", projection_matrix);
|
||||
|
||||
// Draw the build plate (cached model to avoid per-frame uploads)
|
||||
if (const BuildVolume& build_volume = m_bed.build_volume(); build_volume.valid()) {
|
||||
const std::string mask_key = build_volume.type() == BuildVolume_Type::Rectangle
|
||||
? (boost::format("rect|%1$.5f|%2$.5f|%3$.5f|%4$.5f")
|
||||
@@ -8101,87 +8256,49 @@ void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, c
|
||||
}
|
||||
|
||||
if (m_plate_shadow_mask.is_initialized()) {
|
||||
shader->set_uniform("view_model_matrix", view_matrix);
|
||||
m_plate_shadow_mask.render(shader);
|
||||
// Blend the shadow over the already-drawn plate. Depth test keeps it behind anything
|
||||
// already in front; depth writes are off, and a small negative polygon offset lifts it
|
||||
// just above the bed to avoid z-fighting.
|
||||
GLboolean prev_depth_mask = GL_TRUE;
|
||||
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
||||
GLint prev_depth_func = GL_LESS;
|
||||
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glDepthFunc(GL_LEQUAL));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(-1.0f, -1.0f));
|
||||
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
|
||||
plate_shader->start_using();
|
||||
plate_shader->set_uniform("view_model_matrix", view_matrix);
|
||||
plate_shader->set_uniform("projection_matrix", projection_matrix);
|
||||
plate_shader->set_uniform("shadow_map", 4);
|
||||
plate_shader->set_uniform("shadow_light_vp", m_shadow_light_vp);
|
||||
plate_shader->set_uniform("shadow_intensity", 0.35f);
|
||||
plate_shader->set_uniform("shadow_map_texel", 1.0f / static_cast<float>(m_shadow_map_size));
|
||||
m_plate_shadow_mask.render(plate_shader);
|
||||
plate_shader->stop_using();
|
||||
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 1: Project object shadows onto plate (increment stencil to 2)
|
||||
// ============================================================
|
||||
// Only render where plate exists (stencil == 1), then increment to 2
|
||||
glsafe(::glStencilFunc(GL_EQUAL, 1, 0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR));
|
||||
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthFunc(GL_ALWAYS)); // Shadows don't need depth testing
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(-2.0f, -2.0f));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
// Render projected shadow geometry
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
// CRITICAL FIX: Apply shadow projection in object's local space, then to world, then to view
|
||||
// This ensures shadows are cast from the object's actual position
|
||||
Matrix4d world_matrix = volume->world_matrix().matrix();
|
||||
|
||||
// Project the shadow - this flattens the geometry onto Z=0 in WORLD space
|
||||
Matrix4d shadow_world_matrix = shadow_proj * world_matrix;
|
||||
|
||||
// Transform to view space for rendering
|
||||
Matrix4d view_shadow_matrix = view_matrix.matrix() * shadow_world_matrix;
|
||||
|
||||
shader->set_uniform("view_model_matrix", view_shadow_matrix);
|
||||
shader->set_uniform("projection_matrix", projection_matrix);
|
||||
|
||||
volume->model.render(shader);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 2: Draw shadow color where stencil == 2
|
||||
// ============================================================
|
||||
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
|
||||
glsafe(::glStencilFunc(GL_EQUAL, 2, 0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
|
||||
glsafe(::glStencilMask(0x00));
|
||||
|
||||
glsafe(::glDepthFunc(GL_ALWAYS));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
// Draw shadow fill
|
||||
shader->set_uniform("view_model_matrix", Transform3d::Identity());
|
||||
shader->set_uniform("projection_matrix", Transform3d::Identity());
|
||||
|
||||
const ColorRGBA shadow_fill_color(0.0f, 0.0f, 0.0f, 0.4f); // Darker shadow for visibility
|
||||
const ColorRGBA prev_bg_color = m_background.get_geometry().color;
|
||||
m_background.set_color(shadow_fill_color);
|
||||
shader->set_uniform("uniform_color", shadow_fill_color);
|
||||
m_background.render(shader);
|
||||
m_background.set_color(prev_bg_color);
|
||||
shader->set_uniform("uniform_color", prev_bg_color);
|
||||
|
||||
shader->stop_using();
|
||||
|
||||
// ============================================================
|
||||
// RESTORE STATE
|
||||
// ============================================================
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
|
||||
if (!prev_stencil_test)
|
||||
glsafe(::glDisable(GL_STENCIL_TEST));
|
||||
glsafe(::glStencilMask(prev_stencil_mask));
|
||||
}
|
||||
|
||||
|
||||
void GLCanvas3D::_render_plane() const
|
||||
{
|
||||
;//TODO render assemble plane
|
||||
@@ -8260,12 +8377,31 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
shader = wxGetApp().get_shader("gouraud");
|
||||
ECanvasType canvas_type = this->m_canvas_type;
|
||||
bool partly_inside_enable = canvas_type == ECanvasType::CanvasAssembleView ? false : true;
|
||||
// The edited printer's per-extruder printable heights feed the object shader's
|
||||
// extruder_printable_heights uniform. Empty for single-extruder printers, so the shader flag stays
|
||||
// 0.0 and rendering is pixel-identical there (see GLVolumeCollection::render).
|
||||
auto printable_height_option = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloatsNullable>("extruder_printable_height");
|
||||
std::vector<double>* printable_heights = printable_height_option ? &printable_height_option->values : nullptr;
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
|
||||
const bool phong_ssao = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
|
||||
shader->set_uniform("enable_ssao", phong_ssao);
|
||||
|
||||
// Object-on-object and self shadows: sample the depth map built in _render_shadow_map_pass().
|
||||
// shadow_intensity == 0 disables the effect entirely (unchanged behavior when off / unsupported).
|
||||
if (m_shadow_map_valid && m_shadow_map_texture_id != 0) {
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
shader->set_uniform("shadow_map", 4);
|
||||
shader->set_uniform("shadow_light_vp", m_shadow_light_vp);
|
||||
shader->set_uniform("shadow_map_texel", 1.0f / static_cast<float>(m_shadow_map_size));
|
||||
shader->set_uniform("shadow_intensity", 0.35f);
|
||||
}
|
||||
else
|
||||
shader->set_uniform("shadow_intensity", 0.0f);
|
||||
|
||||
const Size& cvn_size = get_canvas_size();
|
||||
{
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
@@ -8307,7 +8443,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
|
||||
}
|
||||
},
|
||||
partly_inside_enable);
|
||||
partly_inside_enable, printable_heights);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -8342,7 +8478,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
return true;
|
||||
}
|
||||
},
|
||||
partly_inside_enable);
|
||||
partly_inside_enable, printable_heights);
|
||||
if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) {
|
||||
const GLGizmosManager& gm = get_gizmos_manager();
|
||||
shader->stop_using();
|
||||
@@ -8357,12 +8493,58 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
shader->set_uniform("show_wireframe", false);
|
||||
}*/
|
||||
|
||||
if (m_shadow_map_valid && m_shadow_map_texture_id != 0) {
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
}
|
||||
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_wireframe_overlay()
|
||||
{
|
||||
if (!wxGetApp().plater()->is_show_wireframe())
|
||||
return;
|
||||
if (m_volumes.empty())
|
||||
return;
|
||||
|
||||
#if SLIC3R_OPENGL_ES
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("wireframe");
|
||||
#else
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("mm_contour");
|
||||
#endif
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
const Transform3d& view_matrix = camera.get_view_matrix();
|
||||
const Transform3d& proj_matrix = camera.get_projection_matrix();
|
||||
const Size sz = get_canvas_size();
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("offset", OpenGLManager::get_gl_info().is_mesa() ? 0.0005 : 0.00001);
|
||||
shader->set_uniform("view_model_matrix", view_matrix);
|
||||
shader->set_uniform("projection_matrix", proj_matrix);
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
if (!OpenGLManager::get_gl_info().is_core_profile())
|
||||
glsafe(::glLineWidth(1.0f));
|
||||
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
|
||||
#endif
|
||||
|
||||
m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, view_matrix, proj_matrix, sz);
|
||||
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
|
||||
#endif
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||
{
|
||||
@@ -8756,6 +8938,8 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
||||
return;
|
||||
}
|
||||
|
||||
_update_imgui_select_plate_toolbar();
|
||||
|
||||
IMToolbarItem* all_plates_stats_item = m_sel_plate_toolbar.m_all_plates_stats_item;
|
||||
|
||||
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
|
||||
@@ -9172,7 +9356,6 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar()
|
||||
m_sel_plate_toolbar.is_display_scrollbar = is_win_hovered;
|
||||
|
||||
imgui.end();
|
||||
m_sel_plate_toolbar.is_render_finish = true;
|
||||
}
|
||||
|
||||
//BBS: GUI refactor: GLToolbar adjust
|
||||
@@ -9401,8 +9584,14 @@ void GLCanvas3D::_render_canvas_toolbar()
|
||||
[this]{wxGetApp().toggle_show_outline();}
|
||||
);
|
||||
|
||||
create_menu_item( _utf8(L("Wireframe")),
|
||||
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
|
||||
p->is_show_wireframe(),
|
||||
[this, p]{p->toggle_show_wireframe(); m_dirty = true;}
|
||||
);
|
||||
|
||||
create_menu_item( _utf8(L("Realistic View")),
|
||||
true,
|
||||
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
|
||||
cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE),
|
||||
[this, &cfg]{
|
||||
cfg->set_bool(SETTING_OPENGL_REALISTIC_MODE, !cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE));
|
||||
@@ -10250,6 +10439,9 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||
|
||||
void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
||||
{
|
||||
// Skip on shutdown: Plater's pImpl is already freed, so get_notification_manager() would use-after-free.
|
||||
if (wxGetApp().is_closing())
|
||||
return;
|
||||
using NotificationLevel = NotificationManager::NotificationLevel;
|
||||
enum ErrorType{
|
||||
PLATER_WARNING,
|
||||
|
||||
Reference in New Issue
Block a user