mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge remote-tracking branch 'remotes/origin/master' into vb_print_regions
This commit is contained in:
		
						commit
						033d9f3a5e
					
				
					 38 changed files with 1502 additions and 587 deletions
				
			
		|  | @ -1,11 +1,12 @@ | |||
| #version 110 | ||||
| 
 | ||||
| uniform vec4 uniform_color; | ||||
| uniform float emission_factor; | ||||
| 
 | ||||
| // x = tainted, y = specular; | ||||
| varying vec2 intensity; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|     gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a); | ||||
|     gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); | ||||
| } | ||||
|  |  | |||
|  | @ -542,7 +542,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec | |||
| 
 | ||||
|             if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) { | ||||
|                 const_cast<Print*>(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, | ||||
|                     _(L("Empty layers detected, the output would not be printable.")) + "\n\n" + | ||||
|                     _(L("Empty layers detected. Make sure the object is printable.")) + "\n\n" + | ||||
|                     _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + | ||||
|                     std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " | ||||
|                         "usually caused by negligibly small extrusions or by a faulty model. Try to repair " | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ using Vec2crd = Eigen::Matrix<coord_t,  2, 1, Eigen::DontAlign>; | |||
| using Vec3crd = Eigen::Matrix<coord_t,  3, 1, Eigen::DontAlign>; | ||||
| using Vec2i   = Eigen::Matrix<int,      2, 1, Eigen::DontAlign>; | ||||
| using Vec3i   = Eigen::Matrix<int,      3, 1, Eigen::DontAlign>; | ||||
| using Vec4i   = Eigen::Matrix<int,      4, 1, Eigen::DontAlign>; | ||||
| using Vec2i32 = Eigen::Matrix<int32_t,  2, 1, Eigen::DontAlign>; | ||||
| using Vec2i64 = Eigen::Matrix<int64_t,  2, 1, Eigen::DontAlign>; | ||||
| using Vec3i32 = Eigen::Matrix<int32_t,  3, 1, Eigen::DontAlign>; | ||||
|  | @ -50,6 +51,8 @@ using Matrix2f       = Eigen::Matrix<float,  2, 2, Eigen::DontAlign>; | |||
| using Matrix2d       = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>; | ||||
| using Matrix3f       = Eigen::Matrix<float,  3, 3, Eigen::DontAlign>; | ||||
| using Matrix3d       = Eigen::Matrix<double, 3, 3, Eigen::DontAlign>; | ||||
| using Matrix4f       = Eigen::Matrix<float,  4, 4, Eigen::DontAlign>; | ||||
| using Matrix4d       = Eigen::Matrix<double, 4, 4, Eigen::DontAlign>; | ||||
| 
 | ||||
| using Transform2f    = Eigen::Transform<float,  2, Eigen::Affine, Eigen::DontAlign>; | ||||
| using Transform2d    = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign>; | ||||
|  |  | |||
|  | @ -354,9 +354,19 @@ bool Print::has_brim() const | |||
|     return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_brim(); }); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons) | ||||
| #else | ||||
| static inline bool sequential_print_horizontal_clearance_valid(const Print &print) | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| { | ||||
| 	Polygons convex_hulls_other; | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     if (polygons != nullptr) | ||||
|         polygons->clear(); | ||||
|     std::vector<size_t> intersecting_idxs; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| 	std::map<ObjectID, Polygon> map_model_object_to_convex_hull; | ||||
| 	for (const PrintObject *print_object : print.objects()) { | ||||
| 	    assert(! print_object->model_object()->instances.empty()); | ||||
|  | @ -378,7 +388,7 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin | |||
|                     // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
 | ||||
|                     // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
 | ||||
|                     float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)), | ||||
|                     jtRound, float(scale_(0.1))).front()); | ||||
|                     jtRound, scale_(0.1)).front()); | ||||
| #else | ||||
| 	        it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,  | ||||
|                 offset(print_object->model_object()->convex_hull_2d( | ||||
|  | @ -391,21 +401,47 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin | |||
|         } | ||||
| 	    // Make a copy, so it may be rotated for instances.
 | ||||
| 	    Polygon convex_hull0 = it_convex_hull->second; | ||||
| 		double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation()); | ||||
| 		const double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation()); | ||||
| 		if (std::abs(z_diff) > EPSILON) | ||||
| 			convex_hull0.rotate(z_diff); | ||||
| 	    // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
 | ||||
| 	    for (const PrintInstance &instance : print_object->instances()) { | ||||
| 	        Polygon convex_hull = convex_hull0; | ||||
| 	        // instance.shift is a position of a centered object, while model object may not be centered.
 | ||||
| 	        // Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
 | ||||
| 	        // Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
 | ||||
| 	        convex_hull.translate(instance.shift - print_object->center_offset()); | ||||
| 	        if (! intersection(convex_hulls_other, (Polygons)convex_hull).empty()) | ||||
| 	            return false; | ||||
| 	        convex_hulls_other.emplace_back(std::move(convex_hull)); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             // if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
 | ||||
|             for (size_t i = 0; i < convex_hulls_other.size(); ++i) { | ||||
|                 if (!intersection((Polygons)convex_hulls_other[i], (Polygons)convex_hull).empty()) { | ||||
|                     if (polygons == nullptr) | ||||
|                         return false; | ||||
|                     else { | ||||
|                         intersecting_idxs.emplace_back(i); | ||||
|                         intersecting_idxs.emplace_back(convex_hulls_other.size()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| #else | ||||
|             if (!intersection(convex_hulls_other, (Polygons)convex_hull).empty()) | ||||
|                 return false; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|             convex_hulls_other.emplace_back(std::move(convex_hull)); | ||||
| 	    } | ||||
| 	} | ||||
| 	return true; | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     if (!intersecting_idxs.empty()) { | ||||
|         // use collected indices (inside convex_hulls_other) to update output
 | ||||
|         std::sort(intersecting_idxs.begin(), intersecting_idxs.end()); | ||||
|         intersecting_idxs.erase(std::unique(intersecting_idxs.begin(), intersecting_idxs.end()), intersecting_idxs.end()); | ||||
|         for (size_t i : intersecting_idxs) { | ||||
|             polygons->emplace_back(std::move(convex_hulls_other[i])); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static inline bool sequential_print_vertical_clearance_valid(const Print &print) | ||||
|  |  | |||
|  | @ -584,6 +584,10 @@ public: | |||
|     const PrintRegion&          get_print_region(size_t idx) const  { return *m_print_regions[idx]; } | ||||
|     const ToolOrdering&         get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| protected: | ||||
|     // Invalidates the step, and its depending steps in Print.
 | ||||
|     bool                invalidate_step(PrintStep step); | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include <functional> | ||||
| #include <optional> | ||||
| 
 | ||||
| #include <libslic3r/OpenVDBUtils.hpp> | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| #define SLASUPPORTTREEALGORITHM_H | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <optional> | ||||
| 
 | ||||
| #include <libslic3r/SLA/SupportTreeBuilder.hpp> | ||||
| #include <libslic3r/SLA/Clustering.hpp> | ||||
|  |  | |||
|  | @ -66,6 +66,8 @@ | |||
| // Enable to push object instances under the bed
 | ||||
| #define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0) | ||||
| #define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z) | ||||
| // Enable visualization of objects clearance for sequential prints
 | ||||
| #define ENABLE_SEQUENTIAL_LIMITS (1 && ENABLE_2_4_0_ALPHA0) | ||||
| 
 | ||||
| 
 | ||||
| #endif // _prusaslicer_technologies_h_
 | ||||
|  |  | |||
|  | @ -1020,6 +1020,33 @@ TriangleMesh make_cylinder(double r, double h, double fa) | |||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| TriangleMesh make_cone(double r, double h, double fa) | ||||
| { | ||||
|     Pointf3s vertices; | ||||
|     std::vector<Vec3i>	facets; | ||||
|     vertices.reserve(3+size_t(2*PI/fa)); | ||||
|     vertices.reserve(3+2*size_t(2*PI/fa)); | ||||
| 
 | ||||
|     vertices = { Vec3d::Zero(), Vec3d(0., 0., h) }; // base center and top vertex
 | ||||
|     size_t i = 0; | ||||
|     for (double angle=0; angle<2*PI; angle+=fa) { | ||||
|         vertices.emplace_back(r*std::cos(angle), r*std::sin(angle), 0.); | ||||
|         if (angle > 0.) { | ||||
|             facets.emplace_back(0, i+2, i+1); | ||||
|             facets.emplace_back(1, i+1, i+2); | ||||
|         } | ||||
|         ++i; | ||||
|     } | ||||
|     facets.emplace_back(0, 2, i+1); // close the shape
 | ||||
|     facets.emplace_back(1, i+1, 2); | ||||
| 
 | ||||
|     TriangleMesh mesh(std::move(vertices), std::move(facets)); | ||||
|     mesh.repair(); | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Generates mesh for a sphere centered about the origin, using the generated angle
 | ||||
| // to determine the granularity. 
 | ||||
| // Default angle is 1 degree.
 | ||||
|  |  | |||
|  | @ -125,10 +125,8 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f | |||
| Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transform3f &t, const float z); | ||||
| 
 | ||||
| TriangleMesh make_cube(double x, double y, double z); | ||||
| 
 | ||||
| // Generate a TriangleMesh of a cylinder
 | ||||
| TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); | ||||
| 
 | ||||
| TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)); | ||||
| TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -5953,16 +5953,18 @@ static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 | |||
| 
 | ||||
| static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) | ||||
| { | ||||
|     (void)pZip; | ||||
|     mz_bool is_zip64_needed = uncomp_size >= MZ_UINT32_MAX || comp_size >= MZ_UINT32_MAX; | ||||
|     memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, (pZip->m_pState->m_zip64) ? 0x002D : (method ? 0x0014 : 0x0000)); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000)); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || comp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || uncomp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, is_zip64_needed ? MZ_UINT32_MAX : comp_size); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, is_zip64_needed ? MZ_UINT32_MAX : uncomp_size); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); | ||||
|     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); | ||||
|     return MZ_TRUE; | ||||
|  | @ -5974,7 +5976,7 @@ static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_ | |||
|                                                        mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, | ||||
|                                                        mz_uint64 local_header_ofs, mz_uint32 ext_attributes) | ||||
| { | ||||
|     mz_zip_internal_state *pState = pZip->m_pState; | ||||
|     (void)pZip; | ||||
|     mz_bool is_zip64_needed = uncomp_size >= MZ_UINT32_MAX || comp_size >= MZ_UINT32_MAX; | ||||
|     memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); | ||||
|     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); | ||||
|  | @ -6793,13 +6795,7 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged | |||
|     } | ||||
| 
 | ||||
|     MZ_CLEAR_OBJ(local_dir_header); | ||||
|     if (pState->m_zip64) | ||||
|     { | ||||
|         pContext->pExtra_data = pContext->extra_data; | ||||
|         pContext->extra_size  = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size, | ||||
|                                                                      (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); | ||||
|     } | ||||
|     else if (!pState->m_zip64 && max_size > MZ_UINT32_MAX) | ||||
|     if (max_size > MZ_UINT32_MAX) | ||||
|     { | ||||
|         pContext->pExtra_data = pContext->extra_data; | ||||
|         pContext->extra_size  = mz_zip_writer_preallocate_extra_data(pContext->extra_data, 2 * sizeof(mz_uint64)); | ||||
|  |  | |||
|  | @ -242,7 +242,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) | |||
| 
 | ||||
| encoding_check(libslic3r_gui) | ||||
| 
 | ||||
| target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES}) | ||||
| target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES}) | ||||
| 
 | ||||
| if (CMAKE_SYSTEM_NAME STREQUAL "Linux") | ||||
|     target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES})  | ||||
|  |  | |||
|  | @ -98,12 +98,6 @@ const float Bed3D::Axes::DefaultStemLength = 25.0f; | |||
| const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius; | ||||
| const float Bed3D::Axes::DefaultTipLength = 5.0f; | ||||
| 
 | ||||
| void Bed3D::Axes::set_stem_length(float length) | ||||
| { | ||||
|     m_stem_length = length; | ||||
|     m_arrow.reset(); | ||||
| } | ||||
| 
 | ||||
| void Bed3D::Axes::render() const | ||||
| { | ||||
|     auto render_axis = [this](const Transform3f& transform) { | ||||
|  | @ -113,7 +107,10 @@ void Bed3D::Axes::render() const | |||
|         glsafe(::glPopMatrix()); | ||||
|     }; | ||||
| 
 | ||||
|     const_cast<GLModel*>(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     if (!m_arrow.is_initialized()) | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|         const_cast<GLModel*>(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); | ||||
| 
 | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader == nullptr) | ||||
|  | @ -122,20 +119,33 @@ void Bed3D::Axes::render() const | |||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     shader->start_using(); | ||||
|     shader->set_uniform("emission_factor", 0.0); | ||||
| 
 | ||||
|     // x axis
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     const_cast<GLModel*>(&m_arrow)->set_color(-1, { 0.75f, 0.0f, 0.0f, 1.0f }); | ||||
| #else | ||||
|     std::array<float, 4> color = { 0.75f, 0.0f, 0.0f, 1.0f }; | ||||
|     shader->set_uniform("uniform_color", color); | ||||
|     render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast<float>()); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }).cast<float>()); | ||||
| 
 | ||||
|     // y axis
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     const_cast<GLModel*>(&m_arrow)->set_color(-1, { 0.0f, 0.75f, 0.0f, 1.0f }); | ||||
| #else | ||||
|     color = { 0.0f, 0.75f, 0.0f, 1.0f }; | ||||
|     shader->set_uniform("uniform_color", color); | ||||
|     render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast<float>()); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }).cast<float>()); | ||||
| 
 | ||||
|     // z axis
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     const_cast<GLModel*>(&m_arrow)->set_color(-1, { 0.0f, 0.0f, 0.75f, 1.0f }); | ||||
| #else | ||||
|     color = { 0.0f, 0.0f, 0.75f, 1.0f }; | ||||
|     shader->set_uniform("uniform_color", color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     render_axis(Geometry::assemble_transform(m_origin).cast<float>()); | ||||
| 
 | ||||
|     shader->stop_using(); | ||||
|  | @ -415,7 +425,10 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const | |||
|             } | ||||
| 
 | ||||
|             glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
|             glsafe(::glDepthMask(GL_FALSE)); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             if (bottom) | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|                 glsafe(::glDepthMask(GL_FALSE)); | ||||
| 
 | ||||
|             glsafe(::glEnable(GL_BLEND)); | ||||
|             glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); | ||||
|  | @ -460,7 +473,10 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const | |||
|                 glsafe(::glFrontFace(GL_CCW)); | ||||
| 
 | ||||
|             glsafe(::glDisable(GL_BLEND)); | ||||
|             glsafe(::glDepthMask(GL_TRUE)); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             if (bottom) | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|                 glsafe(::glDepthMask(GL_TRUE)); | ||||
| 
 | ||||
|             shader->stop_using(); | ||||
|         } | ||||
|  | @ -475,6 +491,10 @@ void Bed3D::render_model() const | |||
|     GLModel* model = const_cast<GLModel*>(&m_model); | ||||
| 
 | ||||
|     if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         model->set_color(-1, m_model_color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|         // move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
 | ||||
|         Vec3d shift = m_bounding_box.center(); | ||||
|         shift(2) = -0.03; | ||||
|  | @ -488,9 +508,12 @@ void Bed3D::render_model() const | |||
|         GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|         if (shader != nullptr) { | ||||
|             shader->start_using(); | ||||
| #if !ENABLE_SEQUENTIAL_LIMITS | ||||
|             shader->set_uniform("uniform_color", m_model_color); | ||||
| #endif // !ENABLE_SEQUENTIAL_LIMITS
 | ||||
|             shader->set_uniform("emission_factor", 0.0); | ||||
|             ::glPushMatrix(); | ||||
|             ::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2)); | ||||
|             ::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()); | ||||
|             model->render(); | ||||
|             ::glPopMatrix(); | ||||
|             shader->stop_using(); | ||||
|  |  | |||
|  | @ -17,8 +17,8 @@ class GeometryBuffer | |||
| { | ||||
|     struct Vertex | ||||
|     { | ||||
|         Vec3f position = Vec3f::Zero(); | ||||
|         Vec2f tex_coords = Vec2f::Zero(); | ||||
|         Vec3f position{ Vec3f::Zero() }; | ||||
|         Vec2f tex_coords{ Vec2f::Zero() }; | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<Vertex> m_vertices; | ||||
|  | @ -53,7 +53,10 @@ class Bed3D | |||
|     public: | ||||
|         const Vec3d& get_origin() const { return m_origin; } | ||||
|         void set_origin(const Vec3d& origin) { m_origin = origin; } | ||||
|         void set_stem_length(float length); | ||||
|         void set_stem_length(float length) { | ||||
|             m_stem_length = length; | ||||
|             m_arrow.reset(); | ||||
|         } | ||||
|         float get_total_length() const { return m_stem_length + DefaultTipLength; } | ||||
|         void render() const; | ||||
|     }; | ||||
|  |  | |||
|  | @ -205,6 +205,9 @@ void GCodeViewer::SequentialRangeCap::reset() { | |||
| void GCodeViewer::SequentialView::Marker::init() | ||||
| { | ||||
|     m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     m_model.set_color(-1, { 1.0f, 1.0f, 1.0f, 0.5f }); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| } | ||||
| 
 | ||||
| void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) | ||||
|  | @ -226,7 +229,10 @@ void GCodeViewer::SequentialView::Marker::render() const | |||
|     glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); | ||||
| 
 | ||||
|     shader->start_using(); | ||||
| #if !ENABLE_SEQUENTIAL_LIMITS | ||||
|     shader->set_uniform("uniform_color", m_color); | ||||
| #endif // !ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     shader->set_uniform("emission_factor", 0.0); | ||||
| 
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glMultMatrixf(m_world_transform.data())); | ||||
|  |  | |||
|  | @ -505,7 +505,9 @@ public: | |||
|             Vec3f m_world_position; | ||||
|             Transform3f m_world_transform; | ||||
|             float m_z_offset{ 0.5f }; | ||||
| #if !ENABLE_SEQUENTIAL_LIMITS | ||||
|             std::array<float, 4> m_color{ 1.0f, 1.0f, 1.0f, 0.5f }; | ||||
| #endif // !ENABLE_SEQUENTIAL_LIMITS
 | ||||
