diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index f9e9e668d..f8e31eeb7 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -68,6 +68,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init _zoom _legend_enabled + _warning_enabled _apply_zoom_to_volumes_filter ) ); @@ -142,6 +143,7 @@ sub new { $self->_sphi(45); $self->_zoom(1); $self->_legend_enabled(0); + $self->_warning_enabled(0); $self->use_plain_shader(0); $self->_apply_zoom_to_volumes_filter(0); @@ -217,7 +219,12 @@ sub new { sub set_legend_enabled { my ($self, $value) = @_; - $self->_legend_enabled($value); + $self->_legend_enabled($value); +} + +sub set_warning_enabled { + my ($self, $value) = @_; + $self->_warning_enabled($value); } sub Destroy { @@ -1296,11 +1303,14 @@ sub Render { } glEnable(GL_LIGHTING); - + # draw objects if (! $self->use_plain_shader) { $self->draw_volumes; } elsif ($self->UseVBOs) { + if ($self->enable_picking) { + $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height')); + } $self->{plain_shader}->enable if $self->{plain_shader}; $self->volumes->render_VBOs; $self->{plain_shader}->disable; @@ -1327,6 +1337,9 @@ sub Render { glDisable(GL_BLEND); } + # draw warning message + $self->draw_warning; + # draw gcode preview legend $self->draw_legend; @@ -1599,31 +1612,65 @@ sub draw_active_object_annotations { sub draw_legend { my ($self) = @_; - if ($self->_legend_enabled) - { - # If the legend texture has not been loaded into the GPU, do it now. - my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture; - if ($tex_id > 0) - { - my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width; - my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height; - if (($tex_w > 0) && ($tex_h > 0)) - { - glDisable(GL_DEPTH_TEST); - glPushMatrix(); - glLoadIdentity(); - - my ($cw, $ch) = $self->GetSizeWH; - - my $l = (-0.5 * $cw) / $self->_zoom; - my $t = (0.5 * $ch) / $self->_zoom; - my $r = $l + $tex_w / $self->_zoom; - my $b = $t - $tex_h / $self->_zoom; - $self->_render_texture($tex_id, $l, $r, $b, $t); + if (!$self->_legend_enabled) { + return; + } - glPopMatrix(); - glEnable(GL_DEPTH_TEST); - } + # If the legend texture has not been loaded into the GPU, do it now. + my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture; + if ($tex_id > 0) + { + my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width; + my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height; + if (($tex_w > 0) && ($tex_h > 0)) + { + glDisable(GL_DEPTH_TEST); + glPushMatrix(); + glLoadIdentity(); + + my ($cw, $ch) = $self->GetSizeWH; + + my $l = (-0.5 * $cw) / $self->_zoom; + my $t = (0.5 * $ch) / $self->_zoom; + my $r = $l + $tex_w / $self->_zoom; + my $b = $t - $tex_h / $self->_zoom; + $self->_render_texture($tex_id, $l, $r, $b, $t); + + glPopMatrix(); + glEnable(GL_DEPTH_TEST); + } + } +} + +sub draw_warning { + my ($self) = @_; + + if (!$self->_warning_enabled) { + return; + } + + # If the warning texture has not been loaded into the GPU, do it now. + my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture; + if ($tex_id > 0) + { + my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width; + my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height; + if (($tex_w > 0) && ($tex_h > 0)) + { + glDisable(GL_DEPTH_TEST); + glPushMatrix(); + glLoadIdentity(); + + my ($cw, $ch) = $self->GetSizeWH; + + my $l = (-0.5 * $tex_w) / $self->_zoom; + my $t = (-0.5 * $ch + $tex_h) / $self->_zoom; + my $r = $l + $tex_w / $self->_zoom; + my $b = $t - $tex_h / $self->_zoom; + $self->_render_texture($tex_id, $l, $r, $b, $t); + + glPopMatrix(); + glEnable(GL_DEPTH_TEST); } } } @@ -1694,39 +1741,55 @@ sub _vertex_shader_Gouraud { #define INTENSITY_CORRECTION 0.7 -#define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31 +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +#define LIGHT_TOP_DIR vec3(-0.4574957, 0.4574957, 0.7624929) #define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) #define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION) #define LIGHT_TOP_SHININESS 50. -#define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43 +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +#define LIGHT_FRONT_DIR vec3(0.6985074, 0.1397015, 0.6985074) #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) #define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) #define LIGHT_FRONT_SHININESS 50. #define INTENSITY_AMBIENT 0.3 +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +struct PrintBoxDetection +{ + vec3 min; + vec3 max; + // xyz contains the offset, if w == 1.0 detection needs to be performed + vec4 volume_origin; +}; + +uniform PrintBoxDetection print_box; + varying float intensity_specular; varying float intensity_tainted; +varying vec3 delta_box_min; +varying vec3 delta_box_max; + void main() { - vec3 eye, normal, lightDir, viewVector, halfVector; + vec3 normal, viewVector, halfVector; float NdotL, NdotHV; - eye = vec3(0., 0., 1.); + vec3 eye = -(gl_ModelViewMatrix * gl_Vertex).xyz; // First transform the normal into eye space and normalize the result. normal = normalize(gl_NormalMatrix * gl_Normal); // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. // Also since we're talking about a directional light, the position field is actually direction. - lightDir = vec3(LIGHT_TOP_DIR); - halfVector = normalize(lightDir + eye); + halfVector = normalize(LIGHT_TOP_DIR + eye); // 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. - NdotL = max(dot(normal, lightDir), 0.0); + NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity_specular = 0.; @@ -1735,15 +1798,27 @@ void main() intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS); // Perform the same lighting calculation for the 2nd light source. - lightDir = vec3(LIGHT_FRONT_DIR); -// halfVector = normalize(lightDir + eye); - NdotL = max(dot(normal, lightDir), 0.0); +// halfVector = normalize(LIGHT_FRONT_DIR + eye); + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE; // compute the specular term if NdotL is larger than zero // if (NdotL > 0.0) // intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_FRONT_SHININESS); + // compute deltas for out of print volume detection (world coordinates) + if (print_box.volume_origin.w == 1.0) + { + vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz; + delta_box_min = v - print_box.min; + delta_box_max = v - print_box.max; + } + else + { + delta_box_min = ZERO; + delta_box_max = ZERO; + } + gl_Position = ftransform(); } @@ -1756,14 +1831,23 @@ sub _fragment_shader_Gouraud { varying float intensity_specular; varying float intensity_tainted; +varying vec3 delta_box_min; +varying vec3 delta_box_max; uniform vec4 uniform_color; +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + void main() { gl_FragColor = vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted; - gl_FragColor.a = uniform_color.a; + + // if the fragment is outside the print volume darken it and set it as transparent + if (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) + gl_FragColor = vec4(mix(gl_FragColor.xyz, ZERO, 0.5), 0.5 * uniform_color.a); + else + gl_FragColor.a = uniform_color.a; } FRAGMENT @@ -1828,6 +1912,7 @@ void main() { // compute the specular term into spec // intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS); } + gl_FragColor = max( vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted, INTENSITY_AMBIENT * uniform_color); @@ -1840,12 +1925,12 @@ sub _vertex_shader_variable_layer_height { return <<'VERTEX'; #version 110 -#define LIGHT_TOP_DIR 0., 1., 0. +const vec3 LIGHT_TOP_DIR = vec3(0.0, 1.0, 0.0); #define LIGHT_TOP_DIFFUSE 0.2 #define LIGHT_TOP_SPECULAR 0.3 #define LIGHT_TOP_SHININESS 50. -#define LIGHT_FRONT_DIR 0., 0., 1. +const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0); #define LIGHT_FRONT_DIFFUSE 0.5 #define LIGHT_FRONT_SPECULAR 0.3 #define LIGHT_FRONT_SHININESS 50. @@ -1859,7 +1944,7 @@ varying float object_z; void main() { - vec3 eye, normal, lightDir, viewVector, halfVector; + vec3 eye, normal, viewVector, halfVector; float NdotL, NdotHV; // eye = gl_ModelViewMatrixInverse[3].xyz; @@ -1870,12 +1955,11 @@ void main() // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. // Also since we're talking about a directional light, the position field is actually direction. - lightDir = vec3(LIGHT_TOP_DIR); - halfVector = normalize(lightDir + eye); + halfVector = normalize(LIGHT_TOP_DIR + eye); // 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. - NdotL = max(dot(normal, lightDir), 0.0); + NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity_specular = 0.; @@ -1884,9 +1968,8 @@ void main() // intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS); // Perform the same lighting calculation for the 2nd light source. - lightDir = vec3(LIGHT_FRONT_DIR); - halfVector = normalize(lightDir + eye); - NdotL = max(dot(normal, lightDir), 0.0); + halfVector = normalize(LIGHT_FRONT_DIR + eye); + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE; // compute the specular term if NdotL is larger than zero diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d48e31462..0e498ee8b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -55,6 +55,7 @@ sub new { serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour + max_print_height )]); # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm $self->{model} = Slic3r::Model->new; @@ -113,6 +114,7 @@ sub new { $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); $self->{canvas3D}->set_on_instances_moved($on_instances_moved); + $self->{canvas3D}->use_plain_shader(1); $self->{canvas3D}->set_on_wipe_tower_moved(sub { my ($new_pos_3f) = @_; my $cfg = Slic3r::Config->new; @@ -1737,6 +1739,8 @@ sub on_config_change { $update_scheduled = 1; my $extruder_colors = $config->get('extruder_colour'); $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); + } elsif ($opt_key eq 'max_print_height') { + $update_scheduled = 1; } } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 1c123e741..8f95a92f4 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -8,6 +8,8 @@ use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL); use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR); use base qw(Slic3r::GUI::3DScene Class::Accessor); +use Wx::Locale gettext => 'L'; + __PACKAGE__->mk_accessors(qw( on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly on_remove_object on_increase_objects on_decrease_objects)); @@ -208,6 +210,20 @@ sub reload_scene { $self->{model}->bounding_box->z_max, $self->UseVBOs); } } + + # checks for geometry outside the print volume to render it accordingly + if (scalar @{$self->volumes} > 0) + { + if (!$self->{model}->fits_print_volume($self->{config})) { + $self->set_warning_enabled(1); + $self->volumes->update_outside_state($self->{config}, 0); + Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume")); + } else { + $self->set_warning_enabled(0); + $self->volumes->update_outside_state($self->{config}, 1); + Slic3r::GUI::_3DScene::reset_warning_texture(); + } + } } sub update_bed_size { diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index a7334308c..b425a4e3c 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -94,6 +94,38 @@ public: void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); } void offset(coordf_t delta); PointClass center() const; + + bool contains(const PointClass &point) const { + return BoundingBoxBase::contains(point) && point.z >= this->min.z && point.z <= this->max.z; + } + + bool contains(const BoundingBox3Base& other) const { + if (!contains(other.min)) + return false; + + if (!contains(PointClass(other.max.x, other.min.y, other.min.z))) + return false; + + if (!contains(PointClass(other.max.x, other.max.y, other.min.z))) + return false; + + if (!contains(PointClass(other.min.x, other.max.y, other.min.z))) + return false; + + if (!contains(PointClass(other.min.x, other.min.y, other.max.z))) + return false; + + if (!contains(PointClass(other.max.x, other.min.y, other.max.z))) + return false; + + if (!contains(other.max)) + return false; + + if (!contains(PointClass(other.min.x, other.max.y, other.max.z))) + return false; + + return true; + } }; class BoundingBox : public BoundingBoxBase diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 1de99bbf8..944bf7743 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -222,7 +222,7 @@ bool Model::add_default_instances() } // this returns the bounding box of the *transformed* instances -BoundingBoxf3 Model::bounding_box() +BoundingBoxf3 Model::bounding_box() const { BoundingBoxf3 bb; for (ModelObject *o : this->objects) @@ -230,6 +230,57 @@ BoundingBoxf3 Model::bounding_box() return bb; } +BoundingBoxf3 Model::transformed_bounding_box() const +{ + BoundingBoxf3 bb; + + for (const ModelObject* obj : this->objects) + { + for (const ModelVolume* vol : obj->volumes) + { + if (!vol->modifier) + { + for (const ModelInstance* inst : obj->instances) + { + double c = cos(inst->rotation); + double s = sin(inst->rotation); + + for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f) + { + const stl_facet& facet = vol->mesh.stl.facet_start[f]; + + for (int i = 0; i < 3; ++i) + { + // original point + const stl_vertex& v = facet.vertex[i]; + Pointf3 p((double)v.x, (double)v.y, (double)v.z); + + // scale + p.x *= inst->scaling_factor; + p.y *= inst->scaling_factor; + p.z *= inst->scaling_factor; + + // rotate Z + double x = p.x; + double y = p.y; + p.x = c * x - s * y; + p.y = s * x + c * y; + + // translate + p.x += inst->offset.x; + p.y += inst->offset.y; + + bb.merge(p); + } + } + } + } + } + } + + return bb; +} + void Model::center_instances_around_point(const Pointf &point) { // BoundingBoxf3 bb = this->bounding_box(); @@ -409,6 +460,23 @@ void Model::convert_multipart_object() this->objects.push_back(object); } +bool Model::fits_print_volume(const DynamicPrintConfig* config) const +{ + if (config == nullptr) + return false; + + if (objects.empty()) + return true; + + const ConfigOptionPoints* opt = dynamic_cast(config->option("bed_shape")); + if (opt == nullptr) + return false; + + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); + BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height"))); + return print_volume.contains(transformed_bounding_box()); +} + ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) : name(other.name), input_file(other.input_file), @@ -671,7 +739,7 @@ void ModelObject::transform(const float* matrix3x4) v->mesh.transform(matrix3x4); } - origin_translation = Pointf3(0.0f, 0.0f, 0.0f); + origin_translation = Pointf3(0.0, 0.0, 0.0); invalidate_bounding_box(); } diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index cf9bfd85a..7ef1803ac 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -260,7 +260,10 @@ public: void delete_material(t_model_material_id material_id); void clear_materials(); bool add_default_instances(); - BoundingBoxf3 bounding_box(); + // Returns approximate axis aligned bounding box of this model + BoundingBoxf3 bounding_box() const; + // Returns tight axis aligned bounding box of this model + BoundingBoxf3 transformed_bounding_box() const; void center_instances_around_point(const Pointf &point); void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } TriangleMesh mesh() const; @@ -273,6 +276,9 @@ public: bool looks_like_multipart_object() const; void convert_multipart_object(); + // Returs true if this model is contained into the print volume defined inside the given config + bool fits_print_volume(const DynamicPrintConfig* config) const; + void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); } }; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c689929c6..1ff764e4a 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -120,6 +120,7 @@ bool Print::invalidate_state_by_config_options(const std::vectormin = 0; def->default_value = new ConfigOptionFloats { 0. }; + def = this->add("max_print_height", coFloat); + def->label = L("Max print height"); + def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing."); + def->sidetext = L("mm"); + def->cli = "max-print-height=f"; + def->default_value = new ConfigOptionFloat(200.0); + def = this->add("max_print_speed", coFloat); def->label = L("Max print speed"); def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed " diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index c589d917a..e87ea11bf 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -583,6 +583,7 @@ public: ConfigOptionFloats max_layer_height; ConfigOptionInts min_fan_speed; ConfigOptionFloats min_layer_height; + ConfigOptionFloat max_print_height; ConfigOptionFloats min_print_speed; ConfigOptionFloat min_skirt_length; ConfigOptionString notes; @@ -647,6 +648,7 @@ protected: OPT_PTR(max_layer_height); OPT_PTR(min_fan_speed); OPT_PTR(min_layer_height); + OPT_PTR(max_print_height); OPT_PTR(min_print_speed); OPT_PTR(min_skirt_length); OPT_PTR(notes); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 04e7f7dc6..e49314013 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -46,6 +46,25 @@ void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) } } +void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) +{ + assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); + assert(quad_indices.empty() && triangle_indices_size == 0); + assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); + + this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); + + unsigned int vertices_count = 0; + for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + const stl_facet &facet = mesh.stl.facet_start[i]; + for (int j = 0; j < 3; ++j) + this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z); + + this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); + vertices_count += 3; + } +} + void GLIndexedVertexArray::finalize_geometry(bool use_VBOs) { assert(this->vertices_and_normals_interleaved_VBO_id == 0); @@ -173,6 +192,45 @@ void GLIndexedVertexArray::render( glDisableClientState(GL_NORMAL_ARRAY); } +const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; +const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; +const float GLVolume::OUTSIDE_COLOR[4] = { 0.75f, 0.0f, 0.75f, 1.0f }; +const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 1.0f, 0.0f, 1.0f, 1.0f }; + +void GLVolume::set_render_color(float r, float g, float b, float a) +{ + render_color[0] = r; + render_color[1] = g; + render_color[2] = b; + render_color[3] = a; +} + +void GLVolume::set_render_color(const float* rgba, unsigned int size) +{ + size = std::min((unsigned int)4, size); + for (int i = 0; i < size; ++i) + { + render_color[i] = rgba[i]; + } +} + +void GLVolume::set_render_color() +{ + if (selected) + { + if (is_outside) + set_render_color(SELECTED_OUTSIDE_COLOR, 4); + else + set_render_color(SELECTED_COLOR, 4); + } + else if (hover) + set_render_color(HOVER_COLOR, 4); + else if (is_outside) + set_render_color(OUTSIDE_COLOR, 4); + else + set_render_color(color, 4); +} + void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -288,7 +346,11 @@ std::vector GLVolumeCollection::load_object( color[3] = model_volume->modifier ? 0.5f : 1.f; this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); - v.indexed_vertex_array.load_mesh_flat_shading(mesh); + if (use_VBOs) + v.indexed_vertex_array.load_mesh_full_shading(mesh); + else + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); @@ -319,7 +381,11 @@ int GLVolumeCollection::load_wipe_tower_preview( this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); auto mesh = make_cube(width, depth, height); - v.indexed_vertex_array.load_mesh_flat_shading(mesh); + if (use_VBOs) + v.indexed_vertex_array.load_mesh_full_shading(mesh); + else + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + v.origin = Pointf3(pos_x, pos_y, 0.); // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); @@ -332,8 +398,9 @@ int GLVolumeCollection::load_wipe_tower_preview( void GLVolumeCollection::render_VBOs() const { -// glEnable(GL_BLEND); -// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glCullFace(GL_BACK); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); @@ -341,6 +408,9 @@ void GLVolumeCollection::render_VBOs() const GLint current_program_id; glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1; + GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1; + GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1; for (GLVolume *volume : this->volumes) { if (!volume->is_active) @@ -348,42 +418,72 @@ void GLVolumeCollection::render_VBOs() const if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) continue; + + volume->set_render_color(); + GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); if (n_triangles + n_quads == 0) { - if (_render_interleaved_only_volumes.enabled) + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + if (color_id >= 0) { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - if (color_id >= 0) - { - float color[4]; - ::memcpy((void*)color, (const void*)volume->color, 3 * sizeof(float)); - color[3] = _render_interleaved_only_volumes.alpha; - ::glUniform4fv(color_id, 1, (const GLfloat*)color); - } - else - ::glColor4f(volume->color[0], volume->color[1], volume->color[2], _render_interleaved_only_volumes.alpha); - - volume->render(); - - ::glDisable(GL_BLEND); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); + float color[4]; + ::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float)); + ::glUniform4fv(color_id, 1, (const GLfloat*)color); } + else + ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); + + if (print_box_min_id != -1) + ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); + + if (print_box_max_id != -1) + ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); + + if (print_box_origin_id != -1) + { + float origin[4] = { volume->origin.x, volume->origin.y, volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f }; + ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin); + } + + volume->render(); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + continue; } + if (color_id >= 0) - glUniform4fv(color_id, 1, (const GLfloat*)volume->color); + glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color); else - glColor4f(volume->color[0], volume->color[1], volume->color[2], volume->color[3]); + glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); + + if (print_box_min_id != -1) + ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); + + if (print_box_max_id != -1) + ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); + + if (print_box_origin_id != -1) + { + float origin[4] = { volume->origin.x, volume->origin.y, volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f }; + ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin); + } + glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); + + bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0); + if (has_offset) { + glPushMatrix(); + glTranslated(volume->origin.x, volume->origin.y, volume->origin.z); + } + if (n_triangles > 0) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id); glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4)); @@ -392,17 +492,25 @@ void GLVolumeCollection::render_VBOs() const glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id); glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4)); } + + if (has_offset) + glPopMatrix(); } glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); -// glDisable(GL_BLEND); + + glDisable(GL_BLEND); } void GLVolumeCollection::render_legacy() const { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glCullFace(GL_BACK); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); @@ -412,27 +520,25 @@ void GLVolumeCollection::render_legacy() const if (!volume->is_active) continue; + volume->set_render_color(); + GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); if (n_triangles + n_quads == 0) { - if (_render_interleaved_only_volumes.enabled) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); - ::glColor4f(volume->color[0], volume->color[1], volume->color[2], _render_interleaved_only_volumes.alpha); - volume->render(); + ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); + volume->render(); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); - ::glDisable(GL_BLEND); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - } continue; } - glColor4f(volume->color[0], volume->color[1], volume->color[2], volume->color[3]); + + glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data()); bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0; @@ -445,11 +551,37 @@ void GLVolumeCollection::render_legacy() const if (n_quads > 0) glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first); if (has_offset) - glPushMatrix(); + glPopMatrix(); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); + + glDisable(GL_BLEND); +} + +void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, bool all_inside) +{ + if (config == nullptr) + return; + + const ConfigOptionPoints* opt = dynamic_cast(config->option("bed_shape")); + if (opt == nullptr) + return; + + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); + BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height"))); + + for (GLVolume* volume : this->volumes) + { + if (all_inside) + { + volume->is_outside = false; + continue; + } + + volume->is_outside = !print_volume.contains(volume->transformed_bounding_box()); + } } std::vector GLVolumeCollection::get_current_print_zs() const @@ -1133,6 +1265,100 @@ static void point3_to_verts(const Point3& point, double width, double height, GL _3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; _3DScene::LegendTexture _3DScene::s_legend_texture; +_3DScene::WarningTexture _3DScene::s_warning_texture; + +unsigned int _3DScene::TextureBase::finalize() +{ + if (!m_data.empty()) { + // sends buffer to gpu + ::glGenTextures(1, &m_tex_id); + ::glBindTexture(GL_TEXTURE_2D, m_tex_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data()); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glBindTexture(GL_TEXTURE_2D, 0); + m_data.clear(); + } + return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0; +} + +void _3DScene::TextureBase::_destroy_texture() +{ + if (m_tex_id > 0) + { + ::glDeleteTextures(1, &m_tex_id); + m_tex_id = 0; + m_tex_height = 0; + m_tex_width = 0; + } + m_data.clear(); +} + + +const unsigned char _3DScene::WarningTexture::Background_Color[3] = { 9, 91, 134 }; +const unsigned char _3DScene::WarningTexture::Opacity = 255; + +// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid. +bool _3DScene::WarningTexture::generate(const std::string& msg) +{ + // Mark the texture as released, but don't release the texture from the GPU yet. + m_tex_width = m_tex_height = 0; + m_data.clear(); + + if (msg.empty()) + return false; + + wxMemoryDC memDC; + // select default font + memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + + // calculates texture size + wxCoord w, h; + memDC.GetTextExtent(msg, &w, &h); + m_tex_width = (unsigned int)w; + m_tex_height = (unsigned int)h; + + // generates bitmap + wxBitmap bitmap(m_tex_width, m_tex_height); + +#if defined(__APPLE__) || defined(_MSC_VER) + bitmap.UseAlpha(); +#endif + + memDC.SelectObject(bitmap); + memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); + memDC.Clear(); + + memDC.SetTextForeground(*wxWHITE); + + // draw message + memDC.DrawText(msg, 0, 0); + + memDC.SelectObject(wxNullBitmap); + + // Convert the bitmap into a linear data ready to be loaded into the GPU. + { + wxImage image = bitmap.ConvertToImage(); + image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]); + + // prepare buffer + m_data.assign(4 * m_tex_width * m_tex_height, 0); + for (unsigned int h = 0; h < m_tex_height; ++h) + { + unsigned int hh = h * m_tex_width; + unsigned char* px_ptr = m_data.data() + 4 * hh; + for (unsigned int w = 0; w < m_tex_width; ++w) + { + *px_ptr++ = image.GetRed(w, h); + *px_ptr++ = image.GetGreen(w, h); + *px_ptr++ = image.GetBlue(w, h); + *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity; + } + } + } + return true; +} const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 }; const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 }; @@ -1276,34 +1502,6 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con return true; } -unsigned int _3DScene::LegendTexture::finalize() -{ - if (! m_data.empty()) { - // sends buffer to gpu - ::glGenTextures(1, &m_tex_id); - ::glBindTexture(GL_TEXTURE_2D, m_tex_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data()); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - ::glBindTexture(GL_TEXTURE_2D, 0); - m_data.clear(); - } - return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0; -} - -void _3DScene::LegendTexture::_destroy_texture() -{ - if (m_tex_id > 0) - { - ::glDeleteTextures(1, &m_tex_id); - m_tex_id = 0; - m_tex_height = 0; - m_tex_width = 0; - } - m_data.clear(); -} - void _3DScene::_glew_init() { glewInit(); @@ -1353,27 +1551,17 @@ void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* pr _load_gcode_unretractions(*preview_data, *volumes, use_VBOs); if (volumes->empty()) - { reset_legend_texture(); - volumes->set_render_interleaved_only_volumes(GLVolumeCollection::RenderInterleavedOnlyVolumes(false, 0.0f)); - } else { _generate_legend_texture(*preview_data, tool_colors); - _load_shells(*print, *volumes, use_VBOs); - volumes->set_render_interleaved_only_volumes(GLVolumeCollection::RenderInterleavedOnlyVolumes(true, 0.25f)); } } _update_gcode_volumes_visibility(*preview_data, *volumes); } -unsigned int _3DScene::get_legend_texture_id() -{ - return s_legend_texture.get_texture_id(); -} - unsigned int _3DScene::get_legend_texture_width() { return s_legend_texture.get_texture_width(); @@ -1389,6 +1577,36 @@ void _3DScene::reset_legend_texture() s_legend_texture.reset_texture(); } +unsigned int _3DScene::finalize_legend_texture() +{ + return s_legend_texture.finalize(); +} + +unsigned int _3DScene::get_warning_texture_width() +{ + return s_warning_texture.get_texture_width(); +} + +unsigned int _3DScene::get_warning_texture_height() +{ + return s_warning_texture.get_texture_height(); +} + +void _3DScene::generate_warning_texture(const std::string& msg) +{ + s_warning_texture.generate(msg); +} + +void _3DScene::reset_warning_texture() +{ + s_warning_texture.reset_texture(); +} + +unsigned int _3DScene::finalize_warning_texture() +{ + return s_warning_texture.finalize(); +} + // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. void _3DScene::_load_print_toolpaths( @@ -1501,6 +1719,7 @@ void _3DScene::_load_print_object_toolpaths( auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { auto *volume = new GLVolume(color); new_volume_mutex.lock(); + volume->outside_printer_detection_enabled = false; volumes->volumes.emplace_back(volume); new_volume_mutex.unlock(); return volume; @@ -1651,6 +1870,7 @@ void _3DScene::_load_wipe_tower_toolpaths( auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { auto *volume = new GLVolume(color); new_volume_mutex.lock(); + volume->outside_printer_detection_enabled = false; volumes->volumes.emplace_back(volume); new_volume_mutex.unlock(); return volume; @@ -2221,6 +2441,7 @@ void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_ for (std::vector::iterator it = begin; it != end; ++it) { GLVolume* volume = *it; + volume->outside_printer_detection_enabled = false; switch (s_gcode_preview_volume_index.first_volumes[i].type) { @@ -2253,6 +2474,7 @@ void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_ case GCodePreviewVolumeIndex::Shell: { volume->is_active = preview_data.shell.is_visible; + volume->color[3] = 0.25f; volume->zoom_to_volumes = false; break; } @@ -2272,11 +2494,6 @@ void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, co s_legend_texture.generate(preview_data, tool_colors); } -unsigned int _3DScene::finalize_legend_texture() -{ - return s_legend_texture.finalize(); -} - void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) { size_t initial_volumes_count = volumes.volumes.size(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 3e26bf6d3..e1e711bfe 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -16,6 +16,7 @@ class PrintObject; class Model; class ModelObject; class GCodePreviewData; +class DynamicPrintConfig; // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. @@ -85,6 +86,7 @@ public: unsigned int quad_indices_VBO_id; void load_mesh_flat_shading(const TriangleMesh &mesh); + void load_mesh_full_shading(const TriangleMesh &mesh); inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } @@ -209,6 +211,11 @@ public: class GLVolume { public: + static const float SELECTED_COLOR[4]; + static const float HOVER_COLOR[4]; + static const float OUTSIDE_COLOR[4]; + static const float SELECTED_OUTSIDE_COLOR[4]; + GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) : composite_id(-1), select_group_id(-1), @@ -216,6 +223,8 @@ public: selected(false), is_active(true), zoom_to_volumes(true), + outside_printer_detection_enabled(true), + is_outside(false), hover(false), tverts_range(0, size_t(-1)), qverts_range(0, size_t(-1)) @@ -224,6 +233,7 @@ public: color[1] = g; color[2] = b; color[3] = a; + set_render_color(r, g, b, a); } GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} @@ -243,7 +253,8 @@ public: Pointf3 origin; // Color of the triangles / quads held by this volume. float color[4]; - + // Color used to render this volume. + float render_color[4]; // An ID containing the object ID, volume ID and instance ID. int composite_id; // An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. @@ -256,6 +267,10 @@ public: bool is_active; // Whether or not to use this volume when applying zoom_to_volumes() bool zoom_to_volumes; + // Wheter or not this volume is enabled for outside print volume detection. + bool outside_printer_detection_enabled; + // Wheter or not this volume is outside print volume. + bool is_outside; // Boolean: Is mouse over this object? bool hover; @@ -271,6 +286,10 @@ public: // Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer. std::vector offsets; + void set_render_color(float r, float g, float b, float a); + void set_render_color(const float* rgba, unsigned int size); + // Sets render color in dependence of current state + void set_render_color(); int object_idx() const { return this->composite_id / 1000000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; } @@ -313,27 +332,9 @@ public: class GLVolumeCollection { -public: - struct RenderInterleavedOnlyVolumes - { - bool enabled; - float alpha; // [0..1] - - RenderInterleavedOnlyVolumes() - : enabled(false) - , alpha(0.0f) - { - } - - RenderInterleavedOnlyVolumes(bool enabled, float alpha) - : enabled(enabled) - , alpha(alpha) - { - } - }; - -private: - RenderInterleavedOnlyVolumes _render_interleaved_only_volumes; + // min and max vertex of the print box volume + float print_box_min[3]; + float print_box_max[3]; public: std::vector volumes; @@ -370,7 +371,12 @@ public: bool empty() const { return volumes.empty(); } void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } - void set_render_interleaved_only_volumes(const RenderInterleavedOnlyVolumes& render_interleaved_only_volumes) { _render_interleaved_only_volumes = render_interleaved_only_volumes; } + void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) { + print_box_min[0] = min_x; print_box_min[1] = min_y; print_box_min[2] = min_z; + print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z; + } + + void update_outside_state(const DynamicPrintConfig* config, bool all_inside); // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection std::vector get_current_print_zs() const; @@ -411,27 +417,20 @@ class _3DScene static GCodePreviewVolumeIndex s_gcode_preview_volume_index; - class LegendTexture + class TextureBase { - static const unsigned int Px_Title_Offset = 5; - static const unsigned int Px_Text_Offset = 5; - static const unsigned int Px_Square = 20; - static const unsigned int Px_Square_Contour = 1; - static const unsigned int Px_Border = Px_Square / 2; - static const unsigned char Squares_Border_Color[3]; - static const unsigned char Background_Color[3]; - static const unsigned char Opacity; - + protected: unsigned int m_tex_id; unsigned int m_tex_width; unsigned int m_tex_height; + // generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering. + std::vector m_data; + public: - LegendTexture() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {} - ~LegendTexture() { _destroy_texture(); } - - // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet. - bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors); + TextureBase() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {} + virtual ~TextureBase() { _destroy_texture(); } + // If not loaded, load the texture data into the GPU. Return a texture ID or 0 if the texture has zero size. unsigned int finalize(); @@ -442,26 +441,61 @@ class _3DScene void reset_texture() { _destroy_texture(); } private: - bool _create_texture(const GCodePreviewData& preview_data, const wxBitmap& bitmap); void _destroy_texture(); - // generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering. - std::vector m_data; + }; + + class WarningTexture : public TextureBase + { + static const unsigned char Background_Color[3]; + static const unsigned char Opacity; + + public: + WarningTexture() : TextureBase() {} + + // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet. + bool generate(const std::string& msg); + }; + + class LegendTexture : public TextureBase + { + static const unsigned int Px_Title_Offset = 5; + static const unsigned int Px_Text_Offset = 5; + static const unsigned int Px_Square = 20; + static const unsigned int Px_Square_Contour = 1; + static const unsigned int Px_Border = Px_Square / 2; + static const unsigned char Squares_Border_Color[3]; + static const unsigned char Background_Color[3]; + static const unsigned char Opacity; + + public: + LegendTexture() : TextureBase() {} + + // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet. + bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors); }; static LegendTexture s_legend_texture; + static WarningTexture s_warning_texture; public: static void _glew_init(); static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs); - static unsigned int get_legend_texture_id(); static unsigned int get_legend_texture_width(); static unsigned int get_legend_texture_height(); static void reset_legend_texture(); static unsigned int finalize_legend_texture(); + static unsigned int get_warning_texture_width(); + static unsigned int get_warning_texture_height(); + + // generates a warning texture containing the given message + static void generate_warning_texture(const std::string& msg); + static void reset_warning_texture(); + static unsigned int finalize_warning_texture(); + static void _load_print_toolpaths( const Print *print, GLVolumeCollection *volumes, diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 52717e1fc..aa1917bd5 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -226,7 +226,7 @@ const std::vector& Preset::printer_options() "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "between_objects_gcode", "printer_notes" + "between_objects_gcode", "printer_notes", "max_print_height" }; s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); } diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 7ce6790a1..82a14ce87 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -968,7 +968,8 @@ void TabPrinter::build() return sizer; }; optgroup->append_line(line); - optgroup->append_single_option_line("z_offset"); + optgroup->append_single_option_line("max_print_height"); + optgroup->append_single_option_line("z_offset"); optgroup = page->new_optgroup(_(L("Capabilities"))); ConfigOptionDef def; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 4670c5c7e..6324f933e 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -97,6 +97,9 @@ void finalize_geometry(bool use_VBOs); void release_geometry(); + void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z); + void update_outside_state(DynamicPrintConfig* config, bool all_inside); + bool move_volume_up(int idx) %code%{ if (idx > 0 && idx < int(THIS->volumes.size())) { @@ -154,13 +157,6 @@ finalize_legend_texture() OUTPUT: RETVAL -unsigned int -get_legend_texture_id() - CODE: - RETVAL = _3DScene::get_legend_texture_id(); - OUTPUT: - RETVAL - unsigned int get_legend_texture_width() CODE: @@ -179,7 +175,38 @@ void reset_legend_texture() CODE: _3DScene::reset_legend_texture(); - + +void +generate_warning_texture(std::string msg) + CODE: + _3DScene::generate_warning_texture(msg); + +unsigned int +finalize_warning_texture() + CODE: + RETVAL = _3DScene::finalize_warning_texture(); + OUTPUT: + RETVAL + +unsigned int +get_warning_texture_width() + CODE: + RETVAL = _3DScene::get_warning_texture_width(); + OUTPUT: + RETVAL + +unsigned int +get_warning_texture_height() + CODE: + RETVAL = _3DScene::get_warning_texture_height(); + OUTPUT: + RETVAL + +void +reset_warning_texture() + CODE: + _3DScene::reset_warning_texture(); + void _load_print_toolpaths(print, volumes, tool_colors, use_VBOs) Print *print; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index c0271ae75..78c94661e 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -99,6 +99,9 @@ void print_info() const; + bool fits_print_volume(DynamicPrintConfig* config) const + %code%{ RETVAL = THIS->fits_print_volume(config); %}; + bool store_stl(char *path, bool binary) %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %}; bool store_amf(char *path, Print* print) @@ -361,9 +364,9 @@ ModelMaterial::attributes() %code%{ RETVAL = &THIS->offset; %}; void set_rotation(double val) - %code%{ THIS->rotation = val; %}; + %code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %}; void set_scaling_factor(double val) - %code%{ THIS->scaling_factor = val; %}; + %code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %}; void set_offset(Pointf *offset) %code%{ THIS->offset = *offset; %};