diff --git a/resources/shaders/110/gouraud_light_clip.fs b/resources/shaders/110/gouraud_light_clip.fs new file mode 100644 index 000000000..5c7068709 --- /dev/null +++ b/resources/shaders/110/gouraud_light_clip.fs @@ -0,0 +1,17 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + if (clipping_planes_dot < 0.0) + discard; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/110/gouraud_light_clip.vs b/resources/shaders/110/gouraud_light_clip.vs new file mode 100644 index 000000000..6d7c32e1b --- /dev/null +++ b/resources/shaders/110/gouraud_light_clip.vs @@ -0,0 +1,54 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 eye_position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; + + // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded. + clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane); +} diff --git a/resources/shaders/140/gouraud_light_clip.fs b/resources/shaders/140/gouraud_light_clip.fs new file mode 100644 index 000000000..714e5bcaa --- /dev/null +++ b/resources/shaders/140/gouraud_light_clip.fs @@ -0,0 +1,19 @@ +#version 140 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +in vec2 intensity; + +in float clipping_planes_dot; + +out vec4 out_color; + +void main() +{ + if (clipping_planes_dot < 0.0) + discard; + + out_color = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/140/gouraud_light_clip.vs b/resources/shaders/140/gouraud_light_clip.vs new file mode 100644 index 000000000..8fca59380 --- /dev/null +++ b/resources/shaders/140/gouraud_light_clip.vs @@ -0,0 +1,54 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +in vec3 v_position; +in vec3 v_normal; + +// x = tainted, y = specular; +out vec2 intensity; + +out float clipping_planes_dot; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 eye_position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; + + // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded. + clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane); +} diff --git a/resources/shaders/ES/gouraud_light_clip.fs b/resources/shaders/ES/gouraud_light_clip.fs new file mode 100644 index 000000000..45cae0ddb --- /dev/null +++ b/resources/shaders/ES/gouraud_light_clip.fs @@ -0,0 +1,19 @@ +#version 100 + +precision highp float; + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + if (clipping_planes_dot < 0.0) + discard; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/ES/gouraud_light_clip.vs b/resources/shaders/ES/gouraud_light_clip.vs new file mode 100644 index 000000000..f3ab8c3dc --- /dev/null +++ b/resources/shaders/ES/gouraud_light_clip.vs @@ -0,0 +1,54 @@ +#version 100 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 eye_position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; + + // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded. + clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane); +} diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 9ca610087..ca74cb391 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -476,6 +476,11 @@ public: const PrintObjects& objects() const { return m_objects; } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. + const SLAPrintObject* get_print_object_by_model_object_id(ObjectID object_id) const { + auto it = std::find_if(m_objects.begin(), m_objects.end(), + [object_id](const SLAPrintObject* obj) { return obj->model_object()->id() == object_id; }); + return (it == m_objects.end()) ? nullptr : *it; + } const SLAPrintObject* get_object(ObjectID object_id) const { auto it = std::find_if(m_objects.begin(), m_objects.end(), [object_id](const SLAPrintObject *obj) { return obj->id() == object_id; }); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 103ab339c..c6ef2d13b 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -852,74 +852,6 @@ int GLVolumeCollection::load_object_volume( return int(this->volumes.size() - 1); } -//// Load SLA auxiliary GLVolumes (for support trees or pad). -//// This function produces volumes for multiple instances in a single shot, -//// as some object specific mesh conversions may be expensive. -//#if ENABLE_LEGACY_OPENGL_REMOVAL -//void GLVolumeCollection::load_object_auxiliary( -// const SLAPrintObject* print_object, -// int obj_idx, -// // pairs of -// const std::vector>& instances, -// SLAPrintObjectStep milestone, -// // Timestamp of the last change of the milestone -// size_t timestamp) -//#else -//void GLVolumeCollection::load_object_auxiliary( -// const SLAPrintObject *print_object, -// int obj_idx, -// // pairs of -// const std::vector>& instances, -// SLAPrintObjectStep milestone, -// // Timestamp of the last change of the milestone -// size_t timestamp, -// bool opengl_initialized) -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//{ -// assert(print_object->is_step_done(milestone)); -// Transform3d mesh_trafo_inv = print_object->trafo().inverse(); -// // Get the support mesh. -// TriangleMesh mesh = print_object->get_mesh(milestone); -// mesh.transform(mesh_trafo_inv); -// // Convex hull is required for out of print bed detection. -// TriangleMesh convex_hull = mesh.convex_hull_3d(); -// for (const std::pair& instance_idx : instances) { -// const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; -// this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); -// GLVolume& v = *this->volumes.back(); -//#if ENABLE_LEGACY_OPENGL_REMOVAL -//#if ENABLE_SMOOTH_NORMALS -// v.model.init_from(mesh, true); -//#else -// v.model.init_from(mesh); -// v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); -//#if ENABLE_RAYCAST_PICKING -// v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -//#endif // ENABLE_RAYCAST_PICKING -//#endif // ENABLE_SMOOTH_NORMALS -//#else -//#if ENABLE_SMOOTH_NORMALS -// v.indexed_vertex_array.load_mesh(mesh, true); -//#else -// v.indexed_vertex_array.load_mesh(mesh); -//#endif // ENABLE_SMOOTH_NORMALS -// v.indexed_vertex_array.finalize_geometry(opengl_initialized); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -// v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); -// v.geometry_id = std::pair(timestamp, model_instance.id().id); -// // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. -// if (&instance_idx == &instances.back()) -// v.set_convex_hull(std::move(convex_hull)); -// else -// v.set_convex_hull(convex_hull); -// v.is_modifier = false; -// v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); -// v.set_instance_transformation(model_instance.get_transformation()); -// // Leave the volume transformation at identity. -// // v.set_volume_transformation(model_volume->get_transformation()); -// } -//} - #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_OPENGL_ES int GLVolumeCollection::load_wipe_tower_preview( diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index abf3e9264..d1c16290f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -658,16 +658,6 @@ public: int volume_idx, int instance_idx); - // Load SLA auxiliary GLVolumes (for support trees or pad). -// void load_object_auxiliary( -// const SLAPrintObject* print_object, -// int obj_idx, -// // pairs of -// const std::vector>& instances, -// SLAPrintObjectStep milestone, -// // Timestamp of the last change of the milestone -// size_t timestamp); - #if ENABLE_OPENGL_ES int load_wipe_tower_preview( float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr); @@ -689,17 +679,6 @@ public: int instance_idx, bool opengl_initialized); - // Load SLA auxiliary GLVolumes (for support trees or pad). - void load_object_auxiliary( - const SLAPrintObject *print_object, - int obj_idx, - // pairs of - const std::vector>& instances, - SLAPrintObjectStep milestone, - // Timestamp of the last change of the milestone - size_t timestamp, - bool opengl_initialized); - int load_wipe_tower_preview( float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized); #endif // ENABLE_LEGACY_OPENGL_REMOVAL diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a698633a8..636080db7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2011,15 +2011,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volume_idx; }; -// // SLA steps to pull the preview meshes for. -// typedef std::array SLASteps; -// SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; -// struct SLASupportState { -// std::array::value> step; -// }; -// // State of the sla_steps for all SLAPrintObjects. -// std::vector sla_support_state; - std::vector instance_ids_selected; std::vector map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); std::vector deleted_volumes; @@ -2044,33 +2035,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } } -// if (printer_technology == ptSLA) { -// const SLAPrint* sla_print = this->sla_print(); -//#ifndef NDEBUG -// // Verify that the SLAPrint object is synchronized with m_model. -// check_model_ids_equal(*m_model, sla_print->model()); -//#endif /* NDEBUG */ -// sla_support_state.reserve(sla_print->objects().size()); -// for (const SLAPrintObject* print_object : sla_print->objects()) { -// SLASupportState state; -// for (size_t istep = 0; istep < sla_steps.size(); ++istep) { -// state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]); -// if (state.step[istep].state == PrintStateBase::DONE) { -// if (!print_object->has_mesh(sla_steps[istep])) -// // Consider the DONE step without a valid mesh as invalid for the purpose -// // of mesh visualization. -// state.step[istep].state = PrintStateBase::INVALID; -// else if (sla_steps[istep] != slaposDrillHoles) -// for (const ModelInstance* model_instance : print_object->model_object()->instances) -// // Only the instances, which are currently printable, will have the SLA support structures kept. -// // The instances outside the print bed will have the GLVolumes of their support structures released. -// if (model_instance->is_printable()) -// aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); -// } -// } -// sla_support_state.emplace_back(state); -// } -// } + std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. @@ -2190,139 +2155,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } if (printer_technology == ptSLA) { -// size_t idx = 0; -// const SLAPrint *sla_print = this->sla_print(); -// std::vector shift_zs(m_model->objects.size(), 0); -// double relative_correction_z = sla_print->relative_correction().z(); -// if (relative_correction_z <= EPSILON) -// relative_correction_z = 1.; -// for (const SLAPrintObject *print_object : sla_print->objects()) { -// SLASupportState &state = sla_support_state[idx ++]; -// const ModelObject *model_object = print_object->model_object(); -// // Find an index of the ModelObject -// int object_idx; -// // There may be new SLA volumes added to the scene for this print_object. -// // Find the object index of this print_object in the Model::objects list. -// auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); -// assert(it != sla_print->model().objects.end()); -// object_idx = it - sla_print->model().objects.begin(); -// // Cache the Z offset to be applied to all volumes with this object_idx. -// shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z; -// // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene. -// // pairs of -// std::vector> instances[std::tuple_size::value]; -// for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) { -// const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx]; -// // Find index of ModelInstance corresponding to this SLAPrintObject::Instance. -// auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), -// [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; }); -// assert(it != model_object->instances.end()); -// int instance_idx = it - model_object->instances.begin(); -// for (size_t istep = 0; istep < sla_steps.size(); ++ istep) -// if (sla_steps[istep] == slaposDrillHoles) { -// // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, -// // not into its own GLVolume. -// // There shall always be such a GLVolume allocated. -// ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); -// auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); -// assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); -// assert(!it->new_geometry()); -// GLVolume &volume = *m_volumes.volumes[it->volume_idx]; -// if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { -// // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.reset(); -//#else -// volume.indexed_vertex_array.release_geometry(); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -// if (state.step[istep].state == PrintStateBase::DONE) { -// TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); -// assert(! mesh.empty()); - -// // sla_trafo does not contain volume trafo. To get a mesh to create -// // a new volume from, we have to apply vol trafo inverse separately. -// const ModelObject& mo = *m_model->objects[volume.object_idx()]; -// Transform3d trafo = sla_print->sla_trafo(mo) -// * mo.volumes.front()->get_transformation().get_matrix(); -// mesh.transform(trafo.inverse()); -//#if ENABLE_SMOOTH_NORMALS -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.init_from(mesh, true); -//#else -// volume.indexed_vertex_array.load_mesh(mesh, true); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#else -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.init_from(mesh); -//#if ENABLE_RAYCAST_PICKING -// volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -//#endif // ENABLE_RAYCAST_PICKING -//#else -// volume.indexed_vertex_array.load_mesh(mesh); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#endif // ENABLE_SMOOTH_NORMALS -// } -// else { -// // Reload the original volume. -//#if ENABLE_SMOOTH_NORMALS -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -//#else -// volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#else -//#if ENABLE_LEGACY_OPENGL_REMOVAL -//#if ENABLE_RAYCAST_PICKING -// const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); -// volume.model.init_from(new_mesh); -// volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); -//#else -// volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); -//#endif // ENABLE_RAYCAST_PICKING -//#else -// volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#endif // ENABLE_SMOOTH_NORMALS -// } -//#if !ENABLE_LEGACY_OPENGL_REMOVAL -// volume.finalize_geometry(true); -//#endif // !ENABLE_LEGACY_OPENGL_REMOVAL -// } -// //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable -// // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables -// // of various concenrs (model vs. 3D print path). -// volume.offsets = { state.step[istep].timestamp }; -// } -// else if (state.step[istep].state == PrintStateBase::DONE) { -// // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. -// ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); -// auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); -// assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); -// if (it->new_geometry()) { -// // This can be an SLA support structure that should not be rendered (in case someone used undo -// // to revert to before it was generated). If that's the case, we should not generate anything. -// if (model_object->sla_points_status != sla::PointsStatus::NoPoints) -// instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); -// else -// shift_zs[object_idx] = 0.; -// } -// else { -// // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. -// m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); -// m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); -// } -// } -// } - -// for (size_t istep = 0; istep < sla_steps.size(); ++istep) -// if (!instances[istep].empty()) -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); -//#else -// m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -// } - // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { @@ -7638,25 +7470,24 @@ void GLCanvas3D::_load_sla_shells() }; // adds objects' volumes - for (const SLAPrintObject* obj : print->objects()) - /*if (obj->is_step_done(slaposSliceSupports))*/ { - unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); - for (const SLAPrintObject::Instance& instance : obj->instances()) { - add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); - // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when - // through the update_volumes_colors_by_extruder() call. - m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) - add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); - if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) - add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); - } - double shift_z = obj->get_current_elevation(); - for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { - // apply shift z - m_volumes.volumes[i]->set_sla_shift_z(shift_z); - } + for (const SLAPrintObject* obj : print->objects()) { + unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); + for (const SLAPrintObject::Instance& instance : obj->instances()) { + add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); + // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when + // through the update_volumes_colors_by_extruder() call. + m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); + if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) + add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); + if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) + add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); } + double shift_z = obj->get_current_elevation(); + for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { + // apply shift z + m_volumes.volumes[i]->set_sla_shift_z(shift_z); + } + } update_volumes_colors_by_extruder(); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 140cbaf74..a519a26a1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -688,8 +688,6 @@ public: void set_raycaster_gizmos_on_top(bool value) { m_scene_raycaster.set_gizmos_on_top(value); } - - const SceneRaycaster & raycaster() const { return m_scene_raycaster; } #endif // ENABLE_RAYCAST_PICKING void set_as_dirty(); diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index f0b91faf3..a60a5121b 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -72,6 +72,8 @@ std::pair GLShadersManager::init() #if ENABLE_LEGACY_OPENGL_REMOVAL // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" }); + // extend "gouraud_light" by adding clipping, used in sla gizmos + valid &= append_shader("gouraud_light_clip", { prefix + "gouraud_light_clip.vs", prefix + "gouraud_light_clip.fs" }); // used to render printbed valid &= append_shader("printbed", { prefix + "printbed.vs", prefix + "printbed.fs" }); #else diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 52aed6bfc..d098d41f5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -10,6 +10,7 @@ #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Plater.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/SLAPrint.hpp" #include "libslic3r/Model.hpp" @@ -53,14 +54,21 @@ void GLGizmoHollow::data_changed() reload_cache(); m_old_mo_id = mo->id(); } -// if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) -// m_holes_in_drilled_mesh = mo->sla_drain_holes; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po != nullptr && po->get_mesh_to_print().empty()) + process_mesh(slaposAssembly); + + update_volumes(); + #if ENABLE_RAYCAST_PICKING - if (m_raycasters.empty()) - on_register_raycasters_for_picking(); + if (m_hole_raycasters.empty()) + register_hole_raycasters_for_picking(); else - update_raycasters_for_picking_transform(); + update_hole_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING + + m_c->instances_hider()->set_hide_full_scene(true); } } @@ -87,16 +95,15 @@ void GLGizmoHollow::on_render() glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - if (selection.is_from_single_instance()) #if ENABLE_RAYCAST_PICKING - render_points(selection); + render_volumes(); + render_points(selection); #else - render_points(selection, false); + render_points(selection, false); #endif // ENABLE_RAYCAST_PICKING m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); - m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } @@ -104,27 +111,14 @@ void GLGizmoHollow::on_render() #if ENABLE_RAYCAST_PICKING void GLGizmoHollow::on_register_raycasters_for_picking() { - assert(m_raycasters.empty()); - - init_cylinder_model(); - - set_sla_auxiliary_volumes_picking_state(false); - - const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); - if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { - const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; - for (int i = 0; i < (int)drain_holes.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster)); - } - update_raycasters_for_picking_transform(); - } + register_hole_raycasters_for_picking(); + register_volume_raycasters_for_picking(); } void GLGizmoHollow::on_unregister_raycasters_for_picking() { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); - m_raycasters.clear(); - set_sla_auxiliary_volumes_picking_state(true); + unregister_hole_raycasters_for_picking(); + unregister_volume_raycasters_for_picking(); } #else void GLGizmoHollow::on_render_for_picking() @@ -190,7 +184,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #if ENABLE_RAYCAST_PICKING const bool clipped = is_mesh_point_clipped(drain_hole.pos.cast()); - m_raycasters[i]->set_active(!clipped); + m_hole_raycasters[i]->set_active(!clipped); if (clipped) continue; #else @@ -270,6 +264,25 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +void GLGizmoHollow::render_volumes() +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("emission_factor", 0.0f); + const Camera& camera = wxGetApp().plater()->get_camera(); + + ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); + clipping_plane.set_normal(-clipping_plane.get_normal()); + m_volumes.set_clipping_plane(clipping_plane.get_data()); + + m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->stop_using(); + +} + bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const { if (m_c->object_clipper()->get_position() == 0.) @@ -291,48 +304,26 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { + if (m_c->raycaster()->raycasters().size() != 1) + return false; if (! m_c->raycaster()->raycaster()) return false; - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); - - double clp_dist = m_c->object_clipper()->get_position(); - const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); - // The raycaster query Vec3f hit; Vec3f normal; if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, - trafo.get_matrix(), - camera, + m_volumes.volumes.front()->world_matrix(), + wxGetApp().plater()->get_camera(), hit, normal, - clp_dist != 0. ? clp : nullptr)) - { -// if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) { -// // in this case the raycaster sees the hollowed and drilled mesh. -// // if the point lies on the surface created by the hole, we want -// // to ignore it. -// for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) { -// sla::DrainHole outer(hole); -// outer.radius *= 1.001f; -// outer.height *= 1.001f; -// if (outer.is_inside(hit)) -// return false; -// } -// } - + m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; } - else - return false; + return false; } // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. @@ -348,9 +339,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos // left down with shift - show the selection rectangle: if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { if (m_hover_id == -1) { - if (shift_down || alt_down) { + if (shift_down || alt_down) m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); - } } else { if (m_selected[m_hover_id]) @@ -383,8 +373,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos m_parent.set_as_dirty(); m_wait_for_up_event = true; #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_hole_raycasters_for_picking(); + register_hole_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING } else @@ -510,8 +500,8 @@ void GLGizmoHollow::delete_selected_points() } #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_hole_raycasters_for_picking(); + register_hole_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING select_point(NoPoints); } @@ -580,39 +570,61 @@ bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event) return false; } -void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) +void GLGizmoHollow::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages) { - wxGetApp().CallAfter([this, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_hollowing( - *m_c->selection_info()->model_object(), postpone_error_messages); - }); + wxGetApp().CallAfter([this, step, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); + }); } #if ENABLE_RAYCAST_PICKING -void GLGizmoHollow::set_sla_auxiliary_volumes_picking_state(bool state) +void GLGizmoHollow::register_hole_raycasters_for_picking() { - std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (raycasters != nullptr) { - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList ids = selection.get_volume_idxs(); - for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - if (v->is_sla_pad() || v->is_sla_support()) { - auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); - if (it != raycasters->end()) - (*it)->set_active(state); - } + assert(m_hole_raycasters.empty()); + + init_cylinder_model(); + + const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); + if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { + const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; + for (int i = 0; i < (int)drain_holes.size(); ++i) { + m_hole_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity())); } + update_hole_raycasters_for_picking_transform(); } } -void GLGizmoHollow::update_raycasters_for_picking_transform() +void GLGizmoHollow::unregister_hole_raycasters_for_picking() +{ + for (size_t i = 0; i < m_hole_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, i); + } + m_hole_raycasters.clear(); +} + +void GLGizmoHollow::register_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + const GLVolume* v = m_volumes.volumes[i]; + m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix())); + } +} + +void GLGizmoHollow::unregister_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i); + } + m_volume_raycasters.clear(); +} + +void GLGizmoHollow::update_hole_raycasters_for_picking_transform() { const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); if (info != nullptr) { const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; if (!drain_holes.empty()) { - assert(!m_raycasters.empty()); + assert(!m_hole_raycasters.empty()); const GLVolume* vol = m_parent.get_selection().get_first_volume(); const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_scaling_factor_matrix().inverse(); @@ -625,13 +637,62 @@ void GLGizmoHollow::update_raycasters_for_picking_transform() const Eigen::AngleAxisd aa(q); const Transform3d matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); - m_raycasters[i]->set_transform(matrix); + m_hole_raycasters[i]->set_transform(matrix); } } } } #endif // ENABLE_RAYCAST_PICKING +void GLGizmoHollow::update_volumes() +{ + m_volumes.clear(); + unregister_volume_raycasters_for_picking(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo == nullptr) + return; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po == nullptr) + return; + + TriangleMesh backend_mesh = po->get_mesh_to_print(); + if (!backend_mesh.empty()) { + // The backend has generated a valid mesh. Use it + backend_mesh.transform(po->trafo().inverse()); + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + new_volume->model.init_from(backend_mesh); + new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); + new_volume->set_sla_shift_z(po->get_current_elevation()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(backend_mesh); + } + + if (m_volumes.volumes.empty()) { + // No valid mesh found in the backend. Use the selection to duplicate the volumes + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int idx : idxs) { + const GLVolume* v = selection.get_volume(idx); + if (!v->is_modifier) { + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); + new_volume->model.init_from(mesh); + new_volume->set_instance_transformation(v->get_instance_transformation()); + new_volume->set_volume_transformation(v->get_volume_transformation()); + new_volume->set_sla_shift_z(v->get_sla_shift_z()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(mesh); + } + } + } + + register_volume_raycasters_for_picking(); +} + std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const { @@ -724,8 +785,8 @@ RENDER_AGAIN: window_width = std::max(window_width, button_preview_width); if (m_imgui->button(m_desc["preview"])) - hollow_mesh(); - + process_mesh(slaposDrillHoles); + bool config_changed = false; ImGui::Separator(); @@ -896,13 +957,6 @@ RENDER_AGAIN: if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position_by_ratio(clp_dist, true); - // make sure supports are shown/hidden as appropriate - bool show_sups = m_c->instances_hider()->are_supports_shown(); - if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) { - m_c->instances_hider()->show_supports(show_sups); - force_refresh = true; - } - m_imgui->end(); @@ -935,7 +989,7 @@ bool GLGizmoHollow::on_is_activable() const const Selection& selection = m_parent.get_selection(); if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA - || !selection.is_from_single_instance()) + || !selection.is_single_full_instance()) return false; // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. @@ -964,9 +1018,7 @@ CommonGizmosDataID GLGizmoHollow::on_get_requirements() const int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::HollowedMesh) - | int(CommonGizmosDataID::ObjectClipper) - | int(CommonGizmosDataID::SupportsClipper)); + | int(CommonGizmosDataID::ObjectClipper)); } @@ -975,8 +1027,12 @@ void GLGizmoHollow::on_set_state() if (m_state == m_old_state) return; - if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off + if (m_state == Off && m_old_state != Off) { + // the gizmo was just turned Off m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); + m_c->instances_hider()->set_hide_full_scene(false); + } + m_old_state = m_state; } @@ -1091,6 +1147,9 @@ void GLGizmoHollow::reload_cache() void GLGizmoHollow::on_set_hover_id() { + if (m_c->selection_info()->model_object() == nullptr) + return; + if (int(m_c->selection_info()->model_object()->sla_drain_holes.size()) <= m_hover_id) m_hover_id = -1; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 0fdc3b2db..e7a324a1e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -3,6 +3,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" +#include "slic3r/GUI/3DScene.hpp" #include #include @@ -25,7 +26,6 @@ class GLGizmoHollow : public GLGizmoBase private: bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); - public: GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); void data_changed() override; @@ -59,21 +59,29 @@ private: #else void render_points(const Selection& selection, bool picking = false); #endif // ENABLE_RAYCAST_PICKING - void hollow_mesh(bool postpone_error_messages = false); + void render_volumes(); + void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false); #if ENABLE_RAYCAST_PICKING - void set_sla_auxiliary_volumes_picking_state(bool state); - void update_raycasters_for_picking_transform(); + void register_hole_raycasters_for_picking(); + void unregister_hole_raycasters_for_picking(); + void register_volume_raycasters_for_picking(); + void unregister_volume_raycasters_for_picking(); + void update_hole_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING + void update_volumes(); ObjectID m_old_mo_id = -1; #if ENABLE_RAYCAST_PICKING PickingModel m_cylinder; - std::vector> m_raycasters; + std::vector> m_hole_raycasters; + std::vector> m_volume_raycasters; #else GLModel m_cylinder; #endif // ENABLE_RAYCAST_PICKING + GLVolumeCollection m_volumes; + float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; mutable std::vector m_selected; // which holes are currently selected diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index d0dd3baa0..cc45a8e73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -60,6 +60,16 @@ bool GLGizmoSlaSupports::on_init() return true; } +static int last_completed_step(const SLAPrint& sla) +{ + int step = -1; + for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) { + if (sla.is_step_done((SLAPrintObjectStep)i)) + ++step; + } + return step; +} + void GLGizmoSlaSupports::data_changed() { if (! m_c->selection_info()) @@ -71,19 +81,25 @@ void GLGizmoSlaSupports::data_changed() disable_editing_mode(); reload_cache(); m_old_mo_id = mo->id(); - m_c->instances_hider()->show_supports(true); } // If we triggered autogeneration before, check backend and fetch results if they are there if (mo) { + m_c->instances_hider()->set_hide_full_scene(true); + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po != nullptr && last_completed_step(*po->print()) < (int)slaposDrillHoles) + process_mesh(slaposDrillHoles, false); + + update_volumes(); + if (mo->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); #if ENABLE_RAYCAST_PICKING - if (m_raycasters.empty()) - on_register_raycasters_for_picking(); + if (m_point_raycasters.empty()) + register_point_raycasters_for_picking(); else - update_raycasters_for_picking_transform(); + update_point_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING } @@ -111,8 +127,6 @@ void GLGizmoSlaSupports::on_render() if (!m_sphere.is_initialized()) m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); #endif // ENABLE_RAYCAST_PICKING - if (!m_cylinder.is_initialized()) - m_cylinder.init_from(its_make_cylinder(1.0, 1.0, double(PI) / 12.0)); ModelObject* mo = m_c->selection_info()->model_object(); const Selection& selection = m_parent.get_selection(); @@ -128,16 +142,15 @@ void GLGizmoSlaSupports::on_render() glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - if (selection.is_from_single_instance()) #if ENABLE_RAYCAST_PICKING - render_points(selection); + render_volumes(); + render_points(selection); #else - render_points(selection, false); + render_points(selection, false); #endif // ENABLE_RAYCAST_PICKING m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); - m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } @@ -145,23 +158,14 @@ void GLGizmoSlaSupports::on_render() #if ENABLE_RAYCAST_PICKING void GLGizmoSlaSupports::on_register_raycasters_for_picking() { - assert(m_raycasters.empty()); - set_sla_auxiliary_volumes_picking_state(false); - - if (m_editing_mode && !m_editing_cache.empty()) { - for (size_t i = 0; i < m_editing_cache.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster), - m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster)); - } - update_raycasters_for_picking_transform(); - } + register_point_raycasters_for_picking(); + register_volume_raycasters_for_picking(); } void GLGizmoSlaSupports::on_unregister_raycasters_for_picking() { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); - m_raycasters.clear(); - set_sla_auxiliary_volumes_picking_state(true); + unregister_point_raycasters_for_picking(); + unregister_volume_raycasters_for_picking(); } #else void GLGizmoSlaSupports::on_render_for_picking() @@ -181,10 +185,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); const bool has_points = (cache_size != 0); - const bool has_holes = (/*! m_c->hollowed_mesh()->get_hollowed_mesh() - &&*/ ! m_c->selection_info()->model_object()->sla_drain_holes.empty()); - - if (! has_points && ! has_holes) + if (!has_points) return; #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -234,9 +235,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #if ENABLE_RAYCAST_PICKING const bool clipped = is_mesh_point_clipped(support_point.pos.cast()); - if (!m_raycasters.empty()) { - m_raycasters[i].first->set_active(!clipped); - m_raycasters[i].second->set_active(!clipped); + if (i < m_point_raycasters.size()) { + m_point_raycasters[i].first->set_active(!clipped); + m_point_raycasters[i].second->set_active(!clipped); } if (clipped) continue; @@ -306,8 +307,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (m_editing_mode) { // in case the normal is not yet cached, find and cache it if (m_editing_cache[i].normal == Vec3f::Zero()) - m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); - //m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); @@ -373,67 +373,28 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } - // Now render the drain holes: -#if ENABLE_RAYCAST_PICKING - if (has_holes) { -#else - if (has_holes && ! picking) { -#endif // ENABLE_RAYCAST_PICKING - render_color = { 0.7f, 0.7f, 0.7f, 0.7f }; -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_cylinder.set_color(render_color); -#else - m_cylinder.set_color(-1, render_color); - if (shader != nullptr) -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - shader->set_uniform("emission_factor", 0.5f); - for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { - if (is_mesh_point_clipped(drain_hole.pos.cast())) - continue; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; -#else - // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.pos.x(), drain_hole.pos.y(), drain_hole.pos.z())); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - if (vol->is_left_handed()) - glsafe(::glFrontFace(GL_CW)); - - // Matrices set, we can render the point mark now. - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); - const Eigen::AngleAxisd aa(q); -#if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d model_matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * - Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); - shader->set_uniform("view_model_matrix", view_matrix * model_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); -#else - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z())); - glsafe(::glTranslated(0., 0., -drain_hole.height)); - glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - m_cylinder.render(); - - if (vol->is_left_handed()) - glsafe(::glFrontFace(GL_CCW)); -#if !ENABLE_LEGACY_OPENGL_REMOVAL - glsafe(::glPopMatrix()); -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - } - } - #if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glPopMatrix()); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +void GLGizmoSlaSupports::render_volumes() +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); + if (shader == nullptr) + return; + shader->start_using(); + shader->set_uniform("emission_factor", 0.0f); + const Camera& camera = wxGetApp().plater()->get_camera(); + + ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); + clipping_plane.set_normal(-clipping_plane.get_normal()); + m_volumes.set_clipping_plane(clipping_plane.get_data()); + + m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->stop_using(); +} bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { @@ -456,48 +417,22 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->empty()) + if (!m_c->raycaster()->raycaster()) return false; - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); - - double clp_dist = m_c->object_clipper()->get_position(); - const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); - // The raycaster query Vec3f hit; Vec3f normal; - if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->unproject_on_mesh( + if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, - trafo.get_matrix(), - camera, + m_volumes.volumes.front()->world_matrix(), + wxGetApp().plater()->get_camera(), hit, normal, - clp_dist != 0. ? clp : nullptr)) - { - // Check whether the hit is in a hole -// bool in_hole = false; - // In case the hollowed and drilled mesh is available, we can allow - // placing points in holes, because they should never end up - // on surface that's been drilled away. -// if (! m_c->hollowed_mesh()->get_hollowed_mesh()) { -// sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; -// for (const sla::DrainHole& hole : drain_holes) { -// if (hole.is_inside(hit)) { -// in_hole = true; -// break; -// } -// } -// } -// if (! in_hole) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); - return true; -// } + m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; } return false; @@ -548,8 +483,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_parent.set_as_dirty(); m_wait_for_up_event = true; #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_point_raycasters_for_picking(); + register_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING } else @@ -592,9 +527,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous for (size_t idx : points_idxs) points_inside.emplace_back((trafo.get_matrix().cast() * (m_editing_cache[idx].support_point.pos + m_editing_cache[idx].normal)).cast()); - assert(!m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume).emtpy()); - for (size_t idx : m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_unobscured_idxs( - trafo, wxGetApp().plater()->get_camera(), points_inside, + for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs( + trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->object_clipper()->get_clipping_plane())) { if (idx >= orig_pts_num) // this is a cone-base, get index of point it belongs to @@ -712,8 +646,8 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) } #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_point_raycasters_for_picking(); + register_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING select_point(NoPoints); @@ -1027,7 +961,7 @@ bool GLGizmoSlaSupports::on_is_activable() const const Selection& selection = m_parent.get_selection(); if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA - || !selection.is_from_single_instance()) + || !selection.is_single_full_instance()) return false; // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. @@ -1055,9 +989,7 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::HollowedMesh) - | int(CommonGizmosDataID::ObjectClipper) - | int(CommonGizmosDataID::SupportsClipper)); + | int(CommonGizmosDataID::ObjectClipper)); } @@ -1101,6 +1033,9 @@ void GLGizmoSlaSupports::on_set_state() disable_editing_mode(); // so it is not active next time the gizmo opens m_old_mo_id = -1; } + + if (m_state == Off) + m_c->instances_hider()->set_hide_full_scene(false); } m_old_state = m_state; } @@ -1395,10 +1330,8 @@ void GLGizmoSlaSupports::switch_to_editing_mode() m_editing_cache.emplace_back(sp); select_point(NoPoints); #if ENABLE_RAYCAST_PICKING - on_register_raycasters_for_picking(); + register_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING - - m_c->instances_hider()->show_supports(false); m_parent.set_as_dirty(); } @@ -1408,10 +1341,9 @@ void GLGizmoSlaSupports::disable_editing_mode() if (m_editing_mode) { m_editing_mode = false; wxGetApp().plater()->leave_gizmos_stack(); - m_c->instances_hider()->show_supports(true); m_parent.set_as_dirty(); #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); + unregister_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING } wxGetApp().plater()->get_notification_manager()->close_notification_of_type(NotificationType::QuitSLAManualMode); @@ -1432,53 +1364,130 @@ bool GLGizmoSlaSupports::unsaved_changes() const } #if ENABLE_RAYCAST_PICKING -void GLGizmoSlaSupports::set_sla_auxiliary_volumes_picking_state(bool state) +void GLGizmoSlaSupports::register_point_raycasters_for_picking() { - std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (raycasters != nullptr) { - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList ids = selection.get_volume_idxs(); - for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - if (v->is_sla_pad() || v->is_sla_support()) { - auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); - if (it != raycasters->end()) - (*it)->set_active(state); - } + assert(m_point_raycasters.empty()); + + if (m_editing_mode && !m_editing_cache.empty()) { + for (size_t i = 0; i < m_editing_cache.size(); ++i) { + m_point_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()), + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity())); } + update_point_raycasters_for_picking_transform(); } } -void GLGizmoSlaSupports::update_raycasters_for_picking_transform() +void GLGizmoSlaSupports::unregister_point_raycasters_for_picking() { - if (!m_editing_cache.empty()) { - assert(!m_raycasters.empty()); + for (size_t i = 0; i < m_point_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, i); + } + m_point_raycasters.clear(); +} - const GLVolume* vol = m_parent.get_selection().get_first_volume(); - const Geometry::Transformation transformation(vol->world_matrix()); - const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); - for (size_t i = 0; i < m_editing_cache.size(); ++i) { - const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; +void GLGizmoSlaSupports::register_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + const GLVolume* v = m_volumes.volumes[i]; + m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix())); + } +} - if (m_editing_cache[i].normal == Vec3f::Zero()) - m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); +void GLGizmoSlaSupports::unregister_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i); + } + m_volume_raycasters.clear(); +} - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); - const Eigen::AngleAxisd aa(q); - const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * - Geometry::translation_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ()) * - Geometry::rotation_transform({ double(PI), 0.0, 0.0 }) * Geometry::scale_transform({ CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT }); - m_raycasters[i].second->set_transform(cone_matrix); +void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform() +{ + if (m_editing_cache.empty()) + return; - const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale; - const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); - m_raycasters[i].first->set_transform(sphere_matrix); - } + assert(!m_point_raycasters.empty()); + + const GLVolume* vol = m_parent.get_selection().get_first_volume(); + const Geometry::Transformation transformation(vol->world_matrix()); + const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); + for (size_t i = 0; i < m_editing_cache.size(); ++i) { + const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; + + if (m_editing_cache[i].normal == Vec3f::Zero()) + m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); + const Eigen::AngleAxisd aa(q); + const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::assemble_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), + Vec3d(PI, 0.0, 0.0), Vec3d(CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT)); + m_point_raycasters[i].second->set_transform(cone_matrix); + + const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale; + const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); + m_point_raycasters[i].first->set_transform(sphere_matrix); } } #endif // ENABLE_RAYCAST_PICKING +void GLGizmoSlaSupports::update_volumes() +{ + m_volumes.clear(); + unregister_volume_raycasters_for_picking(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo == nullptr) + return; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po == nullptr) + return; + + TriangleMesh backend_mesh = po->get_mesh_to_print(); + if (!backend_mesh.empty()) { + // The backend has generated a valid mesh. Use it + backend_mesh.transform(po->trafo().inverse()); + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + new_volume->model.init_from(backend_mesh); + new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); + new_volume->set_sla_shift_z(po->get_current_elevation()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(backend_mesh); + } + + if (m_volumes.volumes.empty()) { + // No valid mesh found in the backend. Use the selection to duplicate the volumes + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int idx : idxs) { + const GLVolume* v = selection.get_volume(idx); + if (!v->is_modifier) { + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); + new_volume->model.init_from(mesh); + new_volume->set_instance_transformation(v->get_instance_transformation()); + new_volume->set_volume_transformation(v->get_volume_transformation()); + new_volume->set_sla_shift_z(v->get_sla_shift_z()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(mesh); + } + } + } + + register_volume_raycasters_for_picking(); +} + +void GLGizmoSlaSupports::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages) +{ + wxGetApp().CallAfter([this, step, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); + }); +} + SlaGizmoHelpDialog::SlaGizmoHelpDialog() : wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index c4232ed48..6729d6eda 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -3,6 +3,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" +#include "slic3r/GUI/3DScene.hpp" #include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/ObjectID.hpp" @@ -22,7 +23,6 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoSlaSupports : public GLGizmoBase { private: - bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); static constexpr float RenderPointScale = 1.f; @@ -93,11 +93,17 @@ private: #else void render_points(const Selection& selection, bool picking = false); #endif // ENABLE_RAYCAST_PICKING + void render_volumes(); bool unsaved_changes() const; #if ENABLE_RAYCAST_PICKING - void set_sla_auxiliary_volumes_picking_state(bool state); - void update_raycasters_for_picking_transform(); + void register_point_raycasters_for_picking(); + void unregister_point_raycasters_for_picking(); + void register_volume_raycasters_for_picking(); + void unregister_volume_raycasters_for_picking(); + void update_point_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING + void update_volumes(); + void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false); bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? @@ -113,12 +119,14 @@ private: #if ENABLE_RAYCAST_PICKING PickingModel m_sphere; PickingModel m_cone; - std::vector, std::shared_ptr>> m_raycasters; + std::vector, std::shared_ptr>> m_point_raycasters; + std::vector> m_volume_raycasters; #else GLModel m_cone; GLModel m_sphere; #endif // ENABLE_RAYCAST_PICKING - GLModel m_cylinder; + + GLVolumeCollection m_volumes; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 31fbb0f7a..d8a858bac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -59,13 +59,6 @@ InstancesHider* CommonGizmosDataPool::instances_hider() const return inst_hider->is_valid() ? inst_hider : nullptr; } -//HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const -//{ -// HollowedMesh* hol_mesh = dynamic_cast(m_data.at(CommonGizmosDataID::HollowedMesh).get()); -// assert(hol_mesh); -// return hol_mesh->is_valid() ? hol_mesh : nullptr; -//} - Raycaster* CommonGizmosDataPool::raycaster() const { Raycaster* rc = dynamic_cast(m_data.at(CommonGizmosDataID::Raycaster).get()); @@ -123,9 +116,8 @@ void SelectionInfo::on_update() if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; - if (m_model_object) - m_print_object = get_pool()->get_canvas()->sla_print()->get_object(m_model_object->id()); + m_print_object = get_pool()->get_canvas()->sla_print()->get_print_object_by_model_object_id(m_model_object->id()); m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } @@ -154,8 +146,10 @@ void InstancesHider::on_update() if (mo && active_inst != -1) { canvas->toggle_model_objects_visibility(false); - canvas->toggle_model_objects_visibility(true, mo, active_inst); - canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); + if (!m_hide_full_scene) { + canvas->toggle_model_objects_visibility(true, mo, active_inst); + canvas->toggle_sla_auxiliaries_visibility(false, mo, active_inst); + } canvas->set_use_clipping_planes(true); // Some objects may be sinking, do not show whatever is below the bed. canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); @@ -188,9 +182,10 @@ void InstancesHider::on_release() m_clippers.clear(); } -void InstancesHider::show_supports(bool show) { - if (m_show_supports != show) { - m_show_supports = show; +void InstancesHider::set_hide_full_scene(bool hide) +{ + if (m_hide_full_scene != hide) { + m_hide_full_scene = hide; on_update(); } } @@ -255,81 +250,6 @@ void InstancesHider::render_cut() const -//void HollowedMesh::on_update() -//{ -// const ModelObject* mo = get_pool()->selection_info()->model_object(); -// bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; -// if (! mo || ! is_sla) -// return; - -// const GLCanvas3D* canvas = get_pool()->get_canvas(); -// const PrintObjects& print_objects = canvas->sla_print()->objects(); -// const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size())) -// ? print_objects[m_print_object_idx] -// : nullptr; - -// // Find the respective SLAPrintObject. -// if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { -// m_print_objects_count = print_objects.size(); -// m_print_object_idx = -1; -// for (const SLAPrintObject* po : print_objects) { -// ++m_print_object_idx; -// if (po->model_object()->id() == mo->id()) { -// print_object = po; -// break; -// } -// } -// } - -// // If there is a valid SLAPrintObject, check state of Hollowing step. -// if (print_object) { -// if (print_object->is_step_done(slaposDrillHoles) && !print_object->get_mesh_to_print().empty()) { -// size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; -// if (timestamp > m_old_hollowing_timestamp) { -// const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); -// if (! backend_mesh.empty()) { -// m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); -// Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse(); -// m_hollowed_mesh_transformed->transform(trafo_inv); -// m_drainholes = print_object->model_object()->sla_drain_holes; -// m_old_hollowing_timestamp = timestamp; - -//// indexed_triangle_set interior = print_object->hollowed_interior_mesh(); -//// its_flip_triangles(interior); -//// m_hollowed_interior_transformed = std::make_unique(std::move(interior)); -//// m_hollowed_interior_transformed->transform(trafo_inv); -// } -// else { -// m_hollowed_mesh_transformed.reset(nullptr); -// } -// } -// } -// else -// m_hollowed_mesh_transformed.reset(nullptr); -// } -//} - - -//void HollowedMesh::on_release() -//{ -// m_hollowed_mesh_transformed.reset(); -// m_old_hollowing_timestamp = 0; -// m_print_object_idx = -1; -//} - - -//const TriangleMesh* HollowedMesh::get_hollowed_mesh() const -//{ -// return m_hollowed_mesh_transformed.get(); -//} - -//const TriangleMesh* HollowedMesh::get_hollowed_interior() const -//{ -// return m_hollowed_interior_transformed.get(); -//} - - - void Raycaster::on_update() { @@ -347,21 +267,28 @@ void Raycaster::on_update() mvs = mo->volumes; std::vector meshes; - const std::vector& mvs = mo->volumes; -// if (mvs.size() == 1) { -// assert(mvs.front()->is_model_part()); -// const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); -// if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh()) -// meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); -// } - if (meshes.empty()) { - for (const ModelVolume* v : mvs) { - if (v->is_model_part()) - meshes.push_back(&v->mesh()); + bool force_raycaster_regeneration = false; + if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { + // For sla printers we use the mesh generated by the backend + const SLAPrintObject* po = get_pool()->selection_info()->print_object(); + assert(po != nullptr); + m_sla_mesh_cache = po->get_mesh_to_print(); + if (!m_sla_mesh_cache.empty()) { + m_sla_mesh_cache.transform(po->trafo().inverse()); + meshes.emplace_back(&m_sla_mesh_cache); + force_raycaster_regeneration = true; } } - if (meshes != m_old_meshes) { + if (meshes.empty()) { + const std::vector& mvs = mo->volumes; + for (const ModelVolume* mv : mvs) { + if (mv->is_model_part()) + meshes.push_back(&mv->mesh()); + } + } + + if (force_raycaster_regeneration || meshes != m_old_meshes) { m_raycasters.clear(); for (const TriangleMesh* mesh : meshes) #if ENABLE_RAYCAST_PICKING @@ -399,25 +326,36 @@ void ObjectClipper::on_update() // which mesh should be cut? std::vector meshes; -// bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh(); -// if (has_hollowed) -// meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); + std::vector trafos; + bool force_clipper_regeneration = false; + if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { + // For sla printers we use the mesh generated by the backend + const SLAPrintObject* po = get_pool()->selection_info()->print_object(); + assert(po != nullptr); + m_sla_mesh_cache = po->get_mesh_to_print(); + if (!m_sla_mesh_cache.empty()) { + m_sla_mesh_cache.transform(po->trafo().inverse()); + meshes.emplace_back(&m_sla_mesh_cache); + trafos.emplace_back(Geometry::Transformation()); + force_clipper_regeneration = true; + } + } - if (meshes.empty()) - for (const ModelVolume* mv : mo->volumes) - meshes.push_back(&mv->mesh()); + if (meshes.empty()) { + for (const ModelVolume* mv : mo->volumes) { + meshes.emplace_back(&mv->mesh()); + trafos.emplace_back(mv->get_transformation()); + } + } - if (meshes != m_old_meshes) { + if (force_clipper_regeneration || meshes != m_old_meshes) { m_clippers.clear(); - for (const TriangleMesh* mesh : meshes) { - m_clippers.emplace_back(new MeshClipper); - m_clippers.back()->set_mesh(*mesh); + for (size_t i = 0; i < meshes.size(); ++i) { + m_clippers.emplace_back(new MeshClipper, trafos[i]); + m_clippers.back().first->set_mesh(*meshes[i]); } m_old_meshes = meshes; -// if (has_hollowed) -// m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); - m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); } @@ -437,46 +375,27 @@ void ObjectClipper::render_cut() const { if (m_clp_ratio == 0.) return; + const SelectionInfo* sel_info = get_pool()->selection_info(); - int sel_instance_idx = sel_info->get_active_instance(); - if (sel_instance_idx < 0) - return; - const ModelObject* mo = sel_info->model_object(); - const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); - - size_t clipper_id = 0; - for (const ModelVolume* mv : mo->volumes) { - const Geometry::Transformation vol_trafo = mv->get_transformation(); - Geometry::Transformation trafo = inst_trafo * vol_trafo; + const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + for (auto& clipper : m_clippers) { + Geometry::Transformation trafo = inst_trafo * clipper.second; trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - auto& clipper = m_clippers[clipper_id]; - clipper->set_plane(*m_clp); - clipper->set_transformation(trafo); - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); -#if ENABLE_LEGACY_OPENGL_REMOVAL - clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); - clipper->render_contour({ 1.f, 1.f, 1.f, 1.f}); -#else - glsafe(::glPushMatrix()); - glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); - clipper->render_cut(); - glsafe(::glColor3f(1.f, 1.f, 1.f)); - clipper->render_contour(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - ++clipper_id; + clipper.first->set_plane(*m_clp); + clipper.first->set_transformation(trafo); + clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + clipper.first->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); } } bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const std::unique_ptr& cl) { return cl->is_projection_inside_cut(point); }); + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto& cl) { return cl.first->is_projection_inside_cut(point); }); } bool ObjectClipper::has_valid_contour() const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const std::unique_ptr& cl) { return cl->has_valid_contour(); }); + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); } void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) @@ -514,13 +433,13 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou { m_hide_clipped = hide_clipped; for (auto& clipper : m_clippers) - clipper->set_behaviour(fill_cut, contour_width); + clipper.first->set_behaviour(fill_cut, contour_width); } void ObjectClipper::pass_mouse_click(const Vec3d& pt) { for (auto& clipper : m_clippers) - clipper->pass_mouse_click(pt); + clipper.first->pass_mouse_click(pt); } std::vector ObjectClipper::get_disabled_contours() const @@ -586,7 +505,6 @@ void SupportsClipper::render_cut() const { const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper(); if (ocl->get_position() == 0. - || ! get_pool()->instances_hider()->are_supports_shown() || ! m_clipper) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index d6c580123..aa9493d9d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -63,7 +63,6 @@ enum class CommonGizmosDataID { None = 0, SelectionInfo = 1 << 0, InstancesHider = 1 << 1, - HollowedMesh = 1 << 2, Raycaster = 1 << 3, ObjectClipper = 1 << 4, SupportsClipper = 1 << 5, @@ -187,8 +186,7 @@ public: CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG - void show_supports(bool show); - bool are_supports_shown() const { return m_show_supports; } + void set_hide_full_scene(bool hide); void render_cut() const; protected: @@ -196,42 +194,13 @@ protected: void on_release() override; private: - bool m_show_supports = false; + bool m_hide_full_scene{ false }; std::vector m_old_meshes; std::vector> m_clippers; }; -//class HollowedMesh : public CommonGizmosDataBase -//{ -//public: -// explicit HollowedMesh(CommonGizmosDataPool* cgdp) -// : CommonGizmosDataBase(cgdp) {} -//#ifndef NDEBUG -// CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } -//#endif // NDEBUG - -// const sla::DrainHoles &get_drainholes() const { return m_drainholes; } - -// const TriangleMesh* get_hollowed_mesh() const; -// const TriangleMesh* get_hollowed_interior() const; - -//protected: -// void on_update() override; -// void on_release() override; - -//private: -// std::unique_ptr m_hollowed_mesh_transformed; -// std::unique_ptr m_hollowed_interior_transformed; -// size_t m_old_hollowing_timestamp = 0; -// int m_print_object_idx = -1; -// int m_print_objects_count = 0; -// sla::DrainHoles m_drainholes; -//}; - - - class Raycaster : public CommonGizmosDataBase { public: @@ -251,6 +220,8 @@ protected: private: std::vector> m_raycasters; std::vector m_old_meshes; + // Used to store the sla mesh coming from the backend + TriangleMesh m_sla_mesh_cache; }; @@ -285,7 +256,9 @@ protected: private: std::vector m_old_meshes; - std::vector> m_clippers; + // Used to store the sla mesh coming from the backend + TriangleMesh m_sla_mesh_cache; + std::vector, Geometry::Transformation>> m_clippers; std::unique_ptr m_clp; double m_clp_ratio = 0.; double m_active_inst_bb_radius = 0.; diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index f309d6eaa..9e726b7f4 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -100,7 +100,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came }; #if ENABLE_RAYCAST_PICKING_DEBUG - m_last_hit.reset(); + const_cast*>(&m_last_hit)->reset(); #endif // ENABLE_RAYCAST_PICKING_DEBUG HitResult ret; @@ -142,7 +142,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came ret.raycaster_id = decode_id(ret.type, ret.raycaster_id); #if ENABLE_RAYCAST_PICKING_DEBUG - m_last_hit = ret; + *const_cast*>(&m_last_hit) = ret; #endif // ENABLE_RAYCAST_PICKING_DEBUG return ret; }