|             bool m_visible{ true }; | ||||
| 
 | ||||
|         public: | ||||
|  | @ -514,7 +516,9 @@ public: | |||
|             const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } | ||||
| 
 | ||||
|             void set_world_position(const Vec3f& position); | ||||
| #if !ENABLE_SEQUENTIAL_LIMITS | ||||
|             void set_color(const std::array<float, 4>& color) { m_color = color; } | ||||
| #endif // !ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|             bool is_visible() const { return m_visible; } | ||||
|             void set_visible(bool visible) { m_visible = visible; } | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| #include "libslic3r/libslic3r.h" | ||||
| #include "GLCanvas3D.hpp" | ||||
| 
 | ||||
| #include <igl/unproject.h> | ||||
| 
 | ||||
| #include "libslic3r/ClipperUtils.hpp" | ||||
| #include "libslic3r/PrintConfig.hpp" | ||||
| #include "libslic3r/GCode/ThumbnailData.hpp" | ||||
|  | @ -787,6 +789,97 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas | |||
|     ImGui::PopStyleVar(2); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| void GLCanvas3D::SequentialPrintClearance::set(const Polygons& polygons, bool fill) | ||||
| { | ||||
|     m_render_fill = fill; | ||||
| 
 | ||||
|     m_perimeter.reset(); | ||||
|     m_fill.reset(); | ||||
|     if (polygons.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     size_t triangles_count = 0; | ||||
|     for (const Polygon& poly : polygons) { | ||||
|         triangles_count += poly.points.size() - 2; | ||||
|     } | ||||
|     size_t vertices_count = 3 * triangles_count; | ||||
| 
 | ||||
|     if (fill) { | ||||
|         GLModel::InitializationData fill_data; | ||||
|         GLModel::InitializationData::Entity entity; | ||||
|         entity.type = GLModel::PrimitiveType::Triangles; | ||||
|         entity.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; | ||||
|         entity.positions.reserve(vertices_count); | ||||
|         entity.normals.reserve(vertices_count); | ||||
|         entity.indices.reserve(vertices_count); | ||||
| 
 | ||||
|         ExPolygons polygons_union = union_ex(polygons); | ||||
|         for (const ExPolygon& poly : polygons_union) { | ||||
|             std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly, false); | ||||
|             for (const Vec3d& v : triangulation) { | ||||
|                 entity.positions.emplace_back(v.cast<float>() + Vec3f(0.0f, 0.0f, 0.0125f)); // add a small positive z to avoid z-fighting
 | ||||
|                 entity.normals.emplace_back(Vec3f::UnitZ()); | ||||
|                 size_t positions_count = entity.positions.size(); | ||||
|                 if (positions_count % 3 == 0) { | ||||
|                     entity.indices.emplace_back(positions_count - 3); | ||||
|                     entity.indices.emplace_back(positions_count - 2); | ||||
|                     entity.indices.emplace_back(positions_count - 1); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         fill_data.entities.emplace_back(entity); | ||||
|         m_fill.init_from(fill_data); | ||||
|     } | ||||
| 
 | ||||
|     GLModel::InitializationData perimeter_data; | ||||
|     for (const Polygon& poly : polygons) { | ||||
|         GLModel::InitializationData::Entity ent; | ||||
|         ent.type = GLModel::PrimitiveType::LineLoop; | ||||
|         ent.positions.reserve(poly.points.size()); | ||||
|         ent.indices.reserve(poly.points.size()); | ||||
|         unsigned int id_count = 0; | ||||
|         for (const Point& p : poly.points) { | ||||
|             ent.positions.emplace_back(unscale<float>(p.x()), unscale<float>(p.y()), 0.025f); // add a small positive z to avoid z-fighting
 | ||||
|             ent.normals.emplace_back(Vec3f::UnitZ()); | ||||
|             ent.indices.emplace_back(id_count++); | ||||
|         } | ||||
| 
 | ||||
|         perimeter_data.entities.emplace_back(ent); | ||||
|     } | ||||
| 
 | ||||
|     m_perimeter.init_from(perimeter_data); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::SequentialPrintClearance::render() const | ||||
| { | ||||
|     std::array<float, 4> FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; | ||||
|     std::array<float, 4> NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; | ||||
| 
 | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     shader->start_using(); | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
|     glsafe(::glDisable(GL_CULL_FACE)); | ||||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|     glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); | ||||
| 
 | ||||
|     const_cast<GLModel*>(&m_perimeter)->set_color(-1, m_render_fill ? FILL_COLOR : NO_FILL_COLOR); | ||||
|     m_perimeter.render(); | ||||
|     m_fill.render(); | ||||
| 
 | ||||
|     glsafe(::glDisable(GL_BLEND)); | ||||
|     glsafe(::glEnable(GL_CULL_FACE)); | ||||
|     glsafe(::glDisable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     shader->stop_using(); | ||||
| } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); | ||||
| wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); | ||||
|  | @ -1375,7 +1468,11 @@ void GLCanvas3D::render() | |||
|     _render_sla_slices(); | ||||
|     _render_selection(); | ||||
|     _render_bed(!camera.is_looking_downward(), true); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined && | ||||
|         !m_layers_editing.is_enabled()) | ||||
|         _render_sequential_clearance(); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
|     _render_selection_center(); | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
|  | @ -1616,6 +1713,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|      | ||||
|     _set_current(); | ||||
| 
 | ||||
|     m_hover_volume_idxs.clear(); | ||||
| 
 | ||||
|     struct ModelVolumeState { | ||||
|         ModelVolumeState(const GLVolume* volume) : | ||||
|             model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {} | ||||
|  | @ -1914,7 +2013,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|         bool wt = dynamic_cast<const ConfigOptionBool*>(m_config->option("wipe_tower"))->value; | ||||
|         bool co = dynamic_cast<const ConfigOptionBool*>(m_config->option("complete_objects"))->value; | ||||
| 
 | ||||
|         if ((extruders_count > 1) && wt && !co) { | ||||
|         if (extruders_count > 1 && wt && !co) { | ||||
|             // Height of a print (Show at least a slab)
 | ||||
|             double height = std::max(m_model->bounding_box().max(2), 10.0); | ||||
| 
 | ||||
|  | @ -2861,6 +2960,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
| 
 | ||||
|         m_mouse.set_start_position_3D_as_invalid(); | ||||
|         m_mouse.position = pos.cast<double>(); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         if (evt.Dragging() && current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { | ||||
|             switch (m_gizmos.get_current_type()) | ||||
|             { | ||||
|             case GLGizmosManager::EType::Move: | ||||
|             case GLGizmosManager::EType::Scale: | ||||
|             case GLGizmosManager::EType::Rotate: | ||||
|             { | ||||
|                 update_sequential_clearance(); | ||||
|                 break; | ||||
|             } | ||||
|             default: { break; } | ||||
|             } | ||||
|         } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -2983,6 +3099,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|                         m_mouse.drag.move_volume_idx = volume_idx; | ||||
|                         m_selection.start_dragging(); | ||||
|                         m_mouse.drag.start_position_3D = m_mouse.scene_position; | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|                         m_sequential_print_clearance_first_displacement = true; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|                         m_moving = true; | ||||
|                     } | ||||
|                 } | ||||
|  | @ -3028,6 +3147,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|             } | ||||
| 
 | ||||
|             m_selection.translate(cur_pos - m_mouse.drag.start_position_3D); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) | ||||
|                 update_sequential_clearance(); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|             wxGetApp().obj_manipul()->set_dirty(); | ||||
|             m_dirty = true; | ||||
|         } | ||||
|  | @ -3060,7 +3183,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) | |||
|                     // See GH issue #3816.
 | ||||
|                     Camera& camera = wxGetApp().plater()->get_camera(); | ||||
|                     camera.recover_from_free_camera(); | ||||
|                     camera.rotate_on_sphere(rot.x(), rot.y(), wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); | ||||
|                     camera.rotate_on_sphere(rot.x(), rot.y(), current_printer_technology() != ptSLA); | ||||
|                 } | ||||
| 
 | ||||
|                 m_dirty = true; | ||||
|  | @ -3277,15 +3400,15 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) | |||
|     for (const std::pair<int, int>& i : done) { | ||||
|         ModelObject* m = m_model->objects[i.first]; | ||||
| #if ENABLE_ALLOW_NEGATIVE_Z | ||||
|         double shift_z = m->get_instance_min_z(i.second); | ||||
|         const double shift_z = m->get_instance_min_z(i.second); | ||||
| #if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA | ||||
|         if (current_printer_technology() == ptSLA || shift_z > 0.0) { | ||||
| #else | ||||
|         if (shift_z > 0.0) { | ||||
| #endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
 | ||||
|             Vec3d shift(0.0, 0.0, -shift_z); | ||||
|             const Vec3d shift(0.0, 0.0, -shift_z); | ||||
| #else | ||||
|         Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); | ||||
|         const Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); | ||||
| #endif // ENABLE_ALLOW_NEGATIVE_Z
 | ||||
|         m_selection.translate(i.first, i.second, shift); | ||||
|         m->translate_instance(i.second, shift); | ||||
|  | @ -3307,6 +3430,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) | |||
|     if (wipe_tower_origin != Vec3d::Zero()) | ||||
|         post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     set_sequential_print_clearance(Polygons(), false); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
|  | @ -3653,6 +3780,93 @@ void GLCanvas3D::mouse_up_cleanup() | |||
|         m_canvas->ReleaseMouse(); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| void GLCanvas3D::update_sequential_clearance() | ||||
| { | ||||
|     if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) | ||||
|         return; | ||||
| 
 | ||||
|     // collects instance transformations from volumes
 | ||||
|     // first define temporary cache
 | ||||
|     unsigned int instances_count = 0; | ||||
|     std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms; | ||||
|     for (size_t obj = 0; obj < m_model->objects.size(); ++obj) { | ||||
|         instance_transforms.emplace_back(std::vector<std::optional<Geometry::Transformation>>()); | ||||
|         const ModelObject* model_object = m_model->objects[obj]; | ||||
|         for (size_t i = 0; i < model_object->instances.size(); ++i) { | ||||
|             instance_transforms[obj].emplace_back(std::optional<Geometry::Transformation>()); | ||||
|             ++instances_count; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (instances_count == 1) | ||||
|         return; | ||||
| 
 | ||||
|     // second fill temporary cache with data from volumes
 | ||||
|     for (const GLVolume* v : m_volumes.volumes) { | ||||
|         if (v->is_modifier || v->is_wipe_tower) | ||||
|             continue; | ||||
| 
 | ||||
|         auto& transform = instance_transforms[v->object_idx()][v->instance_idx()]; | ||||
|         if (!transform.has_value()) | ||||
|             transform = v->get_instance_transformation(); | ||||
|     } | ||||
| 
 | ||||
|     // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
 | ||||
|     // this is done only the first time this method is called while moving the mouse,
 | ||||
|     // the results are then cached for following displacements
 | ||||
|     if (m_sequential_print_clearance_first_displacement) { | ||||
|         m_sequential_print_clearance.m_hull_2d_cache.clear(); | ||||
|         float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); | ||||
|         double mitter_limit = scale_(0.1); | ||||
|         m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); | ||||
|         for (size_t i = 0; i < m_model->objects.size(); ++i) { | ||||
|             ModelObject* model_object = m_model->objects[i]; | ||||
|             ModelInstance* model_instance0 = model_object->instances.front(); | ||||
|             Polygon hull_2d = offset(model_object->convex_hull_2d(Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), | ||||
|                 model_instance0->get_scaling_factor(), model_instance0->get_mirror())), | ||||
|                 // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
 | ||||
|                 // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
 | ||||
|                 shrink_factor, | ||||
|                 jtRound, mitter_limit).front(); | ||||
| 
 | ||||
|             Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); | ||||
|             cache_hull_2d.reserve(hull_2d.points.size()); | ||||
|             for (const Point& p : hull_2d.points) { | ||||
|                 cache_hull_2d.emplace_back(unscale<double>(p.x()), unscale<double>(p.y()), 0.0); | ||||
|             } | ||||
|         } | ||||
|         m_sequential_print_clearance_first_displacement = false; | ||||
|     } | ||||
| 
 | ||||
|     // calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
 | ||||
|     Polygons polygons; | ||||
|     polygons.reserve(instances_count); | ||||
|     for (size_t i = 0; i < instance_transforms.size(); ++i) { | ||||
|         const auto& instances = instance_transforms[i]; | ||||
|         double rotation_z0 = instances.front()->get_rotation().z(); | ||||
|         for (const auto& instance : instances) { | ||||
|             Geometry::Transformation transformation; | ||||
|             const Vec3d& offset = instance->get_offset(); | ||||
|             transformation.set_offset({ offset.x(), offset.y(), 0.0 }); | ||||
|             transformation.set_rotation(Z, instance->get_rotation().z() - rotation_z0); | ||||
|             const Transform3d& trafo = transformation.get_matrix(); | ||||
|             const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i]; | ||||
|             Points inst_pts; | ||||
|             inst_pts.reserve(hull_2d.size()); | ||||
|             for (size_t j = 0; j < hull_2d.size(); ++j) { | ||||
|                 const Vec3d p = trafo * hull_2d[j]; | ||||
|                 inst_pts.emplace_back(scaled<double>(p.x()), scaled<double>(p.y())); | ||||
|             } | ||||
|             polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // sends instances 2d hulls to be rendered
 | ||||
|     set_sequential_print_clearance(polygons, false); | ||||
| } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| bool GLCanvas3D::_is_shown_on_screen() const | ||||
| { | ||||
|     return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; | ||||
|  | @ -3916,7 +4130,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool | |||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     shader->start_using(); | ||||
|     shader->set_uniform("print_box.volume_detection", 0); | ||||
|     shader->set_uniform("emission_factor", 0.0); | ||||
| 
 | ||||
|     for (GLVolume* vol : visible_volumes) { | ||||
|         shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); | ||||
|  | @ -4754,7 +4968,7 @@ void GLCanvas3D::_render_background() const | |||
|     bool use_error_color = false; | ||||
|     if (wxGetApp().is_editor()) { | ||||
|         use_error_color = m_dynamic_background_enabled && | ||||
|             (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA || !m_volumes.empty()); | ||||
|         (current_printer_technology() != ptSLA || !m_volumes.empty()); | ||||
| 
 | ||||
|         if (!m_volumes.empty()) | ||||
|             use_error_color &= _is_any_volume_outside(); | ||||
|  | @ -4902,6 +5116,13 @@ void GLCanvas3D::_render_selection() const | |||
|         m_selection.render(scale_factor); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| void GLCanvas3D::_render_sequential_clearance() const | ||||
| { | ||||
|     m_sequential_print_clearance.render(); | ||||
| } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
| void GLCanvas3D::_render_selection_center() const | ||||
| { | ||||
|  | @ -5167,7 +5388,7 @@ void GLCanvas3D::_render_camera_target() const | |||
| 
 | ||||
| void GLCanvas3D::_render_sla_slices() const | ||||
| { | ||||
|     if (!m_use_clipping_planes || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) | ||||
|     if (!m_use_clipping_planes || current_printer_technology() != ptSLA) | ||||
|         return; | ||||
| 
 | ||||
|     const SLAPrint* print = this->sla_print(); | ||||
|  | @ -5393,9 +5614,9 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) | |||
|         return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); | ||||
| 
 | ||||
|     const Camera& camera = wxGetApp().plater()->get_camera(); | ||||
|     const std::array<int, 4>& viewport = camera.get_viewport(); | ||||
|     const Transform3d& modelview_matrix = camera.get_view_matrix(); | ||||
|     const Transform3d& projection_matrix = camera.get_projection_matrix(); | ||||
|     Matrix4d modelview = camera.get_view_matrix().matrix(); | ||||
|     Matrix4d projection= camera.get_projection_matrix().matrix(); | ||||
|     Vec4i viewport(camera.get_viewport().data()); | ||||
| 
 | ||||
|     GLint y = viewport[3] - (GLint)mouse_pos(1); | ||||
|     GLfloat mouse_z; | ||||
|  | @ -5404,9 +5625,9 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) | |||
|     else | ||||
|         mouse_z = *z; | ||||
| 
 | ||||
|     GLdouble out_x, out_y, out_z; | ||||
|     ::gluUnProject((GLdouble)mouse_pos(0), (GLdouble)y, (GLdouble)mouse_z, (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z); | ||||
|     return Vec3d((double)out_x, (double)out_y, (double)out_z); | ||||
|     Vec3d out; | ||||
|     igl::unproject(Vec3d(mouse_pos(0), y, mouse_z), modelview, projection, viewport, out); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) | ||||
|  |  | |||
|  | @ -496,6 +496,26 @@ private: | |||
| 
 | ||||
|     void load_arrange_settings(); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     class SequentialPrintClearance | ||||
|     { | ||||
|         GLModel m_fill; | ||||
|         GLModel m_perimeter; | ||||
|         bool m_render_fill{ true }; | ||||
| 
 | ||||
|         std::vector<Pointf3s> m_hull_2d_cache; | ||||
| 
 | ||||
|     public: | ||||
|         void set(const Polygons& polygons, bool fill); | ||||
|         void render() const; | ||||
| 
 | ||||
|         friend class GLCanvas3D; | ||||
|     }; | ||||
| 
 | ||||
|     SequentialPrintClearance m_sequential_print_clearance; | ||||
|     bool m_sequential_print_clearance_first_displacement{ true }; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| public: | ||||
|     explicit GLCanvas3D(wxGLCanvas* canvas); | ||||
|     ~GLCanvas3D(); | ||||
|  | @ -737,6 +757,14 @@ public: | |||
| #endif | ||||
|     } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     void set_sequential_print_clearance(const Polygons& polygons, bool fill) { m_sequential_print_clearance.set(polygons, fill); } | ||||
|     void update_sequential_clearance(); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     const Print* fff_print() const; | ||||
|     const SLAPrint* sla_print() const; | ||||
| 
 | ||||
| #if ENABLE_SCROLLABLE_LEGEND | ||||
|     void reset_old_size() { m_old_size = { 0, 0 }; } | ||||
| #endif // ENABLE_SCROLLABLE_LEGEND
 | ||||
|  | @ -767,6 +795,9 @@ private: | |||
|     void _render_objects() const; | ||||
|     void _render_gcode() const; | ||||
|     void _render_selection() const; | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     void _render_sequential_clearance() const; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
|     void _render_selection_center() const; | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
|  | @ -843,10 +874,6 @@ private: | |||
|     float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } | ||||
| 
 | ||||
|     static std::vector<float> _parse_colors(const std::vector<std::string>& colors); | ||||
| 
 | ||||
| public: | ||||
|     const Print* fff_print() const; | ||||
|     const SLAPrint* sla_print() const; | ||||
| }; | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
|  |  | |||
|  | @ -2,6 +2,11 @@ | |||
| #include "GLModel.hpp" | ||||
| 
 | ||||
| #include "3DScene.hpp" | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| #include "GUI_App.hpp" | ||||
| #include "GLShader.hpp" | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| #include "libslic3r/TriangleMesh.hpp" | ||||
| #include "libslic3r/Model.hpp" | ||||
| 
 | ||||
|  | @ -13,6 +18,45 @@ | |||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| void GLModel::init_from(const InitializationData& data) | ||||
| { | ||||
|     if (!m_render_data.empty()) // call reset() if you want to reuse this model
 | ||||
|         return; | ||||
| 
 | ||||
|     for (const InitializationData::Entity& entity : data.entities) { | ||||
|         if (entity.positions.empty() || entity.indices.empty()) | ||||
|             continue; | ||||
| 
 | ||||
|         assert(entity.normals.empty() || entity.normals.size() == entity.positions.size()); | ||||
| 
 | ||||
|         RenderData rdata; | ||||
|         rdata.type = entity.type; | ||||
|         rdata.color = entity.color; | ||||
| 
 | ||||
|         // vertices/normals data
 | ||||
|         std::vector<float> vertices(6 * entity.positions.size()); | ||||
|         for (size_t i = 0; i < entity.positions.size(); ++i) { | ||||
|             const size_t offset = i * 6; | ||||
|             ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(entity.positions[i].data()), 3 * sizeof(float)); | ||||
|             if (!entity.normals.empty()) | ||||
|                 ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(entity.normals[i].data()), 3 * sizeof(float)); | ||||
|         } | ||||
| 
 | ||||
|         // indices data
 | ||||
|         std::vector<unsigned int> indices = entity.indices; | ||||
| 
 | ||||
|         rdata.indices_count = static_cast<unsigned int>(indices.size()); | ||||
| 
 | ||||
|         // update bounding box
 | ||||
|         for (size_t i = 0; i < entity.positions.size(); ++i) { | ||||
|             m_bounding_box.merge(entity.positions[i].cast<double>()); | ||||
|         } | ||||
| 
 | ||||
|         send_to_gpu(rdata, vertices, indices); | ||||
|         m_render_data.emplace_back(rdata); | ||||
|     } | ||||
| #else | ||||
| void GLModel::init_from(const GLModelInitializationData& data) | ||||
| { | ||||
|     assert(!data.positions.empty() && !data.triangles.empty()); | ||||
|  | @ -44,10 +88,41 @@ void GLModel::init_from(const GLModelInitializationData& data) | |||
|     } | ||||
| 
 | ||||
|     send_to_gpu(vertices, indices); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| } | ||||
| 
 | ||||
| void GLModel::init_from(const TriangleMesh& mesh) | ||||
| { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     if (!m_render_data.empty()) // call reset() if you want to reuse this model
 | ||||
|         return; | ||||
| 
 | ||||
|     RenderData data; | ||||
|     data.type = PrimitiveType::Triangles; | ||||
| 
 | ||||
|     std::vector<float> vertices = std::vector<float>(18 * mesh.stl.stats.number_of_facets); | ||||
|     std::vector<unsigned int> indices = std::vector<unsigned int>(3 * mesh.stl.stats.number_of_facets); | ||||
| 
 | ||||
|     unsigned int vertices_count = 0; | ||||
|     for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) { | ||||
|         const stl_facet& facet = mesh.stl.facet_start[i]; | ||||
|         for (size_t j = 0; j < 3; ++j) { | ||||
|             size_t offset = i * 18 + j * 6; | ||||
|             ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(facet.vertex[j].data()), 3 * sizeof(float)); | ||||
|             ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(facet.normal.data()), 3 * sizeof(float)); | ||||
|         } | ||||
|         for (size_t j = 0; j < 3; ++j) { | ||||
|             indices[i * 3 + j] = vertices_count + j; | ||||
|         } | ||||
|         vertices_count += 3; | ||||
|     } | ||||
| 
 | ||||
|     data.indices_count = static_cast<unsigned int>(indices.size()); | ||||
|     m_bounding_box = mesh.bounding_box(); | ||||
| 
 | ||||
|     send_to_gpu(data, vertices, indices); | ||||
|     m_render_data.emplace_back(data); | ||||
| #else | ||||
|     if (m_vbo_id > 0) // call reset() if you want to reuse this model
 | ||||
|         return; | ||||
| 
 | ||||
|  | @ -72,6 +147,7 @@ void GLModel::init_from(const TriangleMesh& mesh) | |||
|     m_bounding_box = mesh.bounding_box(); | ||||
| 
 | ||||
|     send_to_gpu(vertices, indices); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| } | ||||
| 
 | ||||
| bool GLModel::init_from_file(const std::string& filename) | ||||
|  | @ -99,8 +175,29 @@ bool GLModel::init_from_file(const std::string& filename) | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| void GLModel::set_color(int entity_id, const std::array<float, 4>& color) | ||||
| { | ||||
|     for (size_t i = 0; i < m_render_data.size(); ++i) { | ||||
|         if (entity_id == -1 || static_cast<int>(i) == entity_id) | ||||
|             m_render_data[i].color = color; | ||||
|     } | ||||
| } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| void GLModel::reset() | ||||
| { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     for (RenderData& data : m_render_data) { | ||||
|         // release gpu memory
 | ||||
|         if (data.ibo_id > 0) | ||||
|             glsafe(::glDeleteBuffers(1, &data.ibo_id)); | ||||
|         if (data.vbo_id > 0) | ||||
|             glsafe(::glDeleteBuffers(1, &data.vbo_id)); | ||||
|     } | ||||
| 
 | ||||
|     m_render_data.clear(); | ||||
| #else | ||||
|     // release gpu memory
 | ||||
|     if (m_ibo_id > 0) { | ||||
|         glsafe(::glDeleteBuffers(1, &m_ibo_id)); | ||||
|  | @ -113,12 +210,49 @@ void GLModel::reset() | |||
|     } | ||||
| 
 | ||||
|     m_indices_count = 0; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     m_bounding_box = BoundingBoxf3(); | ||||
|     m_filename = std::string(); | ||||
| } | ||||
| 
 | ||||
| void GLModel::render() const | ||||
| { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     for (const RenderData& data : m_render_data) { | ||||
|         if (data.vbo_id == 0 || data.ibo_id == 0) | ||||
|             continue; | ||||
| 
 | ||||
|         GLenum mode; | ||||
|         switch (data.type) | ||||
|         { | ||||
|         default: | ||||
|         case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } | ||||
|         case PrimitiveType::Lines:     { mode = GL_LINES; break; } | ||||
|         case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } | ||||
|         case PrimitiveType::LineLoop:  { mode = GL_LINE_LOOP; break; } | ||||
|         } | ||||
| 
 | ||||
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); | ||||
|         glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); | ||||
|         glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); | ||||
| 
 | ||||
|         glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); | ||||
|         glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); | ||||
| 
 | ||||
|         GLShaderProgram* shader = wxGetApp().get_current_shader(); | ||||
|         if (shader != nullptr) | ||||
|             shader->set_uniform("uniform_color", data.color); | ||||
| 
 | ||||
|         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); | ||||
|         glsafe(::glDrawElements(mode, static_cast<GLsizei>(data.indices_count), GL_UNSIGNED_INT, (const void*)0)); | ||||
|         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); | ||||
| 
 | ||||
|         glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); | ||||
|         glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); | ||||
| 
 | ||||
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); | ||||
|     } | ||||
| #else | ||||
|     if (m_vbo_id == 0 || m_ibo_id == 0) | ||||
|         return; | ||||
| 
 | ||||
|  | @ -137,8 +271,28 @@ void GLModel::render() const | |||
|     glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); | ||||
| 
 | ||||
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| void GLModel::send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices) | ||||
| { | ||||
|     assert(data.vbo_id == 0); | ||||
|     assert(data.ibo_id == 0); | ||||
| 
 | ||||
|     // vertex data -> send to gpu
 | ||||
|     glsafe(::glGenBuffers(1, &data.vbo_id)); | ||||
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); | ||||
|     glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); | ||||
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); | ||||
| 
 | ||||
|     // indices data -> send to gpu
 | ||||
|     glsafe(::glGenBuffers(1, &data.ibo_id)); | ||||
|     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); | ||||
|     glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); | ||||
|     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); | ||||
| } | ||||
| #else | ||||
| void GLModel::send_to_gpu(const std::vector<float>& vertices, const std::vector<unsigned int>& indices) | ||||
| { | ||||
|     // vertex data -> send to gpu
 | ||||
|  | @ -153,60 +307,138 @@ void GLModel::send_to_gpu(const std::vector<float>& vertices, const std::vector< | |||
|     glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); | ||||
|     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); | ||||
| } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) | ||||
| { | ||||
|     auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { | ||||
|         entity.positions.emplace_back(position); | ||||
|         entity.normals.emplace_back(normal); | ||||
|     }; | ||||
|     auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { | ||||
|         entity.indices.emplace_back(v1); | ||||
|         entity.indices.emplace_back(v2); | ||||
|         entity.indices.emplace_back(v3); | ||||
|     }; | ||||
| #else | ||||
| GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) | ||||
| { | ||||
|     auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { | ||||
|         data.positions.emplace_back(position); | ||||
|         data.normals.emplace_back(normal); | ||||
|     }; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     resolution = std::max(4, resolution); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     GLModel::InitializationData data; | ||||
|     GLModel::InitializationData::Entity entity; | ||||
|     entity.type = GLModel::PrimitiveType::Triangles; | ||||
| #else | ||||
|     GLModelInitializationData data; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     const float angle_step = 2.0f * M_PI / static_cast<float>(resolution); | ||||
|     std::vector<float> cosines(resolution); | ||||
|     std::vector<float> sines(resolution); | ||||
| 
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|         float angle = angle_step * static_cast<float>(i); | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const float angle = angle_step * static_cast<float>(i); | ||||
|         cosines[i] = ::cos(angle); | ||||
|         sines[i] = -::sin(angle); | ||||
|     } | ||||
| 
 | ||||
|     const float total_height = tip_height + stem_height; | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     // tip vertices/normals
 | ||||
|     append_vertex(entity, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     // tip triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const int v3 = (i < resolution - 1) ? i + 2 : 1; | ||||
|         append_indices(entity, 0, i + 1, v3); | ||||
|     } | ||||
| 
 | ||||
|     // tip cap outer perimeter vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // tip cap inner perimeter vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // tip cap triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; | ||||
|         const int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; | ||||
|         append_indices(entity, i + resolution + 1, v3, v2); | ||||
|         append_indices(entity, i + resolution + 1, i + 2 * resolution + 1, v3); | ||||
|     } | ||||
| 
 | ||||
|     // stem bottom vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     // stem top vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     // stem triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; | ||||
|         const int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; | ||||
|         append_indices(entity, i + 3 * resolution + 1, v3, v2); | ||||
|         append_indices(entity, i + 3 * resolution + 1, i + 4 * resolution + 1, v3); | ||||
|     } | ||||
| 
 | ||||
|     // stem cap vertices
 | ||||
|     append_vertex(entity, Vec3f::Zero(), -Vec3f::UnitZ()); | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // stem cap triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; | ||||
|         append_indices(entity, 5 * resolution + 1, v3, i + 5 * resolution + 2); | ||||
|     } | ||||
| 
 | ||||
|     data.entities.emplace_back(entity); | ||||
| #else | ||||
|     // tip vertices/normals
 | ||||
|     append_vertex(data, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     // tip triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         int v3 = (i < resolution - 1) ? i + 2 : 1; | ||||
|         data.triangles.emplace_back(0, i + 1, v3); | ||||
|     } | ||||
| 
 | ||||
|     // tip cap outer perimeter vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // tip cap inner perimeter vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // tip cap triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; | ||||
|         int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; | ||||
|         data.triangles.emplace_back(i + resolution + 1, v3, v2); | ||||
|  | @ -214,20 +446,17 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float | |||
|     } | ||||
| 
 | ||||
|     // stem bottom vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     // stem top vertices
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     // stem triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; | ||||
|         int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; | ||||
|         data.triangles.emplace_back(i + 3 * resolution + 1, v3, v2); | ||||
|  | @ -236,31 +465,50 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float | |||
| 
 | ||||
|     // stem cap vertices
 | ||||
|     append_vertex(data, Vec3f::Zero(), -Vec3f::UnitZ()); | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // stem cap triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; | ||||
|         data.triangles.emplace_back(5 * resolution + 1, v3, i + 5 * resolution + 2); | ||||
|     } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) | ||||
| { | ||||
|     auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { | ||||
|         entity.positions.emplace_back(position); | ||||
|         entity.normals.emplace_back(normal); | ||||
|     }; | ||||
|     auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { | ||||
|         entity.indices.emplace_back(v1); | ||||
|         entity.indices.emplace_back(v2); | ||||
|         entity.indices.emplace_back(v3); | ||||
|     }; | ||||
| #else | ||||
| GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) | ||||
| { | ||||
|     auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { | ||||
|         data.positions.emplace_back(position); | ||||
|         data.normals.emplace_back(normal); | ||||
|     }; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     resolution = std::max(2, resolution); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     GLModel::InitializationData data; | ||||
|     GLModel::InitializationData::Entity entity; | ||||
|     entity.type = GLModel::PrimitiveType::Triangles; | ||||
| #else | ||||
|     GLModelInitializationData data; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     const float half_thickness = 0.5f * thickness; | ||||
|     const float half_stem_width = 0.5f * stem_width; | ||||
|  | @ -270,6 +518,153 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip | |||
|     const float inner_radius = radius - half_stem_width; | ||||
|     const float step_angle = 0.5f * PI / static_cast<float>(resolution); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     // tip
 | ||||
|     // top face vertices
 | ||||
|     append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -tip_height, radius, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ()); | ||||
| 
 | ||||
|     // top face triangles
 | ||||
|     append_indices(entity, 0, 1, 2); | ||||
|     append_indices(entity, 0, 2, 4); | ||||
|     append_indices(entity, 4, 2, 3); | ||||
| 
 | ||||
|     // bottom face vertices
 | ||||
|     append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ()); | ||||
| 
 | ||||
|     // bottom face triangles
 | ||||
|     append_indices(entity, 5, 7, 6); | ||||
|     append_indices(entity, 5, 9, 7); | ||||
|     append_indices(entity, 9, 8, 7); | ||||
| 
 | ||||
|     // side faces vertices
 | ||||
|     append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX()); | ||||
| 
 | ||||
|     Vec3f normal(-half_tip_width, tip_height, 0.0f); | ||||
|     normal.normalize(); | ||||
|     append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, normal); | ||||
|     append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); | ||||
|     append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, normal); | ||||
|     append_vertex(entity, { -tip_height, radius, half_thickness }, normal); | ||||
| 
 | ||||
|     normal = Vec3f(-half_tip_width, -tip_height, 0.0f); | ||||
|     normal.normalize(); | ||||
|     append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); | ||||
|     append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, normal); | ||||
|     append_vertex(entity, { -tip_height, radius, half_thickness }, normal); | ||||
|     append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, normal); | ||||
| 
 | ||||
|     append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); | ||||
| 
 | ||||
|     // side face triangles
 | ||||
|     for (int i = 0; i < 4; ++i) { | ||||
|         const int ii = i * 4; | ||||
|         append_indices(entity, 10 + ii, 11 + ii, 13 + ii); | ||||
|         append_indices(entity, 10 + ii, 13 + ii, 12 + ii); | ||||
|     } | ||||
| 
 | ||||
|     // stem
 | ||||
|     // top face vertices
 | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // top face triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_indices(entity, 26 + i, 27 + i, 27 + resolution + i); | ||||
|         append_indices(entity, 27 + i, 28 + resolution + i, 27 + resolution + i); | ||||
|     } | ||||
| 
 | ||||
|     // bottom face vertices
 | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // bottom face triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         append_indices(entity, 28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); | ||||
|         append_indices(entity, 29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); | ||||
|     } | ||||
| 
 | ||||
|     // side faces vertices and triangles
 | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         const float c = ::cos(angle); | ||||
|         const float s = ::sin(angle); | ||||
|         append_vertex(entity, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         const float c = ::cos(angle); | ||||
|         const float s = ::sin(angle); | ||||
|         append_vertex(entity, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     int first_id = 26 + 4 * (resolution + 1); | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const int ii = first_id + i; | ||||
|         append_indices(entity, ii, ii + 1, ii + resolution + 2); | ||||
|         append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); | ||||
|     } | ||||
| 
 | ||||
|     append_vertex(entity, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); | ||||
| 
 | ||||
|     first_id = 26 + 6 * (resolution + 1); | ||||
|     append_indices(entity, first_id, first_id + 1, first_id + 3); | ||||
|     append_indices(entity, first_id, first_id + 3, first_id + 2); | ||||
| 
 | ||||
|     for (int i = resolution; i >= 0; --i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         const float c = ::cos(angle); | ||||
|         const float s = ::sin(angle); | ||||
|         append_vertex(entity, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = resolution; i >= 0; --i) { | ||||
|         const float angle = static_cast<float>(i) * step_angle; | ||||
|         const float c = ::cos(angle); | ||||
|         const float s = ::sin(angle); | ||||
|         append_vertex(entity, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     first_id = 30 + 6 * (resolution + 1); | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         const int ii = first_id + i; | ||||
|         append_indices(entity, ii, ii + 1, ii + resolution + 2); | ||||
|         append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); | ||||
|     } | ||||
| 
 | ||||
|     data.entities.emplace_back(entity); | ||||
| #else | ||||
|     // tip
 | ||||
|     // top face vertices
 | ||||
|     append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); | ||||
|  | @ -321,8 +716,7 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip | |||
|     append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); | ||||
| 
 | ||||
|     // side face triangles
 | ||||
|     for (int i = 0; i < 4; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < 4; ++i) { | ||||
|         int ii = i * 4; | ||||
|         data.triangles.emplace_back(10 + ii, 11 + ii, 13 + ii); | ||||
|         data.triangles.emplace_back(10 + ii, 13 + ii, 12 + ii); | ||||
|  | @ -330,56 +724,48 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip | |||
| 
 | ||||
|     // stem
 | ||||
|     // top face vertices
 | ||||
|     for (int i = 0; i <= resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i <= resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // top face triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         data.triangles.emplace_back(26 + i, 27 + i, 27 + resolution + i); | ||||
|         data.triangles.emplace_back(27 + i, 28 + resolution + i, 27 + resolution + i); | ||||
|     } | ||||
| 
 | ||||
|     // bottom face vertices
 | ||||
|     for (int i = 0; i <= resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i <= resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); | ||||
|     } | ||||
| 
 | ||||
|     // bottom face triangles
 | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         data.triangles.emplace_back(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); | ||||
|         data.triangles.emplace_back(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); | ||||
|     } | ||||
| 
 | ||||
|     // side faces vertices and triangles
 | ||||
|     for (int i = 0; i <= resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         float c = ::cos(angle); | ||||
|         float s = ::sin(angle); | ||||
|         append_vertex(data, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i <= resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i <= resolution; ++i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         float c = ::cos(angle); | ||||
|         float s = ::sin(angle); | ||||
|  | @ -387,8 +773,7 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip | |||
|     } | ||||
| 
 | ||||
|     int first_id = 26 + 4 * (resolution + 1); | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         int ii = first_id + i; | ||||
|         data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); | ||||
|         data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); | ||||
|  | @ -403,16 +788,14 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip | |||
|     data.triangles.emplace_back(first_id, first_id + 1, first_id + 3); | ||||
|     data.triangles.emplace_back(first_id, first_id + 3, first_id + 2); | ||||
| 
 | ||||
|     for (int i = resolution; i >= 0; --i) | ||||
|     { | ||||
|     for (int i = resolution; i >= 0; --i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         float c = ::cos(angle); | ||||
|         float s = ::sin(angle); | ||||
|         append_vertex(data, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); | ||||
|     } | ||||
| 
 | ||||
|     for (int i = resolution; i >= 0; --i) | ||||
|     { | ||||
|     for (int i = resolution; i >= 0; --i) { | ||||
|         float angle = static_cast<float>(i) * step_angle; | ||||
|         float c = ::cos(angle); | ||||
|         float s = ::sin(angle); | ||||
|  | @ -420,16 +803,33 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip | |||
|     } | ||||
| 
 | ||||
|     first_id = 30 + 6 * (resolution + 1); | ||||
|     for (int i = 0; i < resolution; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < resolution; ++i) { | ||||
|         int ii = first_id + i; | ||||
|         data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); | ||||
|         data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); | ||||
|     } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) | ||||
| { | ||||
|     auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { | ||||
|         entity.positions.emplace_back(position); | ||||
|         entity.normals.emplace_back(normal); | ||||
|     }; | ||||
|     auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { | ||||
|         entity.indices.emplace_back(v1); | ||||
|         entity.indices.emplace_back(v2); | ||||
|         entity.indices.emplace_back(v3); | ||||
|     }; | ||||
| 
 | ||||
|     GLModel::InitializationData data; | ||||
|     GLModel::InitializationData::Entity entity; | ||||
|     entity.type = GLModel::PrimitiveType::Triangles; | ||||
| #else | ||||
| GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) | ||||
| { | ||||
|     auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { | ||||
|  | @ -438,6 +838,7 @@ GLModelInitializationData straight_arrow(float tip_width, float tip_height, floa | |||
|     }; | ||||
| 
 | ||||
|     GLModelInitializationData data; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     const float half_thickness = 0.5f * thickness; | ||||
|     const float half_stem_width = 0.5f * stem_width; | ||||
|  | @ -445,6 +846,87 @@ GLModelInitializationData straight_arrow(float tip_width, float tip_height, floa | |||
|     const float total_height = tip_height + stem_height; | ||||
| 
 | ||||
|     // top face vertices
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0, total_height, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); | ||||
| 
 | ||||
|     // top face triangles
 | ||||
|     append_indices(entity, 0, 1, 6); | ||||
|     append_indices(entity, 6, 1, 5); | ||||
|     append_indices(entity, 4, 5, 3); | ||||
|     append_indices(entity, 5, 1, 3); | ||||
|     append_indices(entity, 1, 2, 3); | ||||
| 
 | ||||
|     // bottom face vertices
 | ||||
|     append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); | ||||
|     append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); | ||||
| 
 | ||||
|     // bottom face triangles
 | ||||
|     append_indices(entity, 7, 13, 8); | ||||
|     append_indices(entity, 13, 12, 8); | ||||
|     append_indices(entity, 12, 11, 10); | ||||
|     append_indices(entity, 8, 12, 10); | ||||
|     append_indices(entity, 9, 8, 10); | ||||
| 
 | ||||
|     // side faces vertices
 | ||||
|     append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX()); | ||||
|     append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX()); | ||||
| 
 | ||||
|     append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); | ||||
| 
 | ||||
|     Vec3f normal(tip_height, half_tip_width, 0.0f); | ||||
|     normal.normalize(); | ||||
|     append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, normal); | ||||
|     append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); | ||||
|     append_vertex(entity, { half_tip_width, stem_height, half_thickness }, normal); | ||||
|     append_vertex(entity, { 0.0, total_height, half_thickness }, normal); | ||||
| 
 | ||||
|     normal = Vec3f(-tip_height, half_tip_width, 0.0f); | ||||
|     normal.normalize(); | ||||
|     append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); | ||||
|     append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, normal); | ||||
|     append_vertex(entity, { 0.0, total_height, half_thickness }, normal); | ||||
|     append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, normal); | ||||
| 
 | ||||
|     append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); | ||||
| 
 | ||||
|     append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX()); | ||||
|     append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX()); | ||||
|     append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX()); | ||||
|     append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX()); | ||||
| 
 | ||||
|     append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); | ||||
|     append_vertex(entity, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); | ||||
| 
 | ||||
|     // side face triangles
 | ||||
|     for (int i = 0; i < 7; ++i) { | ||||
|         const int ii = i * 4; | ||||
|         append_indices(entity, 14 + ii, 15 + ii, 17 + ii); | ||||
|         append_indices(entity, 14 + ii, 17 + ii, 16 + ii); | ||||
|     } | ||||
| 
 | ||||
|     data.entities.emplace_back(entity); | ||||
| #else | ||||
|     append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); | ||||
|     append_vertex(data, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); | ||||
|  | @ -517,12 +999,12 @@ GLModelInitializationData straight_arrow(float tip_width, float tip_height, floa | |||
|     append_vertex(data, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); | ||||
| 
 | ||||
|     // side face triangles
 | ||||
|     for (int i = 0; i < 7; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < 7; ++i) { | ||||
|         int ii = i * 4; | ||||
|         data.triangles.emplace_back(14 + ii, 15 + ii, 17 + ii); | ||||
|         data.triangles.emplace_back(14 + ii, 17 + ii, 16 + ii); | ||||
|     } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     return data; | ||||
| } | ||||
|  |  | |||
|  | @ -12,40 +12,115 @@ class TriangleMesh; | |||
| 
 | ||||
| namespace GUI { | ||||
| 
 | ||||
| #if !ENABLE_SEQUENTIAL_LIMITS | ||||
|     struct GLModelInitializationData | ||||
|     { | ||||
|         std::vector<Vec3f> positions; | ||||
|         std::vector<Vec3f> normals; | ||||
|         std::vector<Vec3i> triangles; | ||||
|     }; | ||||
| #endif // !ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     class GLModel | ||||
|     { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     public: | ||||
|         enum class PrimitiveType : unsigned char | ||||
|         { | ||||
|             Triangles, | ||||
|             Lines, | ||||
|             LineStrip, | ||||
|             LineLoop | ||||
|         }; | ||||
| 
 | ||||
|         struct RenderData | ||||
|         { | ||||
|             PrimitiveType type; | ||||
|             unsigned int vbo_id{ 0 }; | ||||
|             unsigned int ibo_id{ 0 }; | ||||
|             size_t indices_count{ 0 }; | ||||
|             std::array<float, 4> color{ 1.0f, 1.0f, 1.0f, 1.0f }; | ||||
|         }; | ||||
| 
 | ||||
|         struct InitializationData | ||||
|         { | ||||
|             struct Entity | ||||
|             { | ||||
|                 PrimitiveType type; | ||||
|                 std::vector<Vec3f> positions; | ||||
|                 std::vector<Vec3f> normals; | ||||
|                 std::vector<unsigned int> indices; | ||||
|                 std::array<float, 4> color{ 1.0f, 1.0f, 1.0f, 1.0f }; | ||||
|             }; | ||||
| 
 | ||||
|             std::vector<Entity> entities; | ||||
|         }; | ||||
| 
 | ||||
|     private: | ||||
|         std::vector<RenderData> m_render_data; | ||||
| #else | ||||
|         unsigned int m_vbo_id{ 0 }; | ||||
|         unsigned int m_ibo_id{ 0 }; | ||||
|         size_t m_indices_count{ 0 }; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|         BoundingBoxf3 m_bounding_box; | ||||
|         std::string m_filename; | ||||
| 
 | ||||
|     public: | ||||
|         GLModel() = default; | ||||
|         virtual ~GLModel() { reset(); } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         void init_from(const InitializationData& data); | ||||
| #else | ||||
|         void init_from(const GLModelInitializationData& data); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|         void init_from(const TriangleMesh& mesh); | ||||
|         bool init_from_file(const std::string& filename); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         // if entity_id == -1 set the color of all entities
 | ||||
|         void set_color(int entity_id, const std::array<float, 4>& color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|         void reset(); | ||||
|         void render() const; | ||||
| 
 | ||||
|         const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         bool is_initialized() const { return !m_render_data.empty(); } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|         const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } | ||||
|         const std::string& get_filename() const { return m_filename; } | ||||
| 
 | ||||
|     private: | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         void send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices); | ||||
| #else | ||||
|         void send_to_gpu(const std::vector<float>& vertices, const std::vector<unsigned int>& indices); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     }; | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution
 | ||||
|     // the origin of the arrow is in the center of the stem cap
 | ||||
|     // the arrow has its axis of symmetry along the Z axis and is pointing upward
 | ||||
|     // used to render bed axes and sequential marker
 | ||||
|     GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); | ||||
| 
 | ||||
|     // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution
 | ||||
|     // the origin of the arrow is in the center of the circle
 | ||||
|     // the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise
 | ||||
|     // used to render sidebar hints for rotations
 | ||||
|     GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); | ||||
| 
 | ||||
|     // create an arrow with the given dimensions
 | ||||
|     // the origin of the arrow is in the center of the stem cap
 | ||||
|     // the arrow is contained in XY plane and has its main axis along the Y axis
 | ||||
|     // used to render sidebar hints for position and scale
 | ||||
|     GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); | ||||
| #else | ||||
|     // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution
 | ||||
|     // the origin of the arrow is in the center of the stem cap
 | ||||
|     // the arrow has its axis of symmetry along the Z axis and is pointing upward
 | ||||
|  | @ -60,6 +135,7 @@ namespace GUI { | |||
|     // the origin of the arrow is in the center of the stem cap
 | ||||
|     // the arrow is contained in XY plane and has its main axis along the Y axis
 | ||||
|     GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "GLCanvas3D.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| #include "Plater.hpp" | ||||
| #include <igl/project.h> | ||||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
|  | @ -38,23 +39,26 @@ namespace GUI { | |||
|         m_state = Off; | ||||
| 
 | ||||
|         const Camera& camera = wxGetApp().plater()->get_camera(); | ||||
|         const std::array<int, 4>& viewport = camera.get_viewport(); | ||||
|         const Transform3d& modelview_matrix = camera.get_view_matrix(); | ||||
|         const Transform3d& projection_matrix = camera.get_projection_matrix(); | ||||
|         Matrix4d modelview = camera.get_view_matrix().matrix(); | ||||
|         Matrix4d projection= camera.get_projection_matrix().matrix(); | ||||
|         Vec4i viewport(camera.get_viewport().data()); | ||||
| 
 | ||||
|         // Convert our std::vector to Eigen dynamic matrix.
 | ||||
|         Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> pts(points.size(), 3); | ||||
|         for (size_t i=0; i<points.size(); ++i) | ||||
|             pts.block<1, 3>(i, 0) = points[i]; | ||||
| 
 | ||||
|         // Get the projections.
 | ||||
|         Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> projections; | ||||
|         igl::project(pts, modelview, projection, viewport, projections); | ||||
| 
 | ||||
|         // bounding box created from the rectangle corners - will take care of order of the corners
 | ||||
|         BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) }); | ||||
| 
 | ||||
|         // Iterate over all points and determine whether they're in the rectangle.
 | ||||
|         for (unsigned int i = 0; i<points.size(); ++i) { | ||||
|             const Vec3d& point = points[i]; | ||||
|             GLdouble out_x, out_y, out_z; | ||||
|             ::gluProject((GLdouble)point(0), (GLdouble)point(1), (GLdouble)point(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z); | ||||
|             out_y = canvas.get_canvas_size().get_height() - out_y; | ||||
| 
 | ||||
|             if (rectangle.contains(Point(out_x, out_y))) | ||||
|         for (int i = 0; i<projections.rows(); ++i) | ||||
|             if (rectangle.contains(Point(projections(i, 0), canvas.get_canvas_size().get_height() - projections(i, 1)))) | ||||
|                 out.push_back(i); | ||||
|         } | ||||
| 
 | ||||
|         return out; | ||||
|     } | ||||
|  |  | |||
|  | @ -20,26 +20,22 @@ GLGizmoBase::Grabber::Grabber() | |||
|     , dragging(false) | ||||
|     , enabled(true) | ||||
| { | ||||
|     color[0] = 1.0f; | ||||
|     color[1] = 1.0f; | ||||
|     color[2] = 1.0f; | ||||
|     color[3] = 1.0f; | ||||
|     color = { 1.0f, 1.0f, 1.0f, 1.0f }; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::Grabber::render(bool hover, float size) const | ||||
| { | ||||
|     float render_color[4]; | ||||
|     if (hover) | ||||
|     { | ||||
|         render_color[0] = 1.0f - color[0]; | ||||
|         render_color[1] = 1.0f - color[1]; | ||||
|         render_color[2] = 1.0f - color[2]; | ||||
|     std::array<float, 4> render_color; | ||||
|     if (hover) { | ||||
|         render_color[0] = (1.0f - color[0]); | ||||
|         render_color[1] = (1.0f - color[1]); | ||||
|         render_color[2] = (1.0f - color[2]); | ||||
|         render_color[3] = color[3]; | ||||
|     } | ||||
|     else | ||||
|         ::memcpy((void*)render_color, (const void*)color, 4 * sizeof(float)); | ||||
|         render_color = color; | ||||
| 
 | ||||
|     render(size, render_color, true); | ||||
|     render(size, render_color, false); | ||||
| } | ||||
| 
 | ||||
| float GLGizmoBase::Grabber::get_half_size(float size) const | ||||
|  | @ -52,80 +48,37 @@ float GLGizmoBase::Grabber::get_dragging_half_size(float size) const | |||
|     return get_half_size(size) * DraggingScaleFactor; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const | ||||
| void GLGizmoBase::Grabber::render(float size, const std::array<float, 4>& render_color, bool picking) const | ||||
| { | ||||
|     float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size); | ||||
|     if (! cube_initialized) { | ||||
|         // This cannot be done in constructor, OpenGL is not yet
 | ||||
|         // initialized at that point (on Linux at least).
 | ||||
|         TriangleMesh mesh = make_cube(1., 1., 1.); | ||||
|         mesh.translate(Vec3f(-0.5, -0.5, -0.5)); | ||||
|         const_cast<GLModel&>(cube).init_from(mesh); | ||||
|         const_cast<bool&>(cube_initialized) = true; | ||||
|     } | ||||
| 
 | ||||
|     if (use_lighting) | ||||
|         glsafe(::glEnable(GL_LIGHTING)); | ||||
|     float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); | ||||
| 
 | ||||
|     glsafe(::glColor4fv(render_color)); | ||||
|     GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_current_shader(); | ||||
|     if (shader != nullptr) | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         const_cast<GLModel*>(&cube)->set_color(-1, render_color); | ||||
| #else | ||||
|         shader->set_uniform("uniform_color", render_color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     else | ||||
|         glsafe(::glColor4fv(render_color.data())); // picking
 | ||||
| 
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslated(center(0), center(1), center(2))); | ||||
| 
 | ||||
|     glsafe(::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0)); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0)); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0)); | ||||
| 
 | ||||
|     // face min x
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f)); | ||||
|     glsafe(::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f)); | ||||
|     render_face(half_size); | ||||
|     glsafe(::glTranslated(center.x(), center.y(), center.z())); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); | ||||
|     glsafe(::glScaled(fullsize, fullsize, fullsize)); | ||||
|     cube.render(); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     // face max x
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslatef((GLfloat)half_size, 0.0f, 0.0f)); | ||||
|     glsafe(::glRotatef(90.0f, 0.0f, 1.0f, 0.0f)); | ||||
|     render_face(half_size); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     // face min y
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f)); | ||||
|     glsafe(::glRotatef(90.0f, 1.0f, 0.0f, 0.0f)); | ||||
|     render_face(half_size); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     // face max y
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslatef(0.0f, (GLfloat)half_size, 0.0f)); | ||||
|     glsafe(::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f)); | ||||
|     render_face(half_size); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     // face min z
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size)); | ||||
|     glsafe(::glRotatef(180.0f, 1.0f, 0.0f, 0.0f)); | ||||
|     render_face(half_size); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     // face max z
 | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslatef(0.0f, 0.0f, (GLfloat)half_size)); | ||||
|     render_face(half_size); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     if (use_lighting) | ||||
|         glsafe(::glDisable(GL_LIGHTING)); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::Grabber::render_face(float half_size) const | ||||
| { | ||||
|     ::glBegin(GL_TRIANGLES); | ||||
|     ::glNormal3f(0.0f, 0.0f, 1.0f); | ||||
|     ::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f); | ||||
|     ::glVertex3f((GLfloat)half_size, -(GLfloat)half_size, 0.0f); | ||||
|     ::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f); | ||||
|     ::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f); | ||||
|     ::glVertex3f(-(GLfloat)half_size, (GLfloat)half_size, 0.0f); | ||||
|     ::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f); | ||||
|     glsafe(::glEnd()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -141,9 +94,12 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u | |||
|     , m_imgui(wxGetApp().imgui()) | ||||
|     , m_first_input_window_render(true) | ||||
| { | ||||
|     ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 4 * sizeof(float)); | ||||
|     ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 4 * sizeof(float)); | ||||
|     ::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 4 * sizeof(float)); | ||||
|     m_base_color = DEFAULT_BASE_COLOR; | ||||
|     m_drag_color = DEFAULT_DRAG_COLOR; | ||||
|     m_highlight_color = DEFAULT_HIGHLIGHT_COLOR; | ||||
|     m_cone.init_from(make_cone(1., 1., 2 * PI / 24)); | ||||
|     m_sphere.init_from(make_sphere(1., (2 * M_PI) / 24.)); | ||||
|     m_cylinder.init_from(make_cylinder(1., 1., 2 * PI / 24.)); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::set_hover_id(int id) | ||||
|  | @ -155,10 +111,9 @@ void GLGizmoBase::set_hover_id(int id) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::set_highlight_color(const float* color) | ||||
| void GLGizmoBase::set_highlight_color(const std::array<float, 4>& color) | ||||
| { | ||||
|     if (color != nullptr) | ||||
|         ::memcpy((void*)m_highlight_color, (const void*)color, 4 * sizeof(float)); | ||||
|     m_highlight_color = color; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::enable_grabber(unsigned int id) | ||||
|  | @ -227,31 +182,31 @@ std::array<float, 4> GLGizmoBase::picking_color_component(unsigned int id) const | |||
| 
 | ||||
| void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const | ||||
| { | ||||
|     render_grabbers((float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); | ||||
|     render_grabbers((float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::render_grabbers(float size) const | ||||
| { | ||||
|     for (int i = 0; i < (int)m_grabbers.size(); ++i) | ||||
|     { | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader == nullptr) | ||||
|         return; | ||||
|     shader->start_using(); | ||||
|     shader->set_uniform("emission_factor", 0.1); | ||||
|     for (int i = 0; i < (int)m_grabbers.size(); ++i) { | ||||
|         if (m_grabbers[i].enabled) | ||||
|             m_grabbers[i].render((m_hover_id == i), size); | ||||
|             m_grabbers[i].render(m_hover_id == i, size); | ||||
|     } | ||||
|     shader->stop_using(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const | ||||
| { | ||||
|     float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); | ||||
|     float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) | ||||
|     { | ||||
|         if (m_grabbers[i].enabled) | ||||
|         { | ||||
|     for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { | ||||
|         if (m_grabbers[i].enabled) { | ||||
|             std::array<float, 4> color = picking_color_component(i); | ||||
|             m_grabbers[i].color[0] = color[0]; | ||||
|             m_grabbers[i].color[1] = color[1]; | ||||
|             m_grabbers[i].color[2] = color[2]; | ||||
|             m_grabbers[i].color[3] = color[3]; | ||||
|             m_grabbers[i].color = color; | ||||
|             m_grabbers[i].render_for_picking(mean_size); | ||||
|         } | ||||
|     } | ||||
|  | @ -265,8 +220,7 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const | |||
| void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) | ||||
| { | ||||
|     on_render_input_window(x, y, bottom_limit); | ||||
|     if (m_first_input_window_render) | ||||
|     { | ||||
|     if (m_first_input_window_render) { | ||||
|         // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame
 | ||||
|         // so, we forces another frame rendering the first time the imgui window is shown
 | ||||
|         m_parent.set_as_dirty(); | ||||
|  |  | |||
|  | @ -4,13 +4,11 @@ | |||
| #include "libslic3r/Point.hpp" | ||||
| 
 | ||||
| #include "slic3r/GUI/I18N.hpp" | ||||
| #include "slic3r/GUI/GLModel.hpp" | ||||
| 
 | ||||
| #include <cereal/archives/binary.hpp> | ||||
| 
 | ||||
| class wxWindow; | ||||
| class GLUquadric; | ||||
| typedef class GLUquadric GLUquadricObj; | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -20,13 +18,15 @@ class ModelObject; | |||
| 
 | ||||
| namespace GUI { | ||||
| 
 | ||||
| static const float DEFAULT_BASE_COLOR[4] = { 0.625f, 0.625f, 0.625f, 1.0f }; | ||||
| static const float DEFAULT_DRAG_COLOR[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; | ||||
| static const float DEFAULT_HIGHLIGHT_COLOR[4] = { 1.0f, 0.38f, 0.0f, 1.0f }; | ||||
| static const float AXES_COLOR[][4] = { { 0.75f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.75f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.75f, 1.0f } }; | ||||
| static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; | ||||
| 
 | ||||
| 
 | ||||
| static const std::array<float, 4> DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }; | ||||
| static const std::array<float, 4> DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; | ||||
| static const std::array<float, 4> DEFAULT_HIGHLIGHT_COLOR = { 1.0f, 0.38f, 0.0f, 1.0f }; | ||||
| static const std::array<std::array<float, 4>, 3> AXES_COLOR = {{ | ||||
|                                                                 { 0.75f, 0.0f, 0.0f, 1.0f }, | ||||
|                                                                 { 0.0f, 0.75f, 0.0f, 1.0f }, | ||||
|                                                                 { 0.0f, 0.0f, 0.75f, 1.0f } | ||||
|                                                               }}; | ||||
| static const std::array<float, 4> CONSTRAINED_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; | ||||
| 
 | ||||
| class ImGuiWrapper; | ||||
| class GLCanvas3D; | ||||
|  | @ -50,21 +50,23 @@ protected: | |||
| 
 | ||||
|         Vec3d center; | ||||
|         Vec3d angles; | ||||
|         float color[4]; | ||||
|         std::array<float, 4> color; | ||||
|         bool enabled; | ||||
|         bool dragging; | ||||
| 
 | ||||
|         Grabber(); | ||||
| 
 | ||||
|         void render(bool hover, float size) const; | ||||
|         void render_for_picking(float size) const { render(size, color, false); } | ||||
|         void render_for_picking(float size) const { render(size, color, true); } | ||||
| 
 | ||||
|         float get_half_size(float size) const; | ||||
|         float get_dragging_half_size(float size) const; | ||||
| 
 | ||||
|     private: | ||||
|         void render(float size, const float* render_color, bool use_lighting) const; | ||||
|         void render_face(float half_size) const; | ||||
|         void render(float size, const std::array<float, 4>& render_color, bool picking) const; | ||||
| 
 | ||||
|         GLModel cube; | ||||
|         bool cube_initialized = false; | ||||
|     }; | ||||
| 
 | ||||
| public: | ||||
|  | @ -95,14 +97,17 @@ protected: | |||
|     unsigned int m_sprite_id; | ||||
|     int m_hover_id; | ||||
|     bool m_dragging; | ||||
|     float m_base_color[4]; | ||||
|     float m_drag_color[4]; | ||||
|     float m_highlight_color[4]; | ||||
|     std::array<float, 4> m_base_color; | ||||
|     std::array<float, 4> m_drag_color; | ||||
|     std::array<float, 4> m_highlight_color; | ||||
|     mutable std::vector<Grabber> m_grabbers; | ||||
|     ImGuiWrapper* m_imgui; | ||||
|     bool m_first_input_window_render; | ||||
|     mutable std::string m_tooltip; | ||||
|     CommonGizmosDataPool* m_c; | ||||
|     GLModel m_cone; | ||||
|     GLModel m_cylinder; | ||||
|     GLModel m_sphere; | ||||
| 
 | ||||
| public: | ||||
|     GLGizmoBase(GLCanvas3D& parent, | ||||
|  | @ -137,7 +142,7 @@ public: | |||
|     int get_hover_id() const { return m_hover_id; } | ||||
|     void set_hover_id(int id); | ||||
|      | ||||
|     void set_highlight_color(const float* color); | ||||
|     void set_highlight_color(const std::array<float, 4>& color); | ||||
| 
 | ||||
|     void enable_grabber(unsigned int id); | ||||
|     void disable_grabber(unsigned int id); | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ bool GLGizmoCut::on_init() | |||
| 
 | ||||
| std::string GLGizmoCut::on_get_name() const | ||||
| { | ||||
|     return (_(L("Cut")) + " [C]").ToUTF8().data(); | ||||
|     return (_L("Cut") + " [C]").ToUTF8().data(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoCut::on_set_state() | ||||
|  | @ -96,12 +96,12 @@ void GLGizmoCut::on_render() const | |||
| 
 | ||||
|     const BoundingBoxf3& box = selection.get_bounding_box(); | ||||
|     Vec3d plane_center = box.center(); | ||||
|     plane_center(2) = m_cut_z; | ||||
|     plane_center.z() = m_cut_z; | ||||
| 
 | ||||
|     const float min_x = box.min(0) - Margin; | ||||
|     const float max_x = box.max(0) + Margin; | ||||
|     const float min_y = box.min(1) - Margin; | ||||
|     const float max_y = box.max(1) + Margin; | ||||
|     const float min_x = box.min.x() - Margin; | ||||
|     const float max_x = box.max.x() + Margin; | ||||
|     const float min_y = box.min.y() - Margin; | ||||
|     const float max_y = box.max.y() + Margin; | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
|     glsafe(::glDisable(GL_CULL_FACE)); | ||||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|  | @ -110,10 +110,10 @@ void GLGizmoCut::on_render() const | |||
|     // Draw the cutting plane
 | ||||
|     ::glBegin(GL_QUADS); | ||||
|     ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); | ||||
|     ::glVertex3f(min_x, min_y, plane_center(2)); | ||||
|     ::glVertex3f(max_x, min_y, plane_center(2)); | ||||
|     ::glVertex3f(max_x, max_y, plane_center(2)); | ||||
|     ::glVertex3f(min_x, max_y, plane_center(2)); | ||||
|     ::glVertex3f(min_x, min_y, plane_center.z()); | ||||
|     ::glVertex3f(max_x, min_y, plane_center.z()); | ||||
|     ::glVertex3f(max_x, max_y, plane_center.z()); | ||||
|     ::glVertex3f(min_x, max_y, plane_center.z()); | ||||
|     glsafe(::glEnd()); | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_CULL_FACE)); | ||||
|  | @ -123,9 +123,10 @@ void GLGizmoCut::on_render() const | |||
| 
 | ||||
|     // Draw the grabber and the connecting line
 | ||||
|     m_grabbers[0].center = plane_center; | ||||
|     m_grabbers[0].center(2) = plane_center(2) + Offset; | ||||
|     m_grabbers[0].center.z() = plane_center.z() + Offset; | ||||
| 
 | ||||
|     glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); | ||||
| 
 | ||||
|     glsafe(::glDisable(GL_DEPTH_TEST)); | ||||
|     glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); | ||||
|     glsafe(::glColor3f(1.0, 1.0, 0.0)); | ||||
|     ::glBegin(GL_LINES); | ||||
|  | @ -133,8 +134,16 @@ void GLGizmoCut::on_render() const | |||
|     ::glVertex3dv(m_grabbers[0].center.data()); | ||||
|     glsafe(::glEnd()); | ||||
| 
 | ||||
|     std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color); | ||||
|     m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader == nullptr) | ||||
|         return; | ||||
|     shader->start_using(); | ||||
|     shader->set_uniform("emission_factor", 0.1); | ||||
| 
 | ||||
|     m_grabbers[0].color = GrabberColor; | ||||
|     m_grabbers[0].render(m_hover_id == 0, (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); | ||||
| 
 | ||||
|     shader->stop_using(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoCut::on_render_for_picking() const | ||||
|  |  | |||
|  | @ -19,20 +19,10 @@ namespace GUI { | |||
| 
 | ||||
| GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||
|     , m_quadric(nullptr) | ||||
| { | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         // using GLU_FILL does not work when the instance's transformation
 | ||||
|         // contains mirroring (normals are reverted)
 | ||||
|         ::gluQuadricDrawStyle(m_quadric, GLU_FILL); | ||||
|     m_vbo_cylinder.init_from(make_cylinder(1., 1.)); | ||||
| } | ||||
| 
 | ||||
| GLGizmoHollow::~GLGizmoHollow() | ||||
| { | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluDeleteQuadric(m_quadric); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoHollow::on_init() | ||||
| { | ||||
|  | @ -87,7 +77,7 @@ void GLGizmoHollow::on_render() const | |||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     if (m_quadric != nullptr && selection.is_from_single_instance()) | ||||
|     if (selection.is_from_single_instance()) | ||||
|         render_points(selection, false); | ||||
| 
 | ||||
|     m_selection_rectangle.render(m_parent); | ||||
|  | @ -111,8 +101,10 @@ void GLGizmoHollow::on_render_for_picking() const | |||
| 
 | ||||
| void GLGizmoHollow::render_points(const Selection& selection, bool picking) const | ||||
| { | ||||
|     if (!picking) | ||||
|         glsafe(::glEnable(GL_LIGHTING)); | ||||
|     GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader) | ||||
|         shader->start_using(); | ||||
|     ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); | ||||
| 
 | ||||
|     const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
|     const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); | ||||
|  | @ -150,16 +142,21 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons | |||
|             } | ||||
|             else { // neigher hover nor picking
 | ||||
| 
 | ||||
|                 render_color[0] = point_selected ? 1.0f : 0.7f; | ||||
|                 render_color[1] = point_selected ? 0.3f : 0.7f; | ||||
|                 render_color[2] = point_selected ? 0.3f : 0.7f; | ||||
|                 render_color[0] = point_selected ? 1.0f : 1.f; | ||||
|                 render_color[1] = point_selected ? 0.3f : 1.f; | ||||
|                 render_color[2] = point_selected ? 0.3f : 1.f; | ||||
|                 render_color[3] = 0.5f; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         glsafe(::glColor4fv(render_color.data())); | ||||
|         float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; | ||||
|         glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); | ||||
|         if (shader && ! picking) | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             const_cast<GLModel*>(&m_vbo_cylinder)->set_color(-1 , render_color); | ||||
| #else | ||||
|             shader->set_uniform("uniform_color", render_color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|         else // picking
 | ||||
|             glsafe(::glColor4fv(render_color.data())); | ||||
| 
 | ||||
|         // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
 | ||||
|         glsafe(::glPushMatrix()); | ||||
|  | @ -176,12 +173,8 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons | |||
|         glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); | ||||
|         glsafe(::glPushMatrix()); | ||||
|         glsafe(::glTranslated(0., 0., -drain_hole.height)); | ||||
|         ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength, 24, 1); | ||||
|         glsafe(::glTranslated(0., 0., drain_hole.height + sla::HoleStickOutLength)); | ||||
|         ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); | ||||
|         glsafe(::glTranslated(0., 0., -drain_hole.height - sla::HoleStickOutLength)); | ||||
|         glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); | ||||
|         ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); | ||||
|         glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); | ||||
|         m_vbo_cylinder.render(); | ||||
|         glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|         if (vol->is_left_handed()) | ||||
|  | @ -189,15 +182,6 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons | |||
|         glsafe(::glPopMatrix()); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         // Reset emissive component to zero (the default value)
 | ||||
|         float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f }; | ||||
|         glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); | ||||
|     } | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glDisable(GL_LIGHTING)); | ||||
| 
 | ||||
|     glsafe(::glPopMatrix()); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,12 +25,10 @@ class GLGizmoHollow : public GLGizmoBase | |||
| private: | ||||
|     bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal); | ||||
| 
 | ||||
|     GLUquadricObj* m_quadric; | ||||
| 
 | ||||
| 
 | ||||
| public: | ||||
|     GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||
|     ~GLGizmoHollow() override; | ||||
|     virtual ~GLGizmoHollow() = default; | ||||
|     void set_sla_support_data(ModelObject* model_object, const Selection& selection); | ||||
|     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); | ||||
|     void delete_selected_points();     | ||||
|  | @ -50,7 +48,7 @@ private: | |||
| 
 | ||||
|     ObjectID m_old_mo_id = -1; | ||||
| 
 | ||||
|     // bool  m_show_supports = true;
 | ||||
|     GLModel m_vbo_cylinder; | ||||
|     float m_new_hole_radius = 2.f;        // Size of a new hole.
 | ||||
|     float m_new_hole_height = 6.f; | ||||
|     mutable std::vector<bool> m_selected; // which holes are currently selected
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
 | ||||
| #include "GLGizmoMove.hpp" | ||||
| #include "slic3r/GUI/GLCanvas3D.hpp" | ||||
| #include "slic3r/GUI/GUI_App.hpp" | ||||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
|  | @ -18,17 +19,8 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam | |||
|     , m_starting_drag_position(Vec3d::Zero()) | ||||
|     , m_starting_box_center(Vec3d::Zero()) | ||||
|     , m_starting_box_bottom_center(Vec3d::Zero()) | ||||
|     , m_quadric(nullptr) | ||||
| { | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluQuadricDrawStyle(m_quadric, GLU_FILL); | ||||
| } | ||||
| 
 | ||||
| GLGizmoMove3D::~GLGizmoMove3D() | ||||
| { | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluDeleteQuadric(m_quadric); | ||||
|     m_vbo_cone.init_from(make_cone(1., 1., 2*PI/36)); | ||||
| } | ||||
| 
 | ||||
| std::string GLGizmoMove3D::get_tooltip() const | ||||
|  | @ -49,8 +41,7 @@ std::string GLGizmoMove3D::get_tooltip() const | |||
| 
 | ||||
| bool GLGizmoMove3D::on_init() | ||||
| { | ||||
|     for (int i = 0; i < 3; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < 3; ++i) { | ||||
|         m_grabbers.push_back(Grabber()); | ||||
|     } | ||||
| 
 | ||||
|  | @ -61,7 +52,7 @@ bool GLGizmoMove3D::on_init() | |||
| 
 | ||||
| std::string GLGizmoMove3D::on_get_name() const | ||||
| { | ||||
|     return (_(L("Move")) + " [M]").ToUTF8().data(); | ||||
|     return (_L("Move") + " [M]").ToUTF8().data(); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoMove3D::on_is_activable() const | ||||
|  | @ -71,8 +62,7 @@ bool GLGizmoMove3D::on_is_activable() const | |||
| 
 | ||||
| void GLGizmoMove3D::on_start_dragging() | ||||
| { | ||||
|     if (m_hover_id != -1) | ||||
|     { | ||||
|     if (m_hover_id != -1) { | ||||
|         m_displacement = Vec3d::Zero(); | ||||
|         const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); | ||||
|         m_starting_drag_position = m_grabbers[m_hover_id].center; | ||||
|  | @ -90,11 +80,11 @@ void GLGizmoMove3D::on_stop_dragging() | |||
| void GLGizmoMove3D::on_update(const UpdateData& data) | ||||
| { | ||||
|     if (m_hover_id == 0) | ||||
|         m_displacement(0) = calc_projection(data); | ||||
|         m_displacement.x() = calc_projection(data); | ||||
|     else if (m_hover_id == 1) | ||||
|         m_displacement(1) = calc_projection(data); | ||||
|         m_displacement.y() = calc_projection(data); | ||||
|     else if (m_hover_id == 2) | ||||
|         m_displacement(2) = calc_projection(data); | ||||
|         m_displacement.z() = calc_projection(data); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoMove3D::on_render() const | ||||
|  | @ -108,27 +98,24 @@ void GLGizmoMove3D::on_render() const | |||
|     const Vec3d& center = box.center(); | ||||
| 
 | ||||
|     // x axis
 | ||||
|     m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2)); | ||||
|     ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 4 * sizeof(float)); | ||||
|     m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; | ||||
|     m_grabbers[0].color = AXES_COLOR[0]; | ||||
| 
 | ||||
|     // y axis
 | ||||
|     m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2)); | ||||
|     ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 4 * sizeof(float)); | ||||
|     m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; | ||||
|     m_grabbers[1].color = AXES_COLOR[1]; | ||||
| 
 | ||||
|     // z axis
 | ||||
|     m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset); | ||||
|     ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 4 * sizeof(float)); | ||||
|     m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; | ||||
|     m_grabbers[2].color = AXES_COLOR[2]; | ||||
| 
 | ||||
|     glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); | ||||
| 
 | ||||
|     if (m_hover_id == -1) | ||||
|     { | ||||
|     if (m_hover_id == -1) { | ||||
|         // draw axes
 | ||||
|         for (unsigned int i = 0; i < 3; ++i) | ||||
|         { | ||||
|             if (m_grabbers[i].enabled) | ||||
|             { | ||||
|                 glsafe(::glColor4fv(AXES_COLOR[i])); | ||||
|         for (unsigned int i = 0; i < 3; ++i) { | ||||
|             if (m_grabbers[i].enabled) { | ||||
|                 glsafe(::glColor4fv(AXES_COLOR[i].data())); | ||||
|                 ::glBegin(GL_LINES); | ||||
|                 ::glVertex3dv(center.data()); | ||||
|                 ::glVertex3dv(m_grabbers[i].center.data()); | ||||
|  | @ -138,24 +125,28 @@ void GLGizmoMove3D::on_render() const | |||
| 
 | ||||
|         // draw grabbers
 | ||||
|         render_grabbers(box); | ||||
|         for (unsigned int i = 0; i < 3; ++i) | ||||
|         { | ||||
|         for (unsigned int i = 0; i < 3; ++i) { | ||||
|             if (m_grabbers[i].enabled) | ||||
|                 render_grabber_extension((Axis)i, box, false); | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     else { | ||||
|         // draw axis
 | ||||
|         glsafe(::glColor4fv(AXES_COLOR[m_hover_id])); | ||||
|         glsafe(::glColor4fv(AXES_COLOR[m_hover_id].data())); | ||||
|         ::glBegin(GL_LINES); | ||||
|         ::glVertex3dv(center.data()); | ||||
|         ::glVertex3dv(m_grabbers[m_hover_id].center.data()); | ||||
|         glsafe(::glEnd()); | ||||
| 
 | ||||
|         // draw grabber
 | ||||
|         float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); | ||||
|         m_grabbers[m_hover_id].render(true, mean_size); | ||||
|         GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|         if (shader != nullptr) { | ||||
|             shader->start_using(); | ||||
|             shader->set_uniform("emission_factor", 0.1); | ||||
|             // draw grabber
 | ||||
|             float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); | ||||
|             m_grabbers[m_hover_id].render(true, mean_size); | ||||
|             shader->stop_using(); | ||||
|         } | ||||
|         render_grabber_extension((Axis)m_hover_id, box, false); | ||||
|     } | ||||
| } | ||||
|  | @ -177,8 +168,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const | |||
| 
 | ||||
|     Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; | ||||
|     double len_starting_vec = starting_vec.norm(); | ||||
|     if (len_starting_vec != 0.0) | ||||
|     { | ||||
|     if (len_starting_vec != 0.0) { | ||||
|         Vec3d mouse_dir = data.mouse_ray.unit_vector(); | ||||
|         // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
 | ||||
|         // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
 | ||||
|  | @ -200,42 +190,46 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const | |||
| 
 | ||||
| void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const | ||||
| { | ||||
|     if (m_quadric == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); | ||||
|     float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); | ||||
|     double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size); | ||||
| 
 | ||||
|     float color[4]; | ||||
|     ::memcpy((void*)color, (const void*)m_grabbers[axis].color, 4 * sizeof(float)); | ||||
|     if (!picking && (m_hover_id != -1)) | ||||
|     { | ||||
|     std::array<float, 4> color = m_grabbers[axis].color; | ||||
|     if (!picking && m_hover_id != -1) { | ||||
|         color[0] = 1.0f - color[0]; | ||||
|         color[1] = 1.0f - color[1]; | ||||
|         color[2] = 1.0f - color[2]; | ||||
|         color[3] = color[3]; | ||||
|     } | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glEnable(GL_LIGHTING)); | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     if (! picking) { | ||||
|         shader->start_using(); | ||||
|         shader->set_uniform("emission_factor", 0.1); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         const_cast<GLModel*>(&m_vbo_cone)->set_color(-1, color); | ||||
| #else | ||||
|         shader->set_uniform("uniform_color", color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     } else | ||||
|         glsafe(::glColor4fv(color.data())); | ||||
| 
 | ||||
|     glsafe(::glColor4fv(color)); | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2))); | ||||
|     glsafe(::glTranslated(m_grabbers[axis].center.x(), m_grabbers[axis].center.y(), m_grabbers[axis].center.z())); | ||||
|     if (axis == X) | ||||
|         glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); | ||||
|     else if (axis == Y) | ||||
|         glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); | ||||
| 
 | ||||
|     glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); | ||||
|     ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); | ||||
|     ::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1); | ||||
|     ::gluQuadricOrientation(m_quadric, GLU_INSIDE); | ||||
|     ::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1); | ||||
|     glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); | ||||
|     m_vbo_cone.render(); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glDisable(GL_LIGHTING)); | ||||
|     if (! picking) | ||||
|         shader->stop_using(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,11 +19,11 @@ class GLGizmoMove3D : public GLGizmoBase | |||
|     Vec3d m_starting_box_center; | ||||
|     Vec3d m_starting_box_bottom_center; | ||||
| 
 | ||||
|     GLUquadricObj* m_quadric; | ||||
|     GLModel m_vbo_cone; | ||||
| 
 | ||||
| public: | ||||
|     GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||
|     virtual ~GLGizmoMove3D(); | ||||
|     virtual ~GLGizmoMove3D() = default; | ||||
| 
 | ||||
|     double get_snap_step(double step) const { return m_snap_step; } | ||||
|     void set_snap_step(double step) { m_snap_step = step; } | ||||
|  |  | |||
|  | @ -30,7 +30,6 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) | |||
|     : GLGizmoBase(parent, "", -1) | ||||
|     , m_axis(axis) | ||||
|     , m_angle(0.0) | ||||
|     , m_quadric(nullptr) | ||||
|     , m_center(0.0, 0.0, 0.0) | ||||
|     , m_radius(0.0f) | ||||
|     , m_snap_coarse_in_radius(0.0f) | ||||
|  | @ -38,16 +37,12 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) | |||
|     , m_snap_fine_in_radius(0.0f) | ||||
|     , m_snap_fine_out_radius(0.0f) | ||||
| { | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluQuadricDrawStyle(m_quadric, GLU_FILL); | ||||
| } | ||||
| 
 | ||||
| GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) | ||||
|     : GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id) | ||||
|     , m_axis(other.m_axis) | ||||
|     , m_angle(other.m_angle) | ||||
|     , m_quadric(nullptr) | ||||
|     , m_center(other.m_center) | ||||
|     , m_radius(other.m_radius) | ||||
|     , m_snap_coarse_in_radius(other.m_snap_coarse_in_radius) | ||||
|  | @ -55,16 +50,8 @@ GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) | |||
|     , m_snap_fine_in_radius(other.m_snap_fine_in_radius) | ||||
|     , m_snap_fine_out_radius(other.m_snap_fine_out_radius) | ||||
| { | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluQuadricDrawStyle(m_quadric, GLU_FILL); | ||||
| } | ||||
| 
 | ||||
| GLGizmoRotate::~GLGizmoRotate() | ||||
| { | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluDeleteQuadric(m_quadric); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoRotate::set_angle(double angle) | ||||
| { | ||||
|  | @ -146,8 +133,7 @@ void GLGizmoRotate::on_render() const | |||
|     const Selection& selection = m_parent.get_selection(); | ||||
|     const BoundingBoxf3& box = selection.get_bounding_box(); | ||||
| 
 | ||||
|     if (m_hover_id != 0 && !m_grabbers[0].dragging) | ||||
|     { | ||||
|     if (m_hover_id != 0 && !m_grabbers[0].dragging) { | ||||
|         m_center = box.center(); | ||||
|         m_radius = Offset + box.radius(); | ||||
|         m_snap_coarse_in_radius = m_radius / 3.0f; | ||||
|  | @ -162,18 +148,17 @@ void GLGizmoRotate::on_render() const | |||
|     transform_to_local(selection); | ||||
| 
 | ||||
|     glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); | ||||
|     glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color : m_highlight_color)); | ||||
|     glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); | ||||
| 
 | ||||
|     render_circle(); | ||||
| 
 | ||||
|     if (m_hover_id != -1) | ||||
|     { | ||||
|     if (m_hover_id != -1) { | ||||
|         render_scale(); | ||||
|         render_snap_radii(); | ||||
|         render_reference_radius(); | ||||
|     } | ||||
| 
 | ||||
|     glsafe(::glColor4fv(m_highlight_color)); | ||||
|     glsafe(::glColor4fv(m_highlight_color.data())); | ||||
| 
 | ||||
|     if (m_hover_id != -1) | ||||
|         render_angle(); | ||||
|  | @ -324,69 +309,70 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const | |||
|     m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); | ||||
|     m_grabbers[0].angles(2) = m_angle; | ||||
| 
 | ||||
|     glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color : m_highlight_color)); | ||||
|     glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); | ||||
| 
 | ||||
|     ::glBegin(GL_LINES); | ||||
|     ::glVertex3f(0.0f, 0.0f, 0.0f); | ||||
|     ::glVertex3dv(m_grabbers[0].center.data()); | ||||
|     glsafe(::glEnd()); | ||||
| 
 | ||||
|     ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 4 * sizeof(float)); | ||||
|     m_grabbers[0].color = m_highlight_color; | ||||
|     render_grabbers(box); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) const | ||||
| { | ||||
|     if (m_quadric == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); | ||||
|     double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size); | ||||
| 
 | ||||
|     float color[4]; | ||||
|     ::memcpy((void*)color, (const void*)m_grabbers[0].color, 4 * sizeof(float)); | ||||
|     if (!picking && (m_hover_id != -1)) | ||||
|     { | ||||
|     std::array<float, 4> color = m_grabbers[0].color; | ||||
|     if (!picking && m_hover_id != -1) { | ||||
|         color[0] = 1.0f - color[0]; | ||||
|         color[1] = 1.0f - color[1]; | ||||
|         color[2] = 1.0f - color[2]; | ||||
|     } | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glEnable(GL_LIGHTING)); | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     if (! picking) { | ||||
|         shader->start_using(); | ||||
|         shader->set_uniform("emission_factor", 0.1); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         const_cast<GLModel*>(&m_cone)->set_color(-1, color); | ||||
| #else | ||||
|         shader->set_uniform("uniform_color", color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|     } else | ||||
|         glsafe(::glColor4fv(color.data())); | ||||
| 
 | ||||
|     glsafe(::glColor4fv(color)); | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2))); | ||||
|     glsafe(::glTranslated(m_grabbers[0].center.x(), m_grabbers[0].center.y(), m_grabbers[0].center.z())); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); | ||||
|     glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); | ||||
|     glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); | ||||
|     ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); | ||||
|     ::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1); | ||||
|     ::gluQuadricOrientation(m_quadric, GLU_INSIDE); | ||||
|     ::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1); | ||||
|     glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); | ||||
|     m_cone.render(); | ||||
|     glsafe(::glPopMatrix()); | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2))); | ||||
|     glsafe(::glTranslated(m_grabbers[0].center.x(), m_grabbers[0].center.y(), m_grabbers[0].center.z())); | ||||
|     glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0)); | ||||
|     glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); | ||||
|     glsafe(::glTranslated(0.0, 0.0, 2.0 * size)); | ||||
|     ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); | ||||
|     ::gluCylinder(m_quadric, 0.75 * size, 0.0, 3.0 * size, 36, 1); | ||||
|     ::gluQuadricOrientation(m_quadric, GLU_INSIDE); | ||||
|     ::gluDisk(m_quadric, 0.0, 0.75 * size, 36, 1); | ||||
|     glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size)); | ||||
|     m_cone.render(); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glDisable(GL_LIGHTING)); | ||||
|     if (! picking) | ||||
|         shader->stop_using(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoRotate::transform_to_local(const Selection& selection) const | ||||
| { | ||||
|     glsafe(::glTranslated(m_center(0), m_center(1), m_center(2))); | ||||
| 
 | ||||
|     if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) | ||||
|     { | ||||
|     if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) { | ||||
|         Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); | ||||
|         glsafe(::glMultMatrixd(orient_matrix.data())); | ||||
|     } | ||||
|  | @ -457,8 +443,7 @@ GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_fil | |||
|     m_gizmos.emplace_back(parent, GLGizmoRotate::Y); | ||||
|     m_gizmos.emplace_back(parent, GLGizmoRotate::Z); | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < 3; ++i) | ||||
|     { | ||||
|     for (unsigned int i = 0; i < 3; ++i) { | ||||
|         m_gizmos[i].set_group_id(i); | ||||
|     } | ||||
| 
 | ||||
|  | @ -467,14 +452,12 @@ GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_fil | |||
| 
 | ||||
| bool GLGizmoRotate3D::on_init() | ||||
| { | ||||
|     for (GLGizmoRotate& g : m_gizmos) | ||||
|     { | ||||
|     for (GLGizmoRotate& g : m_gizmos) { | ||||
|         if (!g.init()) | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < 3; ++i) | ||||
|     { | ||||
|     for (unsigned int i = 0; i < 3; ++i) { | ||||
|         m_gizmos[i].set_highlight_color(AXES_COLOR[i]); | ||||
|     } | ||||
| 
 | ||||
|  | @ -485,7 +468,7 @@ bool GLGizmoRotate3D::on_init() | |||
| 
 | ||||
| std::string GLGizmoRotate3D::on_get_name() const | ||||
| { | ||||
|     return (_(L("Rotate")) + " [R]").ToUTF8().data(); | ||||
|     return (_L("Rotate") + " [R]").ToUTF8().data(); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoRotate3D::on_is_activable() const | ||||
|  |  | |||
|  | @ -32,8 +32,6 @@ private: | |||
|     Axis m_axis; | ||||
|     double m_angle; | ||||
| 
 | ||||
|     GLUquadricObj* m_quadric; | ||||
| 
 | ||||
|     mutable Vec3d m_center; | ||||
|     mutable float m_radius; | ||||
| 
 | ||||
|  | @ -45,7 +43,7 @@ private: | |||
| public: | ||||
|     GLGizmoRotate(GLCanvas3D& parent, Axis axis); | ||||
|     GLGizmoRotate(const GLGizmoRotate& other); | ||||
|     virtual ~GLGizmoRotate(); | ||||
|     virtual ~GLGizmoRotate() = default; | ||||
| 
 | ||||
|     double get_angle() const { return m_angle; } | ||||
|     void set_angle(double angle); | ||||
|  | @ -122,16 +120,14 @@ protected: | |||
|     void on_stop_dragging() override; | ||||
|     void on_update(const UpdateData& data) override | ||||
|     { | ||||
|         for (GLGizmoRotate& g : m_gizmos) | ||||
|         { | ||||
|         for (GLGizmoRotate& g : m_gizmos) { | ||||
|             g.update(data); | ||||
|         } | ||||
|     } | ||||
|     void on_render() const override; | ||||
|     void on_render_for_picking() const override | ||||
|     { | ||||
|         for (const GLGizmoRotate& g : m_gizmos) | ||||
|         { | ||||
|         for (const GLGizmoRotate& g : m_gizmos) { | ||||
|             g.render_for_picking(); | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
 | ||||
| #include "GLGizmoScale.hpp" | ||||
| #include "slic3r/GUI/GLCanvas3D.hpp" | ||||
| #include "slic3r/GUI/GUI_App.hpp" | ||||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
|  | @ -75,7 +76,7 @@ bool GLGizmoScale3D::on_init() | |||
| 
 | ||||
| std::string GLGizmoScale3D::on_get_name() const | ||||
| { | ||||
|     return (_(L("Scale")) + " [S]").ToUTF8().data(); | ||||
|     return (_L("Scale") + " [S]").ToUTF8().data(); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoScale3D::on_is_activable() const | ||||
|  | @ -131,12 +132,10 @@ void GLGizmoScale3D::on_render() const | |||
|     m_offsets_transform = Transform3d::Identity(); | ||||
|     Vec3d angles = Vec3d::Zero(); | ||||
| 
 | ||||
|     if (single_instance) | ||||
|     { | ||||
|     if (single_instance) { | ||||
|         // calculate bounding box in instance local reference system
 | ||||
|         const Selection::IndicesList& idxs = selection.get_volume_idxs(); | ||||
|         for (unsigned int idx : idxs) | ||||
|         { | ||||
|         for (unsigned int idx : idxs) { | ||||
|             const GLVolume* vol = selection.get_volume(idx); | ||||
|             m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix())); | ||||
|         } | ||||
|  | @ -150,8 +149,7 @@ void GLGizmoScale3D::on_render() const | |||
|         offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); | ||||
|         m_offsets_transform = offsets_transform; | ||||
|     } | ||||
|     else if (single_volume) | ||||
|     { | ||||
|     else if (single_volume) { | ||||
|         const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
|         m_box = v->bounding_box(); | ||||
|         m_transform = v->world_matrix(); | ||||
|  | @ -172,35 +170,33 @@ void GLGizmoScale3D::on_render() const | |||
| 
 | ||||
|     // x axis
 | ||||
|     m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x; | ||||
|     m_grabbers[0].color = (ctrl_down && (m_hover_id == 1)) ? CONSTRAINED_COLOR : AXES_COLOR[0]; | ||||
|     m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x; | ||||
|     ::memcpy((void*)m_grabbers[0].color, (ctrl_down && (m_hover_id == 1)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 4 * sizeof(float)); | ||||
|     ::memcpy((void*)m_grabbers[1].color, (ctrl_down && (m_hover_id == 0)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 4 * sizeof(float)); | ||||
|     m_grabbers[1].color = (ctrl_down && (m_hover_id == 0)) ? CONSTRAINED_COLOR : AXES_COLOR[0]; | ||||
| 
 | ||||
|     // y axis
 | ||||
|     m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y; | ||||
|     m_grabbers[2].color = (ctrl_down && (m_hover_id == 3)) ? CONSTRAINED_COLOR : AXES_COLOR[1]; | ||||
|     m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y; | ||||
|     ::memcpy((void*)m_grabbers[2].color, (ctrl_down && (m_hover_id == 3)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 4 * sizeof(float)); | ||||
|     ::memcpy((void*)m_grabbers[3].color, (ctrl_down && (m_hover_id == 2)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 4 * sizeof(float)); | ||||
|     m_grabbers[3].color = (ctrl_down && (m_hover_id == 2)) ? CONSTRAINED_COLOR : AXES_COLOR[1]; | ||||
| 
 | ||||
|     // z axis
 | ||||
|     m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z; | ||||
|     m_grabbers[4].color = (ctrl_down && (m_hover_id == 5)) ? CONSTRAINED_COLOR : AXES_COLOR[2]; | ||||
|     m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z; | ||||
|     ::memcpy((void*)m_grabbers[4].color, (ctrl_down && (m_hover_id == 5)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 4 * sizeof(float)); | ||||
|     ::memcpy((void*)m_grabbers[5].color, (ctrl_down && (m_hover_id == 4)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 4 * sizeof(float)); | ||||
|     m_grabbers[5].color = (ctrl_down && (m_hover_id == 4)) ? CONSTRAINED_COLOR : AXES_COLOR[2]; | ||||
| 
 | ||||
|     // uniform
 | ||||
|     m_grabbers[6].center = m_transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y; | ||||
|     m_grabbers[7].center = m_transform * Vec3d(m_box.max(0), m_box.min(1), center(2)) + offset_x - offset_y; | ||||
|     m_grabbers[8].center = m_transform * Vec3d(m_box.max(0), m_box.max(1), center(2)) + offset_x + offset_y; | ||||
|     m_grabbers[9].center = m_transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y; | ||||
|     for (int i = 6; i < 10; ++i) | ||||
|     { | ||||
|         ::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 4 * sizeof(float)); | ||||
|     for (int i = 6; i < 10; ++i) { | ||||
|         m_grabbers[i].color = m_highlight_color; | ||||
|     } | ||||
| 
 | ||||
|     // sets grabbers orientation
 | ||||
|     for (int i = 0; i < 10; ++i) | ||||
|     { | ||||
|     for (int i = 0; i < 10; ++i) { | ||||
|         m_grabbers[i].angles = angles; | ||||
|     } | ||||
| 
 | ||||
|  | @ -210,25 +206,21 @@ void GLGizmoScale3D::on_render() const | |||
| 
 | ||||
|     float grabber_mean_size = (float)((selection_box.size()(0) + selection_box.size()(1) + selection_box.size()(2)) / 3.0); | ||||
| 
 | ||||
|     if (m_hover_id == -1) | ||||
|     { | ||||
|     if (m_hover_id == -1) { | ||||
|         // draw connections
 | ||||
|         if (m_grabbers[0].enabled && m_grabbers[1].enabled) | ||||
|         { | ||||
|             glsafe(::glColor4fv(m_grabbers[0].color)); | ||||
|         if (m_grabbers[0].enabled && m_grabbers[1].enabled) { | ||||
|             glsafe(::glColor4fv(m_grabbers[0].color.data())); | ||||
|             render_grabbers_connection(0, 1); | ||||
|         } | ||||
|         if (m_grabbers[2].enabled && m_grabbers[3].enabled) | ||||
|         { | ||||
|             glsafe(::glColor4fv(m_grabbers[2].color)); | ||||
|         if (m_grabbers[2].enabled && m_grabbers[3].enabled) { | ||||
|             glsafe(::glColor4fv(m_grabbers[2].color.data())); | ||||
|             render_grabbers_connection(2, 3); | ||||
|         } | ||||
|         if (m_grabbers[4].enabled && m_grabbers[5].enabled) | ||||
|         { | ||||
|             glsafe(::glColor4fv(m_grabbers[4].color)); | ||||
|         if (m_grabbers[4].enabled && m_grabbers[5].enabled) { | ||||
|             glsafe(::glColor4fv(m_grabbers[4].color.data())); | ||||
|             render_grabbers_connection(4, 5); | ||||
|         } | ||||
|         glsafe(::glColor4fv(m_base_color)); | ||||
|         glsafe(::glColor4fv(m_base_color.data())); | ||||
|         render_grabbers_connection(6, 7); | ||||
|         render_grabbers_connection(7, 8); | ||||
|         render_grabbers_connection(8, 9); | ||||
|  | @ -236,45 +228,68 @@ void GLGizmoScale3D::on_render() const | |||
|         // draw grabbers
 | ||||
|         render_grabbers(grabber_mean_size); | ||||
|     } | ||||
|     else if ((m_hover_id == 0) || (m_hover_id == 1)) | ||||
|     { | ||||
|     else if (m_hover_id == 0 || m_hover_id == 1) { | ||||
|         // draw connection
 | ||||
|         glsafe(::glColor4fv(m_grabbers[0].color)); | ||||
|         glsafe(::glColor4fv(m_grabbers[0].color.data())); | ||||
|         render_grabbers_connection(0, 1); | ||||
|         // draw grabbers
 | ||||
|         m_grabbers[0].render(true, grabber_mean_size); | ||||
|         m_grabbers[1].render(true, grabber_mean_size); | ||||
| 
 | ||||
|         GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|         if (shader != nullptr) { | ||||
|             shader->start_using(); | ||||
|             shader->set_uniform("emission_factor", 0.1); | ||||
|             // draw grabbers
 | ||||
|             m_grabbers[0].render(true, grabber_mean_size); | ||||
|             m_grabbers[1].render(true, grabber_mean_size); | ||||
|             shader->stop_using(); | ||||
|         } | ||||
|     } | ||||
|     else if ((m_hover_id == 2) || (m_hover_id == 3)) | ||||
|     { | ||||
|     else if (m_hover_id == 2 || m_hover_id == 3) { | ||||
|         // draw connection
 | ||||
|         glsafe(::glColor4fv(m_grabbers[2].color)); | ||||
|         glsafe(::glColor4fv(m_grabbers[2].color.data())); | ||||
|         render_grabbers_connection(2, 3); | ||||
|         // draw grabbers
 | ||||
|         m_grabbers[2].render(true, grabber_mean_size); | ||||
|         m_grabbers[3].render(true, grabber_mean_size); | ||||
| 
 | ||||
|         GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|         if (shader != nullptr) { | ||||
|             shader->start_using(); | ||||
|             shader->set_uniform("emission_factor", 0.1); | ||||
|             // draw grabbers
 | ||||
|             m_grabbers[2].render(true, grabber_mean_size); | ||||
|             m_grabbers[3].render(true, grabber_mean_size); | ||||
|             shader->stop_using(); | ||||
|         } | ||||
|     } | ||||
|     else if ((m_hover_id == 4) || (m_hover_id == 5)) | ||||
|     { | ||||
|     else if (m_hover_id == 4 || m_hover_id == 5) { | ||||
|         // draw connection
 | ||||
|         glsafe(::glColor4fv(m_grabbers[4].color)); | ||||
|         glsafe(::glColor4fv(m_grabbers[4].color.data())); | ||||
|         render_grabbers_connection(4, 5); | ||||
|         // draw grabbers
 | ||||
|         m_grabbers[4].render(true, grabber_mean_size); | ||||
|         m_grabbers[5].render(true, grabber_mean_size); | ||||
| 
 | ||||
|         GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|         if (shader != nullptr) { | ||||
|             shader->start_using(); | ||||
|             shader->set_uniform("emission_factor", 0.1); | ||||
|             // draw grabbers
 | ||||
|             m_grabbers[4].render(true, grabber_mean_size); | ||||
|             m_grabbers[5].render(true, grabber_mean_size); | ||||
|             shader->stop_using(); | ||||
|         } | ||||
|     } | ||||
|     else if (m_hover_id >= 6) | ||||
|     { | ||||
|     else if (m_hover_id >= 6) { | ||||
|         // draw connection
 | ||||
|         glsafe(::glColor4fv(m_drag_color)); | ||||
|         glsafe(::glColor4fv(m_drag_color.data())); | ||||
|         render_grabbers_connection(6, 7); | ||||
|         render_grabbers_connection(7, 8); | ||||
|         render_grabbers_connection(8, 9); | ||||
|         render_grabbers_connection(9, 6); | ||||
|         // draw grabbers
 | ||||
|         for (int i = 6; i < 10; ++i) | ||||
|         { | ||||
|             m_grabbers[i].render(true, grabber_mean_size); | ||||
| 
 | ||||
|         GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); | ||||
|         if (shader != nullptr) { | ||||
|             shader->start_using(); | ||||
|             shader->set_uniform("emission_factor", 0.1); | ||||
|             // draw grabbers
 | ||||
|             for (int i = 6; i < 10; ++i) { | ||||
|                 m_grabbers[i].render(true, grabber_mean_size); | ||||
|             } | ||||
|             shader->stop_using(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -26,20 +26,9 @@ namespace GUI { | |||
| 
 | ||||
| GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||
|     , m_quadric(nullptr) | ||||
| {     | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         // using GLU_FILL does not work when the instance's transformation
 | ||||
|         // contains mirroring (normals are reverted)
 | ||||
|         ::gluQuadricDrawStyle(m_quadric, GLU_FILL); | ||||
| { | ||||
| } | ||||
| 
 | ||||
| GLGizmoSlaSupports::~GLGizmoSlaSupports() | ||||
| { | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluDeleteQuadric(m_quadric); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmoSlaSupports::on_init() | ||||
| { | ||||
|  | @ -100,7 +89,7 @@ void GLGizmoSlaSupports::on_render() const | |||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     if (m_quadric != nullptr && selection.is_from_single_instance()) | ||||
|     if (selection.is_from_single_instance()) | ||||
|         render_points(selection, false); | ||||
| 
 | ||||
|     m_selection_rectangle.render(m_parent); | ||||
|  | @ -114,14 +103,28 @@ void GLGizmoSlaSupports::on_render() const | |||
| void GLGizmoSlaSupports::on_render_for_picking() const | ||||
| { | ||||
|     const Selection& selection = m_parent.get_selection(); | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
|     //glsafe(::glEnable(GL_DEPTH_TEST));
 | ||||
|     render_points(selection, true); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const | ||||
| { | ||||
|     if (!picking) | ||||
|         glsafe(::glEnable(GL_LIGHTING)); | ||||
|     size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); | ||||
| 
 | ||||
|     bool has_points = (cache_size != 0); | ||||
|     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) | ||||
|         return; | ||||
| 
 | ||||
|     GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); | ||||
|     if (shader != nullptr) | ||||
|         shader->start_using(); | ||||
|     ScopeGuard guard([shader]() { | ||||
|         if (shader != nullptr) | ||||
|             shader->stop_using(); | ||||
|     }); | ||||
| 
 | ||||
|     const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||
|     const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); | ||||
|  | @ -132,8 +135,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|     glsafe(::glTranslated(0.0, 0.0, z_shift)); | ||||
|     glsafe(::glMultMatrixd(instance_matrix.data())); | ||||
| 
 | ||||
|     float render_color[4]; | ||||
|     size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); | ||||
|     std::array<float, 4> render_color; | ||||
|     for (size_t i = 0; i < cache_size; ++i) | ||||
|     { | ||||
|         const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; | ||||
|  | @ -143,34 +145,38 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|             continue; | ||||
| 
 | ||||
|         // First decide about the color of the point.
 | ||||
|         if (picking) { | ||||
|             std::array<float, 4> color = picking_color_component(i); | ||||
|             render_color[0] = color[0]; | ||||
|             render_color[1] = color[1]; | ||||
|             render_color[2] = color[2]; | ||||
| 	        render_color[3] = color[3]; | ||||
|         } | ||||
|         if (picking) | ||||
|             render_color = picking_color_component(i); | ||||
|         else { | ||||
|             render_color[3] = 1.f; | ||||
|             if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active
 | ||||
|                 render_color[0] = 0.f; | ||||
|                 render_color[1] = 1.0f; | ||||
|                 render_color[2] = 1.0f; | ||||
|             } | ||||
|             if ((size_t(m_hover_id) == i && m_editing_mode)) // ignore hover state unless editing mode is active
 | ||||
|                 render_color = { 0.f, 1.f, 1.f, 1.f }; | ||||
|             else { // neigher hover nor picking
 | ||||
|                 bool supports_new_island = m_lock_unique_islands && support_point.is_new_island; | ||||
|                 if (m_editing_mode) { | ||||
|                     render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f); | ||||
|                     render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f); | ||||
|                     render_color[2] = point_selected ? 0.3f : (supports_new_island ? 1.0f : 0.7f); | ||||
|                     if (point_selected) | ||||
|                         render_color = { 1.f, 0.3f, 0.3f, 1.f}; | ||||
|                     else | ||||
|                         if (supports_new_island) | ||||
|                             render_color = { 0.3f, 0.3f, 1.f, 1.f }; | ||||
|                         else | ||||
|                             render_color = { 0.7f, 0.7f, 0.7f, 1.f }; | ||||
|                 } | ||||
|                 else | ||||
|                     for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f; | ||||
|                     render_color = { 0.5f, 0.5f, 0.5f, 1.f }; | ||||
|             } | ||||
|         } | ||||
|         glsafe(::glColor4fv(render_color)); | ||||
|         float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; | ||||
|         glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); | ||||
|         if (shader && ! picking) { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             const_cast<GLModel*>(&m_cone)->set_color(-1, render_color); | ||||
|             const_cast<GLModel*>(&m_sphere)->set_color(-1, render_color); | ||||
| #else | ||||
|             shader->set_uniform("uniform_color", render_color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|             shader->set_uniform("emission_factor", 0.5); | ||||
|         } | ||||
|         else // picking
 | ||||
|             glsafe(::glColor4fv(render_color.data())); | ||||
| 
 | ||||
| 
 | ||||
|         // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
 | ||||
|         glsafe(::glPushMatrix()); | ||||
|  | @ -195,33 +201,42 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|             const double cone_radius = 0.25; // mm
 | ||||
|             const double cone_height = 0.75; | ||||
|             glsafe(::glPushMatrix()); | ||||
|             glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale)); | ||||
|             ::gluCylinder(m_quadric, 0., cone_radius, cone_height, 24, 1); | ||||
|             glsafe(::glTranslatef(0.f, 0.f, cone_height + support_point.head_front_radius * RenderPointScale)); | ||||
|             glsafe(::glPushMatrix()); | ||||
|             glsafe(::glRotated(180., 1., 0., 0.)); | ||||
|             glsafe(::glScaled(cone_radius, cone_radius, cone_height)); | ||||
|             m_cone.render(); | ||||
|             glsafe(::glPopMatrix()); | ||||
|             glsafe(::glTranslatef(0.f, 0.f, cone_height)); | ||||
|             ::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); | ||||
|             glsafe(::glPopMatrix()); | ||||
|         } | ||||
|         ::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); | ||||
| 
 | ||||
|         glsafe(::glPushMatrix()); | ||||
|         double radius = (double)support_point.head_front_radius * RenderPointScale; | ||||
|         glsafe(::glScaled(radius, radius, radius)); | ||||
|         m_sphere.render(); | ||||
|         glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|         if (vol->is_left_handed()) | ||||
|             glFrontFace(GL_CCW); | ||||
| 
 | ||||
|         glsafe(::glPopMatrix()); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         // Reset emissive component to zero (the default value)
 | ||||
|         float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f }; | ||||
|         glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); | ||||
|     } | ||||
| 
 | ||||
|     // Now render the drain holes:
 | ||||
|     //if (! m_c->has_drilled_mesh()) {
 | ||||
|     if (! m_c->hollowed_mesh()->get_hollowed_mesh()) { | ||||
|     if (has_holes && ! picking) { | ||||
|         render_color[0] = 0.7f; | ||||
|         render_color[1] = 0.7f; | ||||
|         render_color[2] = 0.7f; | ||||
|         render_color[3] = 0.7f; | ||||
|         glsafe(::glColor4fv(render_color)); | ||||
|         if (shader) { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             const_cast<GLModel*>(&m_cylinder)->set_color(-1, render_color); | ||||
| #else | ||||
|             shader->set_uniform("uniform_color", render_color); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|             shader->set_uniform("emission_factor", 0.5); | ||||
|         } | ||||
|         for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { | ||||
|             if (is_mesh_point_clipped(drain_hole.pos.cast<double>())) | ||||
|                 continue; | ||||
|  | @ -242,12 +257,8 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|             glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); | ||||
|             glsafe(::glPushMatrix()); | ||||
|             glsafe(::glTranslated(0., 0., -drain_hole.height)); | ||||
|             ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength, 24, 1); | ||||
|             glsafe(::glTranslated(0., 0., drain_hole.height + sla::HoleStickOutLength)); | ||||
|             ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); | ||||
|             glsafe(::glTranslated(0., 0., -drain_hole.height - sla::HoleStickOutLength)); | ||||
|             glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); | ||||
|             ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); | ||||
|             glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); | ||||
|             m_cylinder.render(); | ||||
|             glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|             if (vol->is_left_handed()) | ||||
|  | @ -256,9 +267,6 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glDisable(GL_LIGHTING)); | ||||
| 
 | ||||
|     glsafe(::glPopMatrix()); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,8 +27,6 @@ private: | |||
| 
 | ||||
|     const float RenderPointScale = 1.f; | ||||
| 
 | ||||
|     GLUquadricObj* m_quadric; | ||||
| 
 | ||||
|     class CacheEntry { | ||||
|     public: | ||||
|         CacheEntry() : | ||||
|  | @ -58,7 +56,7 @@ private: | |||
| 
 | ||||
| public: | ||||
|     GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||
|     ~GLGizmoSlaSupports() override; | ||||
|     virtual ~GLGizmoSlaSupports() = default; | ||||
|     void set_sla_support_data(ModelObject* model_object, const Selection& selection); | ||||
|     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); | ||||
|     void delete_selected_points(bool force = false); | ||||
|  |  | |||
|  | @ -781,11 +781,9 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) | |||
|             break; | ||||
|         } | ||||
| 
 | ||||
| #ifdef __APPLE__ | ||||
|         case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
 | ||||
| #else /* __APPLE__ */ | ||||
| 
 | ||||
|         case WXK_BACK: | ||||
|         case WXK_DELETE: | ||||
| #endif /* __APPLE__ */ | ||||
|         { | ||||
|             if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete)) | ||||
|                 processed = true; | ||||
|  |  | |||
|  | @ -9,6 +9,8 @@ | |||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
| #include <igl/unproject.h> | ||||
| 
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
|  | @ -122,14 +124,16 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const | |||
| void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, | ||||
|                                         Vec3d& point, Vec3d& direction) const | ||||
| { | ||||
|     const std::array<int, 4>& viewport = camera.get_viewport(); | ||||
|     const Transform3d& model_mat = camera.get_view_matrix(); | ||||
|     const Transform3d& proj_mat = camera.get_projection_matrix(); | ||||
|     Matrix4d modelview = camera.get_view_matrix().matrix(); | ||||
|     Matrix4d projection= camera.get_projection_matrix().matrix(); | ||||
|     Vec4i viewport(camera.get_viewport().data()); | ||||
| 
 | ||||
|     Vec3d pt1; | ||||
|     Vec3d pt2; | ||||
|     ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0., model_mat.data(), proj_mat.data(), viewport.data(), &pt1(0), &pt1(1), &pt1(2)); | ||||
|     ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1., model_mat.data(), proj_mat.data(), viewport.data(), &pt2(0), &pt2(1), &pt2(2)); | ||||
|     igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 0.), | ||||
|                    modelview, projection, viewport, pt1); | ||||
|     igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 1.), | ||||
|                    modelview, projection, viewport, pt2); | ||||
| 
 | ||||
|     Transform3d inv = trafo.inverse(); | ||||
|     pt1 = inv * pt1; | ||||
|  |  | |||
|  | @ -2730,6 +2730,10 @@ void Plater::priv::reset() | |||
|     reset_gcode_toolpaths(); | ||||
|     gcode_result.reset(); | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     view3D->get_canvas3d()->set_sequential_print_clearance(Polygons(), false); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
|     // Stop and reset the Print content.
 | ||||
|     this->background_process.reset(); | ||||
|     model.clear_objects(); | ||||
|  | @ -2894,12 +2898,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool | |||
| 
 | ||||
|     // If the update_background_process() was not called by the timer, kill the timer,
 | ||||
|     // so the update_restart_background_process() will not be called again in vain.
 | ||||
|     this->background_process_timer.Stop(); | ||||
|     background_process_timer.Stop(); | ||||
|     // Update the "out of print bed" state of ModelInstances.
 | ||||
|     this->update_print_volume_state(); | ||||
|     update_print_volume_state(); | ||||
|     // Apply new config to the possibly running background task.
 | ||||
|     bool               was_running = this->background_process.running(); | ||||
|     Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); | ||||
|     bool               was_running = background_process.running(); | ||||
|     Print::ApplyStatus invalidated = background_process.apply(q->model(), wxGetApp().preset_bundle->full_config()); | ||||
| 
 | ||||
|     // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
 | ||||
|     if (view3D->is_layers_editing_enabled()) | ||||
|  | @ -2908,40 +2912,56 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool | |||
|     if (invalidated == Print::APPLY_STATUS_INVALIDATED) { | ||||
|         // Some previously calculated data on the Print was invalidated.
 | ||||
|         // Hide the slicing results, as the current slicing status is no more valid.
 | ||||
|         this->sidebar->show_sliced_info_sizer(false); | ||||
|         sidebar->show_sliced_info_sizer(false); | ||||
|         // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
 | ||||
|         // Otherwise they will be just refreshed.
 | ||||
|         if (this->preview != nullptr) { | ||||
|         if (preview != nullptr) { | ||||
|             // If the preview is not visible, the following line just invalidates the preview,
 | ||||
|             // but the G-code paths or SLA preview are calculated first once the preview is made visible.
 | ||||
|             reset_gcode_toolpaths(); | ||||
|             this->preview->reload_print(); | ||||
|             preview->reload_print(); | ||||
|         } | ||||
|         // In FDM mode, we need to reload the 3D scene because of the wipe tower preview box.
 | ||||
|         // In SLA mode, we need to reload the 3D scene every time to show the support structures.
 | ||||
|         if (this->printer_technology == ptSLA || (this->printer_technology == ptFFF && this->config->opt_bool("wipe_tower"))) | ||||
|         if (printer_technology == ptSLA || (printer_technology == ptFFF && config->opt_bool("wipe_tower"))) | ||||
|             return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE; | ||||
|     } | ||||
| 
 | ||||
|     if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! this->background_process.empty()) { | ||||
|     if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! background_process.empty()) { | ||||
| 		// The delayed error message is no more valid.
 | ||||
| 		this->delayed_error_message.clear(); | ||||
| 		delayed_error_message.clear(); | ||||
| 		// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
 | ||||
|         std::string warning; | ||||
|         std::string err = this->background_process.validate(&warning); | ||||
|         std::string err = background_process.validate(&warning); | ||||
|         if (err.empty()) { | ||||
| 			notification_manager->set_all_slicing_errors_gray(true); | ||||
|             if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) | ||||
|             if (invalidated != Print::APPLY_STATUS_UNCHANGED && background_processing_enabled()) | ||||
|                 return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; | ||||
| 
 | ||||
|             // Pass a warning from validation and either show a notification,
 | ||||
|             // or hide the old one.
 | ||||
|             process_validation_warning(warning); | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             if (printer_technology == ptFFF) { | ||||
|                 view3D->get_canvas3d()->set_sequential_print_clearance(Polygons(), false); | ||||
|                 view3D->get_canvas3d()->set_as_dirty(); | ||||
|                 view3D->get_canvas3d()->request_extra_frame(); | ||||
|             } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|         } else { | ||||
| 			// The print is not valid.
 | ||||
| 			// Show error as notification.
 | ||||
|             notification_manager->push_slicing_error_notification(err); | ||||
|             return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|             if (printer_technology == ptFFF) { | ||||
|                 const Print* print = background_process.fff_print(); | ||||
|                 Polygons polygons; | ||||
|                 if (print->config().complete_objects) | ||||
|                     Print::sequential_print_horizontal_clearance_valid(*print, &polygons); | ||||
|                 view3D->get_canvas3d()->set_sequential_print_clearance(polygons, true); | ||||
|             } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|         } | ||||
| 
 | ||||
|     } else if (! this->delayed_error_message.empty()) { | ||||
|  | @ -3077,8 +3097,7 @@ void Plater::priv::update_fff_scene() | |||
|     if (this->preview != nullptr) | ||||
|         this->preview->reload_print(); | ||||
|     // In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
 | ||||
|     view3D->reload_scene(true); | ||||
| 	 | ||||
|     view3D->reload_scene(true);	 | ||||
| } | ||||
| 
 | ||||
| void Plater::priv::update_sla_scene() | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ | |||
| #include <boost/algorithm/string/predicate.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| static const float UNIFORM_SCALE_COLOR[4] = { 0.923f, 0.504f, 0.264f, 1.0f }; | ||||
| static const std::array<float, 4> UNIFORM_SCALE_COLOR = { 0.923f, 0.504f, 0.264f, 1.0f }; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
|  | @ -115,20 +115,8 @@ Selection::Selection() | |||
|     , m_scale_factor(1.0f) | ||||
| { | ||||
|     this->set_bounding_boxes_dirty(); | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluQuadricDrawStyle(m_quadric, GLU_FILL); | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
| Selection::~Selection() | ||||
| { | ||||
|     if (m_quadric != nullptr) | ||||
|         ::gluDeleteQuadric(m_quadric); | ||||
| } | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| 
 | ||||
| void Selection::set_volumes(GLVolumePtrs* volumes) | ||||
| { | ||||
|  | @ -141,6 +129,11 @@ bool Selection::init() | |||
| { | ||||
|     m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f)); | ||||
|     m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f)); | ||||
| 
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
|     m_vbo_sphere.init_from(make_sphere(0.75, 2*PI/24)); | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
|  | @ -1176,22 +1169,18 @@ void Selection::render(float scale_factor) const | |||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
| void Selection::render_center(bool gizmo_is_dragging) const | ||||
| { | ||||
|     if (!m_valid || is_empty() || m_quadric == nullptr) | ||||
|     if (!m_valid || is_empty()) | ||||
|         return; | ||||
| 
 | ||||
|     const Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); | ||||
| 
 | ||||
|     glsafe(::glDisable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_LIGHTING)); | ||||
| 
 | ||||
|     glsafe(::glColor3f(1.0f, 1.0f, 1.0f)); | ||||
|     glsafe(::glPushMatrix()); | ||||
|     glsafe(::glTranslated(center(0), center(1), center(2))); | ||||
|     glsafe(::gluSphere(m_quadric, 0.75, 32, 32)); | ||||
|     m_vbo_sphere.render(); | ||||
|     glsafe(::glPopMatrix()); | ||||
| 
 | ||||
|     glsafe(::glDisable(GL_LIGHTING)); | ||||
| } | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| 
 | ||||
|  | @ -1829,12 +1818,37 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons | |||
|     glsafe(::glEnd()); | ||||
| } | ||||
| 
 | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
| static std::array<float, 4> get_color(Axis axis) | ||||
| { | ||||
|     return { AXES_COLOR[axis][0], AXES_COLOR[axis][1], AXES_COLOR[axis][2], AXES_COLOR[axis][3] }; | ||||
| }; | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| 
 | ||||
| void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const | ||||
| { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     if (boost::ends_with(sidebar_field, "x")) { | ||||
|         glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); | ||||
|         const_cast<GLModel*>(&m_arrow)->set_color(-1, get_color(X)); | ||||
|         m_arrow.render(); | ||||
|     } | ||||
|     else if (boost::ends_with(sidebar_field, "y")) { | ||||
|         const_cast<GLModel*>(&m_arrow)->set_color(-1, get_color(Y)); | ||||
|         m_arrow.render(); | ||||
|     } | ||||
|     else if (boost::ends_with(sidebar_field, "z")) { | ||||
|         glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); | ||||
|         const_cast<GLModel*>(&m_arrow)->set_color(-1, get_color(Z)); | ||||
|         m_arrow.render(); | ||||
|     } | ||||
| #else | ||||
|     auto set_color = [](Axis axis) { | ||||
|         GLShaderProgram* shader = wxGetApp().get_current_shader(); | ||||
|         if (shader != nullptr) | ||||
|             shader->set_uniform("uniform_color", AXES_COLOR[axis], 4); | ||||
|         if (shader != nullptr) { | ||||
|             shader->set_uniform("uniform_color", AXES_COLOR[axis]); | ||||
|             shader->set_uniform("emission_factor", 0.0); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     if (boost::ends_with(sidebar_field, "x")) { | ||||
|  | @ -1849,14 +1863,39 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) | |||
|         glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); | ||||
|         m_arrow.render(); | ||||
|     } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| } | ||||
| 
 | ||||
| void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const | ||||
| { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|     auto render_sidebar_rotation_hint = [this]() { | ||||
|         m_curved_arrow.render(); | ||||
|         glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); | ||||
|         m_curved_arrow.render(); | ||||
|     }; | ||||
| 
 | ||||
|     if (boost::ends_with(sidebar_field, "x")) { | ||||
|         glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); | ||||
|         const_cast<GLModel*>(&m_curved_arrow)->set_color(-1, get_color(X)); | ||||
|         render_sidebar_rotation_hint(); | ||||
|     } | ||||
|     else if (boost::ends_with(sidebar_field, "y")) { | ||||
|         glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); | ||||
|         const_cast<GLModel*>(&m_curved_arrow)->set_color(-1, get_color(Y)); | ||||
|         render_sidebar_rotation_hint(); | ||||
|     } | ||||
|     else if (boost::ends_with(sidebar_field, "z")) { | ||||
|         const_cast<GLModel*>(&m_curved_arrow)->set_color(-1, get_color(Z)); | ||||
|         render_sidebar_rotation_hint(); | ||||
|     } | ||||
| #else | ||||
|     auto set_color = [](Axis axis) { | ||||
|         GLShaderProgram* shader = wxGetApp().get_current_shader(); | ||||
|         if (shader != nullptr) | ||||
|             shader->set_uniform("uniform_color", AXES_COLOR[axis], 4); | ||||
|         if (shader != nullptr) { | ||||
|             shader->set_uniform("uniform_color", AXES_COLOR[axis]); | ||||
|             shader->set_uniform("emission_factor", 0.0); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     auto render_sidebar_rotation_hint = [this]() { | ||||
|  | @ -1877,6 +1916,7 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) | |||
|         set_color(Z); | ||||
|         render_sidebar_rotation_hint(); | ||||
|     } | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
| } | ||||
| 
 | ||||
| void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const | ||||
|  | @ -1884,9 +1924,12 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con | |||
|     bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); | ||||
| 
 | ||||
|     auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis) { | ||||
| #if ENABLE_SEQUENTIAL_LIMITS | ||||
|         const_cast<GLModel*>(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis)); | ||||
| #endif // ENABLE_SEQUENTIAL_LIMITS
 | ||||
|         GLShaderProgram* shader = wxGetApp().get_current_shader(); | ||||
|         if (shader != nullptr) | ||||
|             shader->set_uniform("uniform_color", uniform_scale ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4); | ||||
|             shader->set_uniform("emission_factor", 0.0); | ||||
| 
 | ||||
|         glsafe(::glTranslated(0.0, 5.0, 0.0)); | ||||
|         m_arrow.render(); | ||||
|  |  | |||
|  | @ -5,11 +5,6 @@ | |||
| #include "libslic3r/Geometry.hpp" | ||||
| #include "GLModel.hpp" | ||||
| 
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
| class GLUquadric; | ||||
| typedef class GLUquadric GLUquadricObj; | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class Shader; | ||||
|  | @ -216,7 +211,7 @@ private: | |||
|     bool m_scaled_instance_bounding_box_dirty; | ||||
| 
 | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
|     GLUquadricObj* m_quadric; | ||||
|     GLModel m_vbo_sphere; | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| 
 | ||||
|     GLModel m_arrow; | ||||
|  | @ -226,9 +221,6 @@ private: | |||
| 
 | ||||
| public: | ||||
|     Selection(); | ||||
| #if ENABLE_RENDER_SELECTION_CENTER | ||||
|     ~Selection(); | ||||
| #endif // ENABLE_RENDER_SELECTION_CENTER
 | ||||
| 
 | ||||
|     void set_volumes(GLVolumePtrs* volumes); | ||||
|     bool init(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vojtech Bubnik
						Vojtech Bubnik