mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Tech ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL - Replace GLIndexedVertexArray with GLModel: GLVolume geometry + removed class GLIndexedVertexArray from codebase
(cherry picked from commit prusa3d/PrusaSlicer@1eac357739)
This commit is contained in:
		
							parent
							
								
									d85bbcba50
								
							
						
					
					
						commit
						d09dc36ff1
					
				
					 16 changed files with 765 additions and 1130 deletions
				
			
		|  | @ -2353,7 +2353,7 @@ int CLI::run(int argc, char **argv) | |||
|                             //    continue;
 | ||||
|                             for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { | ||||
|                                 const ModelInstance &model_instance = *model_object.instances[instance_idx]; | ||||
|                                 glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, true, false, true); | ||||
|                                 glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, false, true); | ||||
|                                 //glvolume_collection.volumes.back()->geometry_id = key.geometry_id;
 | ||||
|                                 std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00"; | ||||
| 
 | ||||
|  |  | |||
|  | @ -93,6 +93,9 @@ public: | |||
|     // Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
 | ||||
|     bool         all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const; | ||||
| 
 | ||||
|     const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; } | ||||
|     const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; } | ||||
| 
 | ||||
| private: | ||||
|     // Source definition of the print bed geometry (PrintConfig::printable_area)
 | ||||
|     std::vector<Vec2d>  m_bed_shape; | ||||
|  |  | |||
|  | @ -131,8 +131,8 @@ set(SLIC3R_GUI_SOURCES | |||
|     GUI/Gizmos/GLGizmoSimplify.hpp | ||||
|     GUI/Gizmos/GLGizmoMmuSegmentation.cpp | ||||
|     GUI/Gizmos/GLGizmoMmuSegmentation.hpp | ||||
|     GUI/Gizmos/GLGizmoFaceDetector.cpp | ||||
|     GUI/Gizmos/GLGizmoFaceDetector.hpp | ||||
|     #GUI/Gizmos/GLGizmoFaceDetector.cpp | ||||
|     #GUI/Gizmos/GLGizmoFaceDetector.hpp | ||||
|     GUI/Gizmos/GLGizmoSeam.cpp | ||||
|     GUI/Gizmos/GLGizmoSeam.hpp | ||||
|     GUI/Gizmos/GLGizmoText.cpp | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -57,207 +57,6 @@ using ModelObjectPtrs = std::vector<ModelObject*>; | |||
| // Return appropriate color based on the ModelVolume.
 | ||||
| extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume); | ||||
| 
 | ||||
| // A container for interleaved arrays of 3D vertices and normals,
 | ||||
| // possibly indexed by triangles and / or quads.
 | ||||
| class GLIndexedVertexArray { | ||||
| public: | ||||
|     // Only Eigen types of Nx16 size are vectorized. This bounding box will not be vectorized.
 | ||||
|     static_assert(sizeof(Eigen::AlignedBox<float, 3>) == 24, "Eigen::AlignedBox<float, 3> is not being vectorized, thus it does not need to be aligned"); | ||||
|     using BoundingBox = Eigen::AlignedBox<float, 3>; | ||||
| 
 | ||||
|     GLIndexedVertexArray() { m_bounding_box.setEmpty(); } | ||||
|     GLIndexedVertexArray(const GLIndexedVertexArray &rhs) : | ||||
|         vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved), | ||||
|         triangle_indices(rhs.triangle_indices), | ||||
|         quad_indices(rhs.quad_indices), | ||||
|         m_bounding_box(rhs.m_bounding_box) | ||||
|         { assert(!rhs.has_VBOs()); m_bounding_box.setEmpty(); } | ||||
|     GLIndexedVertexArray(GLIndexedVertexArray &&rhs) : | ||||
|         vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)), | ||||
|         triangle_indices(std::move(rhs.triangle_indices)), | ||||
|         quad_indices(std::move(rhs.quad_indices)), | ||||
|         m_bounding_box(rhs.m_bounding_box) | ||||
|         { assert(! rhs.has_VBOs()); } | ||||
| 
 | ||||
|     ~GLIndexedVertexArray() { release_geometry(); } | ||||
| 
 | ||||
|     GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs) | ||||
|     { | ||||
|         assert(vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         assert(triangle_indices_VBO_id == 0); | ||||
|         assert(quad_indices_VBO_id == 0); | ||||
|         assert(rhs.vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         assert(rhs.triangle_indices_VBO_id == 0); | ||||
|         assert(rhs.quad_indices_VBO_id == 0); | ||||
|         this->vertices_and_normals_interleaved 		 = rhs.vertices_and_normals_interleaved; | ||||
|         this->triangle_indices                 		 = rhs.triangle_indices; | ||||
|         this->quad_indices                     		 = rhs.quad_indices; | ||||
|         this->m_bounding_box                   		 = rhs.m_bounding_box; | ||||
|         this->vertices_and_normals_interleaved_size  = rhs.vertices_and_normals_interleaved_size; | ||||
|         this->triangle_indices_size                  = rhs.triangle_indices_size; | ||||
|         this->quad_indices_size                      = rhs.quad_indices_size; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     GLIndexedVertexArray& operator=(GLIndexedVertexArray &&rhs) | ||||
|     { | ||||
|         assert(vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         assert(triangle_indices_VBO_id == 0); | ||||
|         assert(quad_indices_VBO_id == 0); | ||||
|         assert(rhs.vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         assert(rhs.triangle_indices_VBO_id == 0); | ||||
|         assert(rhs.quad_indices_VBO_id == 0); | ||||
|         this->vertices_and_normals_interleaved 		 = std::move(rhs.vertices_and_normals_interleaved); | ||||
|         this->triangle_indices                 		 = std::move(rhs.triangle_indices); | ||||
|         this->quad_indices                     		 = std::move(rhs.quad_indices); | ||||
|         this->m_bounding_box                   		 = rhs.m_bounding_box; | ||||
|         this->vertices_and_normals_interleaved_size  = rhs.vertices_and_normals_interleaved_size; | ||||
|         this->triangle_indices_size                  = rhs.triangle_indices_size; | ||||
|         this->quad_indices_size                      = rhs.quad_indices_size; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     // Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x)
 | ||||
|     std::vector<float> vertices_and_normals_interleaved; | ||||
|     std::vector<int>   triangle_indices; | ||||
|     std::vector<int>   quad_indices; | ||||
| 
 | ||||
|     // When the geometry data is loaded into the graphics card as Vertex Buffer Objects,
 | ||||
|     // the above mentioned std::vectors are cleared and the following variables keep their original length.
 | ||||
|     size_t vertices_and_normals_interleaved_size{ 0 }; | ||||
|     size_t triangle_indices_size{ 0 }; | ||||
|     size_t quad_indices_size{ 0 }; | ||||
| 
 | ||||
|     // IDs of the Vertex Array Objects, into which the geometry has been loaded.
 | ||||
|     // Zero if the VBOs are not sent to GPU yet.
 | ||||
|     unsigned int       vertices_and_normals_interleaved_VBO_id{ 0 }; | ||||
|     unsigned int       triangle_indices_VBO_id{ 0 }; | ||||
|     unsigned int       quad_indices_VBO_id{ 0 }; | ||||
| 
 | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
|     void load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals = false); | ||||
|     void load_mesh(const TriangleMesh& mesh, bool smooth_normals = false) { this->load_mesh_full_shading(mesh, smooth_normals); } | ||||
| #else | ||||
|     void load_mesh_full_shading(const TriangleMesh& mesh); | ||||
|     void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); } | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
| 
 | ||||
|     void load_its_flat_shading(const indexed_triangle_set &its); | ||||
| 
 | ||||
|     inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } | ||||
| 
 | ||||
|     inline void reserve(size_t sz) { | ||||
|         this->vertices_and_normals_interleaved.reserve(sz * 6); | ||||
|         this->triangle_indices.reserve(sz * 3); | ||||
|         this->quad_indices.reserve(sz * 4); | ||||
|     } | ||||
| 
 | ||||
|     inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) { | ||||
|         assert(this->vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         if (this->vertices_and_normals_interleaved_VBO_id != 0) | ||||
|             return; | ||||
| 
 | ||||
|         if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) | ||||
|             this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); | ||||
|         this->vertices_and_normals_interleaved.emplace_back(nx); | ||||
|         this->vertices_and_normals_interleaved.emplace_back(ny); | ||||
|         this->vertices_and_normals_interleaved.emplace_back(nz); | ||||
|         this->vertices_and_normals_interleaved.emplace_back(x); | ||||
|         this->vertices_and_normals_interleaved.emplace_back(y); | ||||
|         this->vertices_and_normals_interleaved.emplace_back(z); | ||||
| 
 | ||||
|         this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size(); | ||||
|         m_bounding_box.extend(Vec3f(x, y, z)); | ||||
|     }; | ||||
| 
 | ||||
|     inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) { | ||||
|         push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); | ||||
|     } | ||||
| 
 | ||||
|     template<typename Derived, typename Derived2> | ||||
|     inline void push_geometry(const Eigen::MatrixBase<Derived>& p, const Eigen::MatrixBase<Derived2>& n) { | ||||
|         push_geometry(float(p(0)), float(p(1)), float(p(2)), float(n(0)), float(n(1)), float(n(2))); | ||||
|     } | ||||
| 
 | ||||
|     inline void push_triangle(int idx1, int idx2, int idx3) { | ||||
|         assert(this->vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         if (this->vertices_and_normals_interleaved_VBO_id != 0) | ||||
|             return; | ||||
| 
 | ||||
|         if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) | ||||
|             this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); | ||||
|         this->triangle_indices.emplace_back(idx1); | ||||
|         this->triangle_indices.emplace_back(idx2); | ||||
|         this->triangle_indices.emplace_back(idx3); | ||||
|         this->triangle_indices_size = this->triangle_indices.size(); | ||||
|     }; | ||||
| 
 | ||||
|     inline void push_quad(int idx1, int idx2, int idx3, int idx4) { | ||||
|         assert(this->vertices_and_normals_interleaved_VBO_id == 0); | ||||
|         if (this->vertices_and_normals_interleaved_VBO_id != 0) | ||||
|             return; | ||||
| 
 | ||||
|         if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity()) | ||||
|             this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4)); | ||||
|         this->quad_indices.emplace_back(idx1); | ||||
|         this->quad_indices.emplace_back(idx2); | ||||
|         this->quad_indices.emplace_back(idx3); | ||||
|         this->quad_indices.emplace_back(idx4); | ||||
|         this->quad_indices_size = this->quad_indices.size(); | ||||
|     }; | ||||
| 
 | ||||
|     // Finalize the initialization of the geometry & indices,
 | ||||
|     // upload the geometry and indices to OpenGL VBO objects
 | ||||
|     // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
 | ||||
|     void finalize_geometry(bool opengl_initialized); | ||||
|     // Release the geometry data, release OpenGL VBOs.
 | ||||
|     void release_geometry(); | ||||
| 
 | ||||
|     void render() const; | ||||
|     void render(const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& qverts_range) const; | ||||
| 
 | ||||
|     // Is there any geometry data stored?
 | ||||
|     bool empty() const { return vertices_and_normals_interleaved_size == 0; } | ||||
| 
 | ||||
|     void clear() { | ||||
|         this->vertices_and_normals_interleaved.clear(); | ||||
|         this->triangle_indices.clear(); | ||||
|         this->quad_indices.clear(); | ||||
|         vertices_and_normals_interleaved_size = 0; | ||||
|         triangle_indices_size = 0; | ||||
|         quad_indices_size = 0; | ||||
|         m_bounding_box.setEmpty(); | ||||
|     } | ||||
| 
 | ||||
|     // Shrink the internal storage to tighly fit the data stored.
 | ||||
|     void shrink_to_fit() { | ||||
|         this->vertices_and_normals_interleaved.shrink_to_fit(); | ||||
|         this->triangle_indices.shrink_to_fit(); | ||||
|         this->quad_indices.shrink_to_fit(); | ||||
|     } | ||||
| 
 | ||||
|     const BoundingBox& bounding_box() const { return m_bounding_box; } | ||||
| 
 | ||||
|     // Return an estimate of the memory consumed by this class.
 | ||||
|     size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); } | ||||
|     // Return an estimate of the memory held by GPU vertex buffers.
 | ||||
|     size_t gpu_memory_used() const | ||||
|     { | ||||
|     	size_t memsize = 0; | ||||
|     	if (this->vertices_and_normals_interleaved_VBO_id != 0) | ||||
|     		memsize += this->vertices_and_normals_interleaved_size * 4; | ||||
|     	if (this->triangle_indices_VBO_id != 0) | ||||
|     		memsize += this->triangle_indices_size * 4; | ||||
|     	if (this->quad_indices_VBO_id != 0) | ||||
|     		memsize += this->quad_indices_size * 4; | ||||
|     	return memsize; | ||||
|     } | ||||
|     size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } | ||||
| 
 | ||||
| private: | ||||
|     BoundingBox m_bounding_box; | ||||
| }; | ||||
| 
 | ||||
| class GLVolume { | ||||
| public: | ||||
|     std::string name; | ||||
|  | @ -400,15 +199,13 @@ public: | |||
|     // Is mouse or rectangle selection over this object to select/deselect it ?
 | ||||
|     EHoverState         	hover; | ||||
| 
 | ||||
|     // Interleaved triangles & normals with indexed triangles & quads.
 | ||||
|     GLIndexedVertexArray        indexed_vertex_array; | ||||
|     GUI::GLModel            model; | ||||
|     // BBS
 | ||||
|     mutable std::vector<GLIndexedVertexArray> mmuseg_ivas; | ||||
|     mutable std::vector<GUI::GLModel> mmuseg_models; | ||||
|     mutable ObjectBase::Timestamp       mmuseg_ts; | ||||
| 
 | ||||
|     // Ranges of triangle and quad indices to be rendered.
 | ||||
|     std::pair<size_t, size_t>   tverts_range; | ||||
|     std::pair<size_t, size_t>   qverts_range; | ||||
| 
 | ||||
|     // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
 | ||||
|     // of the extrusions per layer.
 | ||||
|  | @ -418,13 +215,7 @@ public: | |||
| 
 | ||||
|     // Bounding box of this volume, in unscaled coordinates.
 | ||||
|     BoundingBoxf3 bounding_box() const { | ||||
|         BoundingBoxf3 out; | ||||
|         if (! this->indexed_vertex_array.bounding_box().isEmpty()) { | ||||
|             out.min = this->indexed_vertex_array.bounding_box().min().cast<double>(); | ||||
|             out.max = this->indexed_vertex_array.bounding_box().max().cast<double>(); | ||||
|             out.defined = true; | ||||
|         }; | ||||
|         return out; | ||||
|         return this->model.get_bounding_box(); | ||||
|     } | ||||
| 
 | ||||
|     void set_color(const ColorRGBA& rgba)        { color = rgba; } | ||||
|  | @ -517,18 +308,15 @@ public: | |||
|     // convex hull
 | ||||
|     const TriangleMesh*  convex_hull() const { return m_convex_hull.get(); } | ||||
| 
 | ||||
|     bool                empty() const { return this->indexed_vertex_array.empty(); } | ||||
|     bool                empty() const { return this->model.is_empty(); } | ||||
| 
 | ||||
|     void                set_range(double low, double high); | ||||
| 
 | ||||
|     //BBS: add outline related logic and add virtual specifier
 | ||||
|     virtual void        render(bool with_outline = false) const; | ||||
|     virtual void        render(bool with_outline = false); | ||||
| 
 | ||||
|     //BBS: add simple render function for thumbnail
 | ||||
|     void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors) const; | ||||
| 
 | ||||
|     void                finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } | ||||
|     void                release_geometry() { this->indexed_vertex_array.release_geometry(); } | ||||
|     void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors); | ||||
| 
 | ||||
|     void                set_bounding_boxes_as_dirty() { | ||||
|         m_transformed_bounding_box.reset(); | ||||
|  | @ -545,11 +333,11 @@ public: | |||
| 
 | ||||
|     // Return an estimate of the memory consumed by this class.
 | ||||
|     size_t 				cpu_memory_used() const { | ||||
|     	//FIXME what to do wih m_convex_hull?
 | ||||
|     	return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t); | ||||
|         return sizeof(*this) + this->model.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + | ||||
|                this->offsets.capacity() * sizeof(size_t); | ||||
|     } | ||||
|     // Return an estimate of the memory held by GPU vertex buffers.
 | ||||
|     size_t 				gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); } | ||||
|     size_t 				gpu_memory_used() const { return this->model.gpu_memory_used(); } | ||||
|     size_t 				total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } | ||||
| }; | ||||
| 
 | ||||
|  | @ -557,9 +345,9 @@ public: | |||
| class GLWipeTowerVolume : public GLVolume { | ||||
| public: | ||||
|     GLWipeTowerVolume(const std::vector<ColorRGBA>& colors); | ||||
|     virtual void render(bool with_outline = false) const; | ||||
|     virtual void render(bool with_outline = false); | ||||
| 
 | ||||
|     std::vector<GLIndexedVertexArray> iva_per_colors; | ||||
|     std::vector<GUI::GLModel> model_per_colors; | ||||
|     bool                              IsTransparent(); | ||||
| 
 | ||||
| private: | ||||
|  | @ -627,17 +415,15 @@ public: | |||
|     ~GLVolumeCollection() { clear(); } | ||||
| 
 | ||||
|     std::vector<int> load_object( | ||||
|         const ModelObject 		*model_object, | ||||
|         const ModelObject* model_object, | ||||
|         int                      obj_idx, | ||||
|         const std::vector<int>	&instance_idxs, | ||||
|         bool 					 opengl_initialized); | ||||
|         const std::vector<int>& instance_idxs); | ||||
| 
 | ||||
|     int load_object_volume( | ||||
|         const ModelObject *model_object, | ||||
|         const ModelObject* model_object, | ||||
|         int                obj_idx, | ||||
|         int                volume_idx, | ||||
|         int                instance_idx, | ||||
|         bool 			   opengl_initialized, | ||||
|         bool               in_assemble_view = false, | ||||
|         bool               use_loaded_id = false); | ||||
| 
 | ||||
|  | @ -649,14 +435,13 @@ public: | |||
|         const std::vector<std::pair<size_t, size_t>>& instances, | ||||
|         SLAPrintObjectStep              milestone, | ||||
|         // Timestamp of the last change of the milestone
 | ||||
|         size_t                          timestamp, | ||||
|         bool 			   				opengl_initialized); | ||||
|         size_t                          timestamp); | ||||
| 
 | ||||
|     int load_wipe_tower_preview( | ||||
|         int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized); | ||||
|         int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); | ||||
| 
 | ||||
|     GLVolume* new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0); | ||||
|     GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0); | ||||
|     GLVolume* new_toolpath_volume(const ColorRGBA& rgba); | ||||
|     GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); | ||||
| 
 | ||||
|     int get_selection_support_threshold_angle(bool&) const; | ||||
|     // Render the volumes by OpenGL.
 | ||||
|  | @ -667,13 +452,6 @@ public: | |||
|                 std::function<bool(const GLVolume &)> filter_func  = std::function<bool(const GLVolume &)>(), | ||||
|                 bool with_outline = true) const; | ||||
| 
 | ||||
|     // Finalize the initialization of the geometry & indices,
 | ||||
|     // upload the geometry and indices to OpenGL VBO objects
 | ||||
|     // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
 | ||||
|     void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); } | ||||
|     // Release the geometry data assigned to the volumes.
 | ||||
|     // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them.
 | ||||
|     void release_geometry() { for (auto *v : volumes) v->release_geometry(); } | ||||
|     // Clear the geometry
 | ||||
|     void clear() { for (auto *v : volumes) delete v; volumes.clear(); } | ||||
| 
 | ||||
|  | @ -724,17 +502,13 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo | |||
| 
 | ||||
| struct _3DScene | ||||
| { | ||||
|     static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume); | ||||
|     static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume); | ||||
| 	static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume); | ||||
|     static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); | ||||
|     static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); | ||||
|     static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume); | ||||
|     static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry); | ||||
|     static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GUI::GLModel::Geometry& geometry); | ||||
|     static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); | ||||
|     static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); | ||||
|     static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); | ||||
|     static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); | ||||
|     static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -920,7 +920,7 @@ std::vector<int> GCodeViewer::get_plater_extruder() | |||
| 
 | ||||
| //BBS: always load shell at preview
 | ||||
| void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, const BuildVolume& build_volume, | ||||
|                 const std::vector<BoundingBoxf3>& exclude_bounding_box, bool initialized, ConfigOptionMode mode, bool only_gcode) | ||||
|                 const std::vector<BoundingBoxf3>& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode) | ||||
| { | ||||
|     // avoid processing if called with the same gcode_result
 | ||||
|     if (m_last_result_id == gcode_result.id) { | ||||
|  | @ -975,7 +975,7 @@ m_sequential_view.m_show_gcode_window = false; | |||
|     //BBS: always load shell at preview
 | ||||
|     /*if (wxGetApp().is_editor())
 | ||||
|     { | ||||
|         //load_shells(print, initialized);
 | ||||
|         load_shells(print); | ||||
|     } | ||||
|     else {*/ | ||||
|     //BBS: add only gcode mode
 | ||||
|  | @ -3088,9 +3088,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const | |||
| } | ||||
| 
 | ||||
| //BBS: always load shell when preview
 | ||||
| void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing) | ||||
| void GCodeViewer::load_shells(const Print& print, bool force_previewing) | ||||
| { | ||||
|     BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing; | ||||
|     BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": force_previewing=%1%") %force_previewing; | ||||
|     if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) { | ||||
|         //BBS: update force previewing logic
 | ||||
|         if (force_previewing) | ||||
|  | @ -3138,7 +3138,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p | |||
|         instance_ids.resize(instance_index); | ||||
| 
 | ||||
|         size_t current_volumes_count = m_shells.volumes.volumes.size(); | ||||
|         m_shells.volumes.load_object(model_obj, object_idx, instance_ids, initialized); | ||||
|         m_shells.volumes.load_object(model_obj, object_idx, instance_ids); | ||||
| 
 | ||||
|         // adjust shells' z if raft is present
 | ||||
|         const SlicingParameters& slicing_parameters = obj->slicing_parameters(); | ||||
|  | @ -4080,14 +4080,6 @@ void GCodeViewer::render_shells() | |||
|     if (shader == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     // when the background processing is enabled, it may happen that the shells data have been loaded
 | ||||
|     // before opengl has been initialized for the preview canvas.
 | ||||
|     // when this happens, the volumes' data have not been sent to gpu yet.
 | ||||
|     for (GLVolume* v : m_shells.volumes.volumes) { | ||||
|         if (!v->indexed_vertex_array.has_VBOs()) | ||||
|             v->finalize_geometry(true); | ||||
|     } | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| //    glsafe(::glDepthMask(GL_FALSE));
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -810,7 +810,7 @@ public: | |||
|     // extract rendering data from the given parameters
 | ||||
|     //BBS: add only gcode mode
 | ||||
|     void load(const GCodeProcessorResult& gcode_result, const Print& print, const BuildVolume& build_volume, | ||||
|             const std::vector<BoundingBoxf3>& exclude_bounding_box, bool initialized, ConfigOptionMode mode, bool only_gcode = false); | ||||
|             const std::vector<BoundingBoxf3>& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode = false); | ||||
|     // recalculate ranges in dependence of what is visible and sets tool/print colors
 | ||||
|     void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors); | ||||
|     void refresh_render_paths(); | ||||
|  | @ -820,7 +820,7 @@ public: | |||
|     void reset(); | ||||
|     //BBS: always load shell at preview
 | ||||
|     void reset_shell(); | ||||
|     void load_shells(const Print& print, bool initialized, bool force_previewing = false); | ||||
|     void load_shells(const Print& print, bool force_previewing = false); | ||||
|     void set_shells_on_preview(bool is_previewing) { m_shells.previewing = is_previewing; } | ||||
|     //BBS: add all plates filament statistics
 | ||||
|     void render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show = true) const; | ||||
|  | @ -900,7 +900,7 @@ public: | |||
| private: | ||||
|     void load_toolpaths(const GCodeProcessorResult& gcode_result, const BuildVolume& build_volume, const std::vector<BoundingBoxf3>& exclude_bounding_box); | ||||
|     //BBS: always load shell at preview
 | ||||
|     //void load_shells(const Print& print, bool initialized);
 | ||||
|     //void load_shells(const Print& print);
 | ||||
|     void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; | ||||
|     void render_toolpaths(); | ||||
|     void render_shells(); | ||||
|  |  | |||
|  | @ -97,10 +97,6 @@ void GLCanvas3D::load_render_colors() | |||
| 
 | ||||
| // Number of floats
 | ||||
| static constexpr const size_t MAX_VERTEX_BUFFER_SIZE     = 131072 * 6; // 3.15MB
 | ||||
| // Reserve size in number of floats.
 | ||||
| static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB
 | ||||
| // Reserve size in number of floats, maximum sum of all preallocated buffers.
 | ||||
| //static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX = 1024 * 1024 * 128 / 4; // 128MB
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
|  | @ -1175,10 +1171,6 @@ bool GLCanvas3D::init() | |||
|     if (m_main_toolbar.is_enabled()) | ||||
|         m_layers_editing.init(); | ||||
| 
 | ||||
|     // on linux the gl context is not valid until the canvas is not shown on screen
 | ||||
|     // we defer the geometry finalization of volumes until the first call to render()
 | ||||
|     m_volumes.finalize_geometry(true); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(info) <<__FUNCTION__<< ": before gizmo init"; | ||||
|     if (m_gizmos.is_enabled() && !m_gizmos.init()) | ||||
|         std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; | ||||
|  | @ -2136,7 +2128,7 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob | |||
|             instance_idxs.emplace_back(i); | ||||
|         } | ||||
|     } | ||||
|     return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_initialized); | ||||
|     return m_volumes.load_object(&model_object, obj_idx, instance_idxs); | ||||
| } | ||||
| 
 | ||||
| std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx) | ||||
|  | @ -2447,7 +2439,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                     // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh
 | ||||
|                     // later in this function.
 | ||||
|                     it->volume_idx = m_volumes.volumes.size(); | ||||
|                     m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_initialized, m_canvas_type == ECanvasType::CanvasAssembleView); | ||||
|                     m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_canvas_type == ECanvasType::CanvasAssembleView); | ||||
|                     m_volumes.volumes.back()->geometry_id = key.geometry_id; | ||||
|                     update_object_list = true; | ||||
|                 } else { | ||||
|  | @ -2504,31 +2496,32 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                         GLVolume &volume = *m_volumes.volumes[it->volume_idx]; | ||||
|                         if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { | ||||
|                         	// The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen.
 | ||||
|                             volume.indexed_vertex_array.release_geometry(); | ||||
|                         	if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                             volume.model.reset(); | ||||
|                             if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                                 TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); | ||||
| 	                            assert(! mesh.empty()); | ||||
|                                 mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
|                                 volume.indexed_vertex_array.load_mesh(mesh, true); | ||||
|                                 volume.model.init_from(mesh, true); | ||||
| #else | ||||
|                                 volume.indexed_vertex_array.load_mesh(mesh); | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
|                             } else { | ||||
| 	                        	// Reload the original volume.
 | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
|                                 volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); | ||||
| #else | ||||
|                                 volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); | ||||
|                                 volume.model.init_from(mesh); | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
|                             } | ||||
|                             else { | ||||
| 	                        	// Reload the original volume.
 | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
|                                 volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); | ||||
| #else | ||||
|                                 volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
|                             } | ||||
|                             volume.finalize_geometry(true); | ||||
| 	                    } | ||||
|                     	//FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable
 | ||||
|                     	// to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables
 | ||||
|                     	// of various concenrs (model vs. 3D print path).
 | ||||
|                     	volume.offsets = { state.step[istep].timestamp }; | ||||
|                     } else if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                     } | ||||
|                     else if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                         // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created.
 | ||||
| 						ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); | ||||
| 						auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); | ||||
|  | @ -2540,7 +2533,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                                 instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx)); | ||||
|                             else | ||||
|                                 shift_zs[object_idx] = 0.; | ||||
|                         } else { | ||||
|                         } | ||||
|                         else { | ||||
|                             // Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
 | ||||
|                             m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); | ||||
|                             m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); | ||||
|  | @ -2550,7 +2544,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
| 
 | ||||
|             for (size_t istep = 0; istep < sla_steps.size(); ++istep) | ||||
|                 if (!instances[istep].empty()) | ||||
|                     m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); | ||||
|                     m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); | ||||
|         } | ||||
| 
 | ||||
| 		// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
 | ||||
|  | @ -2614,7 +2608,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                 int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( | ||||
|                     1000 + plate_id, x + plate_origin(0), y + plate_origin(1), | ||||
|                     (float)wipe_tower_size(0), (float)wipe_tower_size(1), (float)wipe_tower_size(2), a, | ||||
|                     /*!print->is_step_done(psWipeTower)*/ true, brim_width, m_initialized); | ||||
|                     /*!print->is_step_done(psWipeTower)*/ true, brim_width); | ||||
|                 int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id]; | ||||
|                 if (volume_idx_wipe_tower_old != -1) | ||||
|                     map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; | ||||
|  | @ -2687,26 +2681,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|     m_dirty = true; | ||||
| } | ||||
| 
 | ||||
| static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized, size_t prealloc_size = VERTEX_BUFFER_RESERVE_SIZE) | ||||
| { | ||||
| 	// Assign the large pre-allocated buffers to the new GLVolume.
 | ||||
| 	vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array); | ||||
| 	// Copy the content back to the old GLVolume.
 | ||||
| 	vol_old.indexed_vertex_array = vol_new.indexed_vertex_array; | ||||
| 	// Clear the buffers, but keep them pre-allocated.
 | ||||
| 	vol_new.indexed_vertex_array.clear(); | ||||
| 	// Just make sure that clear did not clear the reserved memory.
 | ||||
| 	// Reserving number of vertices (3x position + 3x color)
 | ||||
| 	vol_new.indexed_vertex_array.reserve(prealloc_size / 6); | ||||
| 	// Finalize the old geometry, possibly move data to the graphics card.
 | ||||
| 	vol_old.finalize_geometry(gl_initialized); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::load_shells(const Print& print, bool force_previewing) | ||||
| { | ||||
|     if (m_initialized) | ||||
|     { | ||||
|         m_gcode_viewer.load_shells(print, m_initialized, force_previewing); | ||||
|         m_gcode_viewer.load_shells(print, force_previewing); | ||||
|         m_gcode_viewer.update_shells_color_by_extruder(m_config); | ||||
|     } | ||||
| } | ||||
|  | @ -2726,7 +2705,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co | |||
|     //when load gcode directly, it is too late
 | ||||
|     m_gcode_viewer.init(wxGetApp().get_mode(), wxGetApp().preset_bundle); | ||||
|     m_gcode_viewer.load(gcode_result, *this->fff_print(), wxGetApp().plater()->build_volume(), exclude_bounding_box, | ||||
|         m_initialized, wxGetApp().get_mode(), only_gcode); | ||||
|         wxGetApp().get_mode(), only_gcode); | ||||
| 
 | ||||
|     if (wxGetApp().is_editor()) { | ||||
|         //BBS: always load shell at preview, do this in load_shells
 | ||||
|  | @ -5637,7 +5616,7 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const | |||
|             curr_color = vol->color; | ||||
| 
 | ||||
|             ColorRGBA new_color = adjust_color_for_rendering(curr_color); | ||||
|             shader->set_uniform("uniform_color", new_color); | ||||
| 			vol->model.set_color(new_color); | ||||
|             shader->set_uniform("volume_world_matrix", vol->world_matrix()); | ||||
|             //BBS set all volume to orange
 | ||||
|             //shader->set_uniform("uniform_color", orange);
 | ||||
|  | @ -7192,6 +7171,10 @@ void GLCanvas3D::_render_style_editor() | |||
| 
 | ||||
| void GLCanvas3D::_render_volumes_for_picking() const | ||||
| { | ||||
|     GLShaderProgram* shader = wxGetApp().get_shader("flat"); | ||||
|     if (shader == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     // do not cull backfaces to show broken geometry, if any
 | ||||
|     glsafe(::glDisable(GL_CULL_FACE)); | ||||
| 
 | ||||
|  | @ -7209,8 +7192,10 @@ void GLCanvas3D::_render_volumes_for_picking() const | |||
|                 //BBS: remove the bed picking logic
 | ||||
|                 const unsigned int id = volume.second.first; | ||||
|                 //const unsigned int id = 1 + volume.second.first;
 | ||||
| 				glsafe(::glColor4fv(picking_decode(id).data())); | ||||
| 	            volume.first->render(); | ||||
|                 volume.first->model.set_color(picking_decode(id)); | ||||
|                 shader->start_using(); | ||||
|                 volume.first->render(); | ||||
|                 shader->stop_using(); | ||||
| 	        } | ||||
| 	} | ||||
| 
 | ||||
|  | @ -8384,22 +8369,23 @@ void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume) | |||
|     skirt_height = std::min(skirt_height, print_zs.size()); | ||||
|     print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); | ||||
| 
 | ||||
|     GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE); | ||||
|     GLVolume* volume = m_volumes.new_toolpath_volume(color); | ||||
|     GLModel::Geometry init_data; | ||||
|     init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; | ||||
|     for (size_t i = 0; i < skirt_height; ++ i) { | ||||
|         volume->print_zs.emplace_back(print_zs[i]); | ||||
|         volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size()); | ||||
|         volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size()); | ||||
|         volume->offsets.emplace_back(init_data.indices_count()); | ||||
|         //BBS: usage of m_brim are deleted
 | ||||
|         _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume); | ||||
|         _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), init_data); | ||||
|         // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
 | ||||
|         if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { | ||||
|         	GLVolume &vol = *volume; | ||||
|         if (init_data.vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { | ||||
|             volume->model.init_from(std::move(init_data)); | ||||
|             GLVolume &vol = *volume; | ||||
|             volume = m_volumes.new_toolpath_volume(vol.color); | ||||
|             reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); | ||||
|         } | ||||
|     } | ||||
|     volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box()); | ||||
|     volume->indexed_vertex_array.finalize_geometry(m_initialized); | ||||
|     volume->model.init_from(std::move(init_data)); | ||||
|     volume->is_outside = !contains(build_volume, volume->model); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values) | ||||
|  | @ -8571,7 +8557,10 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c | |||
|         // Allocate the volume before locking.
 | ||||
| 		GLVolume *volume = new GLVolume(color); | ||||
| 		volume->is_extrusion_path = true; | ||||
|     	tbb::spin_mutex::scoped_lock lock; | ||||
|         // to prevent sending data to gpu (in the main thread) while
 | ||||
|         // editing the model geometry
 | ||||
|         volume->model.disable_render(); | ||||
|         tbb::spin_mutex::scoped_lock lock; | ||||
|     	// Lock by ROII, so if the emplace_back() fails, the lock will be released.
 | ||||
|         lock.acquire(new_volume_mutex); | ||||
|         m_volumes.volumes.emplace_back(volume); | ||||
|  | @ -8583,31 +8572,36 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c | |||
|         tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size), | ||||
|         [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range<size_t>& range) { | ||||
|         GLVolumePtrs 		vols; | ||||
|         auto                volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { | ||||
|             return *vols[ctxt.color_by_color_print()? | ||||
|         std::vector<GLModel::Geometry> geometries; | ||||
|         auto select_geometry = [&ctxt, &geometries](size_t layer_idx, int extruder, int feature) -> GLModel::Geometry& { | ||||
|             return geometries[ctxt.color_by_color_print() ? | ||||
|                 ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : | ||||
| 				ctxt.color_by_tool() ? | ||||
| 					std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : | ||||
| 					feature | ||||
| 				]; | ||||
|                 ctxt.color_by_tool() ? | ||||
|                 std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : | ||||
|                 feature | ||||
|             ]; | ||||
|         }; | ||||
|         if (ctxt.color_by_color_print() || ctxt.color_by_tool()) { | ||||
|             for (size_t i = 0; i < ctxt.number_tools(); ++i) | ||||
|             for (size_t i = 0; i < ctxt.number_tools(); ++i) { | ||||
|                 vols.emplace_back(new_volume(ctxt.color_tool(i))); | ||||
|                 geometries.emplace_back(GLModel::Geometry()); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         else { | ||||
|             vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; | ||||
|         for (GLVolume *vol : vols) | ||||
| 			// Reserving number of vertices (3x position + 3x color)
 | ||||
|         	vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); | ||||
|             geometries = { GLModel::Geometry(), GLModel::Geometry(), GLModel::Geometry() }; | ||||
|         } | ||||
| 
 | ||||
|         assert(vols.size() == geometries.size()); | ||||
|         for (GLModel::Geometry& g : geometries) { | ||||
|             g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; | ||||
|         } | ||||
|         for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { | ||||
|             const Layer *layer = ctxt.layers[idx_layer]; | ||||
| 
 | ||||
|             if (is_selected_separate_extruder) | ||||
|             { | ||||
|             if (is_selected_separate_extruder) { | ||||
|                 bool at_least_one_has_correct_extruder = false; | ||||
|                 for (const LayerRegion* layerm : layer->regions()) | ||||
|                 { | ||||
|                 for (const LayerRegion* layerm : layer->regions()) { | ||||
|                     if (layerm->slices.surfaces.empty()) | ||||
|                         continue; | ||||
|                     const PrintRegionConfig& cfg = layerm->region().config(); | ||||
|  | @ -8622,12 +8616,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c | |||
|                     continue; | ||||
|             } | ||||
| 
 | ||||
|             for (GLVolume *vol : vols) | ||||
|             for (size_t i = 0; i < vols.size(); ++i) { | ||||
|                 GLVolume* vol = vols[i]; | ||||
|                 if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { | ||||
|                     vol->print_zs.emplace_back(layer->print_z); | ||||
|                     vol->offsets.emplace_back(vol->indexed_vertex_array.quad_indices.size()); | ||||
|                     vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size()); | ||||
|                     vol->offsets.emplace_back(geometries[i].indices_count()); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             for (const PrintInstance &instance : *ctxt.shifted_copies) { | ||||
|                 const Point © = instance.shift; | ||||
|                 for (const LayerRegion *layerm : layer->regions()) { | ||||
|  | @ -8641,18 +8637,16 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c | |||
|                     } | ||||
|                     if (ctxt.has_perimeters) | ||||
|                         _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, | ||||
|                         	volume(idx_layer, layerm->region().config().wall_filament.value, 0)); | ||||
|                         	select_geometry(idx_layer, layerm->region().config().wall_filament.value, 0)); | ||||
|                     if (ctxt.has_infill) { | ||||
|                         for (const ExtrusionEntity *ee : layerm->fills.entities) { | ||||
|                             // fill represents infill extrusions of a single island.
 | ||||
|                             const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); | ||||
|                             if (! fill->entities.empty()) | ||||
|                                 _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, | ||||
| 	                                volume(idx_layer, | ||||
| 		                                is_solid_infill(fill->entities.front()->role()) ? | ||||
| 			                                layerm->region().config().solid_infill_filament : | ||||
| 			                                layerm->region().config().sparse_infill_filament, | ||||
| 		                                1)); | ||||
|                                     select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ? | ||||
|                                                     layerm->region().config().solid_infill_filament : | ||||
|                                                     layerm->region().config().sparse_infill_filament, 1)); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | @ -8661,28 +8655,25 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c | |||
|                     if (support_layer) { | ||||
|                         for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) | ||||
|                             _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, | ||||
| 	                            volume(idx_layer, | ||||
| 		                            (extrusion_entity->role() == erSupportMaterial || | ||||
|                                      extrusion_entity->role() == erSupportTransition) ? | ||||
| 			                            support_layer->object()->config().support_filament : | ||||
| 			                            support_layer->object()->config().support_interface_filament, | ||||
| 		                            2)); | ||||
| 	                            select_geometry(idx_layer, (extrusion_entity->role() == erSupportMaterial || extrusion_entity->role() == erSupportTransition) ? | ||||
|                                                 support_layer->object()->config().support_filament : | ||||
|                                                 support_layer->object()->config().support_interface_filament, 2)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
 | ||||
| 	        for (size_t i = 0; i < vols.size(); ++i) { | ||||
| 	            GLVolume &vol = *vols[i]; | ||||
| 	            if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { | ||||
| 	                vols[i] = new_volume(vol.color); | ||||
| 	                reserve_new_volume_finalize_old_volume(*vols[i], vol, false); | ||||
| 	            } | ||||
|                 if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { | ||||
|                     vol.model.init_from(std::move(geometries[i])); | ||||
|                     vols[i] = new_volume(vol.color); | ||||
|                 } | ||||
| 	        } | ||||
|         } | ||||
|         for (GLVolume *vol : vols) | ||||
|         	// Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver,
 | ||||
|         	// but this code runs in parallel and the OpenGL driver is not thread safe.
 | ||||
|             vol->indexed_vertex_array.shrink_to_fit(); | ||||
|         for (size_t i = 0; i < vols.size(); ++i) { | ||||
|             if (!geometries[i].is_empty()) | ||||
|                 vols[i]->model.init_from(std::move(geometries[i])); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); | ||||
|  | @ -8697,8 +8688,9 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c | |||
|     } | ||||
|     for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { | ||||
|         GLVolume* v = m_volumes.volumes[i]; | ||||
|         v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); | ||||
|         v->indexed_vertex_array.finalize_geometry(m_initialized); | ||||
|         v->is_outside = !contains(build_volume, v->model); | ||||
|         // We are done editinig the model, now it can be sent to gpu
 | ||||
|         v->model.enable_render(); | ||||
|     } | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); | ||||
|  | @ -8718,10 +8710,10 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con | |||
| 
 | ||||
|     struct Ctxt | ||||
|     { | ||||
|         const Print                 *print; | ||||
|         const std::vector<ColorRGBA>* tool_colors; | ||||
|         Vec2f                        wipe_tower_pos; | ||||
|         float                        wipe_tower_angle; | ||||
|         const Print                  *print; | ||||
|         const std::vector<ColorRGBA> *tool_colors; | ||||
|         Vec2f                         wipe_tower_pos; | ||||
|         float                         wipe_tower_angle; | ||||
| 
 | ||||
|         static ColorRGBA color_support() { return ColorRGBA::GREENISH(); } | ||||
| 
 | ||||
|  | @ -8771,6 +8763,9 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con | |||
|     auto            new_volume = [this, &new_volume_mutex](const ColorRGBA& color) { | ||||
|         auto *volume = new GLVolume(color); | ||||
| 		volume->is_extrusion_path = true; | ||||
|         // to prevent sending data to gpu (in the main thread) while
 | ||||
|         // editing the model geometry
 | ||||
|         volume->model.disable_render(); | ||||
|         tbb::spin_mutex::scoped_lock lock; | ||||
|         lock.acquire(new_volume_mutex); | ||||
|         m_volumes.volumes.emplace_back(volume); | ||||
|  | @ -8784,23 +8779,29 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con | |||
|         [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { | ||||
|         // Bounding box of this slab of a wipe tower.
 | ||||
|         GLVolumePtrs vols; | ||||
|         std::vector<GLModel::Geometry> geometries; | ||||
|         if (ctxt.color_by_tool()) { | ||||
|             for (size_t i = 0; i < ctxt.number_tools(); ++i) | ||||
|             for (size_t i = 0; i < ctxt.number_tools(); ++i) { | ||||
|                 vols.emplace_back(new_volume(ctxt.color_tool(i))); | ||||
|                 geometries.emplace_back(GLModel::Geometry()); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         else { | ||||
|             vols = { new_volume(ctxt.color_support()) }; | ||||
|         for (GLVolume *volume : vols) | ||||
| 			// Reserving number of vertices (3x position + 3x color)
 | ||||
|             volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); | ||||
|             geometries = { GLModel::Geometry() }; | ||||
|         } | ||||
| 
 | ||||
|         assert(vols.size() == geometries.size()); | ||||
|         for (GLModel::Geometry& g : geometries) { | ||||
|             g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; | ||||
|         } | ||||
|         for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { | ||||
|             const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer); | ||||
|             for (size_t i = 0; i < vols.size(); ++i) { | ||||
|                 GLVolume &vol = *vols[i]; | ||||
|                 if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { | ||||
|                     vol.print_zs.emplace_back(layer.front().print_z); | ||||
|                     vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size()); | ||||
|                     vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size()); | ||||
|                     vol.offsets.emplace_back(geometries[i].indices_count()); | ||||
|                 } | ||||
|             } | ||||
|             for (const WipeTower::ToolChangeResult &extrusions : layer) { | ||||
|  | @ -8843,20 +8844,22 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con | |||
|                         e_prev = e; | ||||
|                     } | ||||
|                     _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, | ||||
|                         *vols[ctxt.volume_idx(e.tool, 0)]); | ||||
|                         geometries[ctxt.volume_idx(e.tool, 0)]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for (size_t i = 0; i < vols.size(); ++i) { | ||||
|             GLVolume &vol = *vols[i]; | ||||
|             if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { | ||||
|             if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { | ||||
|                 vol.model.init_from(std::move(geometries[i])); | ||||
|                 vols[i] = new_volume(vol.color); | ||||
|                 reserve_new_volume_finalize_old_volume(*vols[i], vol, false); | ||||
|             } | ||||
|         } | ||||
|         for (GLVolume *vol : vols) | ||||
|             vol->indexed_vertex_array.shrink_to_fit(); | ||||
|     }); | ||||
|         for (size_t i = 0; i < vols.size(); ++i) { | ||||
|             if (!geometries[i].is_empty()) | ||||
|                 vols[i]->model.init_from(std::move(geometries[i])); | ||||
|         } | ||||
|         }); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); | ||||
|     // Remove empty volumes from the newly added volumes.
 | ||||
|  | @ -8870,8 +8873,9 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con | |||
|     } | ||||
|     for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { | ||||
|         GLVolume* v = m_volumes.volumes[i]; | ||||
|         v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); | ||||
|         v->indexed_vertex_array.finalize_geometry(m_initialized); | ||||
|         v->is_outside = !contains(build_volume, v->model); | ||||
|         // We are done editinig the model, now it can be sent to gpu
 | ||||
|         v->model.enable_render(); | ||||
|     } | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); | ||||
|  | @ -8895,11 +8899,10 @@ void GLCanvas3D::_load_sla_shells() | |||
|         m_volumes.volumes.emplace_back(new GLVolume(color)); | ||||
|         GLVolume& v = *m_volumes.volumes.back(); | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
|         v.indexed_vertex_array.load_mesh(mesh, true); | ||||
|         v.model.init_from(mesh, true); | ||||
| #else | ||||
|         v.indexed_vertex_array.load_mesh(mesh); | ||||
|         v.model.init_from(mesh); | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
|         v.indexed_vertex_array.finalize_geometry(m_initialized); | ||||
|         v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; | ||||
|         v.composite_id.volume_id = volume_id; | ||||
|         v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0)); | ||||
|  |  | |||
|  | @ -8,15 +8,50 @@ | |||
| #include "libslic3r/TriangleMesh.hpp" | ||||
| #include "libslic3r/Model.hpp" | ||||
| #include "libslic3r/Polygon.hpp" | ||||
| #include "libslic3r/BuildVolume.hpp" | ||||
| #include "libslic3r/Geometry/ConvexHull.hpp" | ||||
| 
 | ||||
| #include <boost/filesystem/operations.hpp> | ||||
| #include <boost/algorithm/string/predicate.hpp> | ||||
| 
 | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
| #include <igl/per_face_normals.h> | ||||
| #include <igl/per_corner_normals.h> | ||||
| #include <igl/per_vertex_normals.h> | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
| 
 | ||||
| #include <GL/glew.h> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace GUI { | ||||
| 
 | ||||
| #if ENABLE_SMOOTH_NORMALS | ||||
| static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals) | ||||
| { | ||||
|     using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>; | ||||
|     using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>; | ||||
| 
 | ||||
|     std::vector<Vec3f> face_normals = its_face_normals(mesh.its); | ||||
| 
 | ||||
|     Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), | ||||
|         Eigen::Index(mesh.its.vertices.size()), 3).cast<double>(); | ||||
|     Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), | ||||
|         Eigen::Index(mesh.its.indices.size()), 3); | ||||
|     Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), | ||||
|         Eigen::Index(face_normals.size()), 3).cast<double>(); | ||||
|     Eigen::MatrixXd out_normals; | ||||
| 
 | ||||
|     igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); | ||||
| 
 | ||||
|     normals = std::vector<stl_normal>(mesh.its.vertices.size()); | ||||
|     for (size_t i = 0; i < mesh.its.indices.size(); ++i) { | ||||
|         for (size_t j = 0; j < 3; ++j) { | ||||
|             normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast<float>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif // ENABLE_SMOOTH_NORMALS
 | ||||
| 
 | ||||
| void GLModel::Geometry::reserve_vertices(size_t vertices_count) | ||||
| { | ||||
|     vertices.reserve(vertices_count * vertex_stride_floats(format)); | ||||
|  | @ -206,6 +241,35 @@ Vec2f GLModel::Geometry::extract_tex_coord_2(size_t id) const | |||
|     return { *(start + 0), *(start + 1) }; | ||||
| } | ||||
| 
 | ||||
| void GLModel::Geometry::set_vertex(size_t id, const Vec3f& position, const Vec3f& normal) | ||||
| { | ||||
|     assert(format.vertex_layout == EVertexLayout::P3N3); | ||||
|     assert(id < vertices_count()); | ||||
|     if (id < vertices_count()) { | ||||
|         float* start = &vertices[id * vertex_stride_floats(format)]; | ||||
|         *(start + 0) = position.x(); | ||||
|         *(start + 1) = position.y(); | ||||
|         *(start + 2) = position.z(); | ||||
|         *(start + 3) = normal.x(); | ||||
|         *(start + 4) = normal.y(); | ||||
|         *(start + 5) = normal.z(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GLModel::Geometry::set_ushort_index(size_t id, unsigned short index) | ||||
| { | ||||
|     assert(id < indices_count()); | ||||
|     if (id < indices_count()) | ||||
|         ::memcpy(indices.data() + id * sizeof(unsigned short), &index, sizeof(unsigned short)); | ||||
| } | ||||
| 
 | ||||
| void GLModel::Geometry::set_uint_index(size_t id, unsigned int index) | ||||
| { | ||||
|     assert(id < indices_count()); | ||||
|     if (id < indices_count()) | ||||
|         ::memcpy(indices.data() + id * sizeof(unsigned int), &index, sizeof(unsigned int)); | ||||
| } | ||||
| 
 | ||||
| unsigned int GLModel::Geometry::extract_uint_index(size_t id) const | ||||
| { | ||||
|     if (format.index_type != EIndexType::UINT) { | ||||
|  | @ -218,7 +282,7 @@ unsigned int GLModel::Geometry::extract_uint_index(size_t id) const | |||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     unsigned int ret = -1; | ||||
|     unsigned int ret = (unsigned int)-1; | ||||
|     ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned int)); | ||||
|     return ret; | ||||
| } | ||||
|  | @ -235,11 +299,21 @@ unsigned short GLModel::Geometry::extract_ushort_index(size_t id) const | |||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     unsigned short ret = -1; | ||||
|     unsigned short ret = (unsigned short)-1; | ||||
|     ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned short)); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void GLModel::Geometry::remove_vertex(size_t id) | ||||
| { | ||||
|     assert(id < vertices_count()); | ||||
|     if (id < vertices_count()) { | ||||
|         size_t stride = vertex_stride_floats(format); | ||||
|         std::vector<float>::iterator it = vertices.begin() + id * stride; | ||||
|         vertices.erase(it, it + stride); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| size_t GLModel::Geometry::vertex_stride_floats(const Format& format) | ||||
| { | ||||
|     switch (format.vertex_layout) | ||||
|  | @ -417,21 +491,24 @@ void GLModel::init_from(const indexed_triangle_set& its) | |||
|     } | ||||
| 
 | ||||
|     Geometry& data = m_render_data.geometry; | ||||
|     data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, Geometry::EIndexType::UINT }; | ||||
|     data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) }; | ||||
|     data.reserve_vertices(3 * its.indices.size()); | ||||
|     data.reserve_indices(3 * its.indices.size()); | ||||
| 
 | ||||
|     // vertices + indices
 | ||||
|     unsigned int vertices_counter = 0; | ||||
|     for (uint32_t i = 0; i < its.indices.size(); ++i) { | ||||
|         stl_triangle_vertex_indices face = its.indices[i]; | ||||
|         stl_vertex                  vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; | ||||
|         stl_vertex                  n = face_normal_normalized(vertex); | ||||
|         const stl_triangle_vertex_indices face = its.indices[i]; | ||||
|         const stl_vertex                  vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; | ||||
|         const stl_vertex                  n = face_normal_normalized(vertex); | ||||
|         for (size_t j = 0; j < 3; ++j) { | ||||
|             data.add_vertex(vertex[j], n); | ||||
|         } | ||||
|         vertices_counter += 3; | ||||
|         data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); | ||||
|         if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT) | ||||
|             data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1); | ||||
|         else | ||||
|             data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); | ||||
|     } | ||||
| 
 | ||||
|     // update bounding box
 | ||||
|  | @ -553,6 +630,17 @@ static GLenum get_index_type(const GLModel::Geometry::Format& format) | |||
| 
 | ||||
| void GLModel::render() | ||||
| { | ||||
|     render(std::make_pair<size_t, size_t>(0, indices_count())); | ||||
| } | ||||
| 
 | ||||
| void GLModel::render(const std::pair<size_t, size_t>& range) | ||||
| { | ||||
|     if (m_render_disabled) | ||||
|         return; | ||||
| 
 | ||||
|     if (range.second == range.first) | ||||
|         return; | ||||
| 
 | ||||
|     GLShaderProgram* shader = wxGetApp().get_current_shader(); | ||||
| 
 | ||||
|     if (shader == nullptr) | ||||
|  | @ -570,8 +658,8 @@ void GLModel::render() | |||
|     const GLenum index_type = get_index_type(data.format); | ||||
| 
 | ||||
|     const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); | ||||
|     const bool position  = Geometry::has_position(data.format); | ||||
|     const bool normal    = Geometry::has_normal(data.format); | ||||
|     const bool position = Geometry::has_position(data.format); | ||||
|     const bool normal = Geometry::has_normal(data.format); | ||||
|     const bool tex_coord = Geometry::has_tex_coord(data.format); | ||||
| 
 | ||||
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); | ||||
|  | @ -592,7 +680,7 @@ void GLModel::render() | |||
|     shader->set_uniform("uniform_color", data.color); | ||||
| 
 | ||||
|     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); | ||||
|     glsafe(::glDrawElements(mode, indices_count(), index_type, nullptr)); | ||||
|     glsafe(::glDrawElements(mode, range.second - range.first + 1, index_type, (const void*)(range.first * Geometry::index_stride_bytes(data.format)))); | ||||
|     glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); | ||||
| 
 | ||||
|     if (tex_coord) | ||||
|  | @ -721,6 +809,61 @@ static void append_triangle(GLModel::Geometry& data, unsigned short v1, unsigned | |||
|     data.add_ushort_index(v2); | ||||
|     data.add_ushort_index(v3); | ||||
| } | ||||
| 
 | ||||
| template<typename Fn> | ||||
| inline bool all_vertices_inside(const GLModel::Geometry& geometry, Fn fn) | ||||
| { | ||||
|     const size_t position_stride_floats = geometry.position_stride_floats(geometry.format); | ||||
|     const size_t position_offset_floats = geometry.position_offset_floats(geometry.format); | ||||
|     assert(position_stride_floats == 3); | ||||
|     if (geometry.vertices.empty() || position_stride_floats != 3) | ||||
|         return false; | ||||
| 
 | ||||
|     for (auto it = geometry.vertices.begin(); it != geometry.vertices.end(); ) { | ||||
|         it += position_offset_floats; | ||||
|         if (!fn({ *it, *(it + 1), *(it + 2) })) | ||||
|             return false; | ||||
|         it += (geometry.vertex_stride_floats(geometry.format) - position_offset_floats - position_stride_floats); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom) | ||||
| { | ||||
|     static constexpr const double epsilon = BuildVolume::BedEpsilon; | ||||
|     switch (volume.type()) { | ||||
|     case BuildVolume_Type::Rectangle: | ||||
|     { | ||||
|         BoundingBox3Base<Vec3d> build_volume = volume.bounding_volume().inflated(epsilon); | ||||
|         if (volume.printable_height() == 0.0) | ||||
|             build_volume.max.z() = std::numeric_limits<double>::max(); | ||||
|         if (ignore_bottom) | ||||
|             build_volume.min.z() = -std::numeric_limits<double>::max(); | ||||
|         const BoundingBoxf3& model_box = model.get_bounding_box(); | ||||
|         return build_volume.contains(model_box.min) && build_volume.contains(model_box.max); | ||||
|     } | ||||
|     case BuildVolume_Type::Circle: | ||||
|     { | ||||
|         const Geometry::Circled& circle = volume.circle(); | ||||
|         const Vec2f c = unscaled<float>(circle.center); | ||||
|         const float r = unscaled<double>(circle.radius) + float(epsilon); | ||||
|         const float r2 = sqr(r); | ||||
|         return volume.printable_height() == 0.0 ? | ||||
|             all_vertices_inside(model.get_geometry(), [c, r2](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2; }) : | ||||
| 
 | ||||
|             all_vertices_inside(model.get_geometry(), [c, r2, z = volume.printable_height() + epsilon](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; }); | ||||
|     } | ||||
|     case BuildVolume_Type::Convex: | ||||
|         //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
 | ||||
|     case BuildVolume_Type::Custom: | ||||
|         return volume.printable_height() == 0.0 ? | ||||
|             all_vertices_inside(model.get_geometry(), [&volume](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()); }) : | ||||
|             all_vertices_inside(model.get_geometry(), [&volume, z = volume.printable_height() + epsilon](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()) && p.z() <= z; }); | ||||
|     default: | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) | ||||
| { | ||||
|     resolution = std::max<unsigned short>(4, resolution); | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ namespace Slic3r { | |||
| class TriangleMesh; | ||||
| class Polygon; | ||||
| using Polygons = std::vector<Polygon>; | ||||
| class BuildVolume; | ||||
| 
 | ||||
| namespace GUI { | ||||
| 
 | ||||
|  | @ -69,6 +70,11 @@ namespace GUI { | |||
|             void add_vertex(const Vec3f& position, const Vec2f& tex_coord);  // EVertexLayout::P3T2
 | ||||
|             void add_vertex(const Vec3f& position, const Vec3f& normal);     // EVertexLayout::P3N3
 | ||||
| 
 | ||||
|             void set_vertex(size_t id, const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3
 | ||||
| 
 | ||||
|             void set_ushort_index(size_t id, unsigned short index); | ||||
|             void set_uint_index(size_t id, unsigned int index); | ||||
| 
 | ||||
|             void add_ushort_index(unsigned short id); | ||||
|             void add_uint_index(unsigned int id); | ||||
| 
 | ||||
|  | @ -86,7 +92,9 @@ namespace GUI { | |||
|             unsigned int extract_uint_index(size_t id) const; | ||||
|             unsigned short extract_ushort_index(size_t id) const; | ||||
| 
 | ||||
|             bool is_empty() const { return vertices.empty() || indices.empty(); } | ||||
|             void remove_vertex(size_t id); | ||||
| 
 | ||||
|             bool is_empty() const { return vertices_count() == 0 || indices_count() == 0; } | ||||
| 
 | ||||
|             size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); } | ||||
|             size_t indices_count() const  { return indices.size() / index_stride_bytes(format); } | ||||
|  | @ -133,6 +141,14 @@ namespace GUI { | |||
|     private: | ||||
|         RenderData m_render_data; | ||||
| 
 | ||||
|         // By default the vertex and index buffers data are sent to gpu at the first call to render() method.
 | ||||
|         // If you need to initialize a model from outside the main thread, so that a call to render() may happen
 | ||||
|         // before the initialization is complete, use the methods:
 | ||||
|         // disable_render()
 | ||||
|         // ... do your initialization ...
 | ||||
|         // enable_render()
 | ||||
|         // to keep the data on cpu side until needed.
 | ||||
|         bool m_render_disabled{ false }; | ||||
|         BoundingBoxf3 m_bounding_box; | ||||
|         std::string m_filename; | ||||
| 
 | ||||
|  | @ -150,6 +166,7 @@ namespace GUI { | |||
| 
 | ||||
|         size_t indices_size_bytes() const { return indices_count() * Geometry::index_stride_bytes(m_render_data.geometry.format); } | ||||
| 
 | ||||
|         const Geometry& get_geometry() const { return m_render_data.geometry; } | ||||
|         void init_from(Geometry&& data); | ||||
|         void init_from(const TriangleMesh& mesh); | ||||
|         void init_from(const indexed_triangle_set& its); | ||||
|  | @ -161,16 +178,40 @@ namespace GUI { | |||
| 
 | ||||
|         void reset(); | ||||
|         void render(); | ||||
|         void render(const std::pair<size_t, size_t>& range); | ||||
|         void render_instanced(unsigned int instances_vbo, unsigned int instances_count); | ||||
| 
 | ||||
|         bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; } | ||||
|         bool is_empty() const { return m_render_data.geometry.is_empty(); } | ||||
| 
 | ||||
|         const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } | ||||
|         const std::string& get_filename() const { return m_filename; } | ||||
| 
 | ||||
|         bool is_render_disabled() const { return m_render_disabled; } | ||||
|         void enable_render() { m_render_disabled = false; } | ||||
|         void disable_render() { m_render_disabled = true; } | ||||
| 
 | ||||
|         size_t cpu_memory_used() const { | ||||
|             size_t ret = 0; | ||||
|             if (!m_render_data.geometry.vertices.empty()) | ||||
|                 ret += vertices_size_bytes(); | ||||
|             if (!m_render_data.geometry.indices.empty()) | ||||
|                 ret += indices_size_bytes(); | ||||
|             return ret; | ||||
|         } | ||||
|         size_t gpu_memory_used() const { | ||||
|             size_t ret = 0; | ||||
|             if (m_render_data.geometry.vertices.empty()) | ||||
|                 ret += vertices_size_bytes(); | ||||
|             if (m_render_data.geometry.indices.empty()) | ||||
|                 ret += indices_size_bytes(); | ||||
|             return ret; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         bool send_to_gpu(); | ||||
|     }; | ||||
|     bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom = true); | ||||
| 
 | ||||
|     // 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
 | ||||
|  |  | |||
|  | @ -32,9 +32,9 @@ std::string GLGizmoFaceDetector::on_get_name() const | |||
| 
 | ||||
| void GLGizmoFaceDetector::on_render() | ||||
| { | ||||
|     if (m_iva.has_VBOs()) { | ||||
|         ::glColor4f(0.f, 0.f, 1.f, 0.4f); | ||||
|         m_iva.render(); | ||||
|     if (model.is_initialized()) { | ||||
|         model.set_color({0.f, 0.f, 1.f, 0.4f}); | ||||
|         model.render(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -72,7 +72,7 @@ void GLGizmoFaceDetector::on_render_input_window(float x, float y, float bottom_ | |||
| void GLGizmoFaceDetector::on_set_state() | ||||
| { | ||||
|     if (get_state() == On) { | ||||
|         m_iva.release_geometry(); | ||||
|         model.reset(); | ||||
|         display_exterior_face(); | ||||
|     } | ||||
| } | ||||
|  | @ -94,7 +94,10 @@ void GLGizmoFaceDetector::perform_recognition(const Selection& selection) | |||
| void GLGizmoFaceDetector::display_exterior_face() | ||||
| { | ||||
|     int cnt = 0; | ||||
|     m_iva.release_geometry(); | ||||
|     model.reset(); | ||||
| 
 | ||||
|     GLModel::Geometry init_data; | ||||
|     init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; | ||||
| 
 | ||||
|     const ModelObjectPtrs& objects = wxGetApp().model().objects; | ||||
|     for (ModelObject* mo : objects) { | ||||
|  | @ -110,19 +113,15 @@ void GLGizmoFaceDetector::display_exterior_face() | |||
|                     continue; | ||||
| 
 | ||||
|                 for (int i = 0; i < 3; ++i) { | ||||
|                     m_iva.push_geometry(double(mv_its.vertices[facet_vert_idxs[i]](0)), | ||||
|                         double(mv_its.vertices[facet_vert_idxs[i]](1)), | ||||
|                         double(mv_its.vertices[facet_vert_idxs[i]](2)), | ||||
|                         0., 0., 1.); | ||||
|                     init_data.add_vertex((Vec3f) mv_its.vertices[facet_vert_idxs[i]].cast<float>(), Vec3f{0.0f, 0.0f, 1.0f}); | ||||
|                 } | ||||
| 
 | ||||
|                 m_iva.push_triangle(cnt, cnt + 1, cnt + 2); | ||||
|                 init_data.add_uint_triangle(cnt, cnt + 1, cnt + 2); | ||||
|                 cnt += 3; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     m_iva.finalize_geometry(true); | ||||
|     model.init_from(std::move(init_data)); | ||||
| } | ||||
| 
 | ||||
| CommonGizmosDataID GLGizmoFaceDetector::on_get_requirements() const | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ private: | |||
|     void perform_recognition(const Selection& selection); | ||||
|     void display_exterior_face(); | ||||
| 
 | ||||
|     GLIndexedVertexArray m_iva; | ||||
|     GUI::GLModel model; | ||||
|     double m_sample_interval = {0.5}; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -894,13 +894,16 @@ void GLGizmoFdmSupports::run_thread() | |||
|             print->set_status(100, L("Support Generated")); | ||||
|             goto _finished; | ||||
|         } | ||||
|         GLModel::Geometry init_data; | ||||
|         init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; | ||||
|         for (const SupportLayer *support_layer : m_print_instance.print_object->support_layers()) | ||||
|         { | ||||
|             for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) | ||||
|             { | ||||
|                 _3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, *m_support_volume); | ||||
|                 _3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, init_data); | ||||
|             } | ||||
|         } | ||||
|         m_support_volume->model.init_from(std::move(init_data)); | ||||
|         BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished extrusionentity_to_verts, update status to 100%"; | ||||
|         print->set_status(100, L("Support Generated")); | ||||
|          | ||||
|  | @ -926,7 +929,6 @@ _finished: | |||
| void GLGizmoFdmSupports::generate_support_volume() | ||||
| { | ||||
|     BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",before finalize_geometry"; | ||||
|     m_support_volume->indexed_vertex_array.finalize_geometry(m_parent.is_initialized()); | ||||
| 
 | ||||
|     std::unique_lock<std::mutex> lck(m_mutex); | ||||
|     m_volume_ready = true; | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" | ||||
| // BBS
 | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp" | ||||
| //#include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp"
 | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" | ||||
| #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" | ||||
|  |  | |||
|  | @ -2388,8 +2388,8 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) | |||
| { | ||||
|     // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
 | ||||
|     ImGuiIO& io = ImGui::GetIO(); | ||||
|     int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); | ||||
|     int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); | ||||
|     const int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); | ||||
|     const int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); | ||||
|     if (fb_width == 0 || fb_height == 0) | ||||
|         return; | ||||
|     draw_data->ScaleClipRects(io.DisplayFramebufferScale); | ||||
|  | @ -2432,8 +2432,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) | |||
| 
 | ||||
|     // Render command lists
 | ||||
|     ImVec2 pos = draw_data->DisplayPos; | ||||
|     for (int n = 0; n < draw_data->CmdListsCount; n++) | ||||
|     { | ||||
|     for (int n = 0; n < draw_data->CmdListsCount; ++n) { | ||||
|         const ImDrawList* cmd_list = draw_data->CmdLists[n]; | ||||
|         const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; | ||||
|         const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; | ||||
|  | @ -2441,19 +2440,14 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) | |||
|         glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)))); | ||||
|         glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)))); | ||||
| 
 | ||||
|         for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) | ||||
|         { | ||||
|         for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; ++cmd_i) { | ||||
|             const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; | ||||
|             if (pcmd->UserCallback) | ||||
|             { | ||||
|                 // User callback (registered via ImDrawList::AddCallback)
 | ||||
|                 pcmd->UserCallback(cmd_list, pcmd); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|             else { | ||||
|                 ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); | ||||
|                 if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) | ||||
|                 { | ||||
|                 if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { | ||||
|                     // Apply scissor/clipping rectangle
 | ||||
|                     glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y))); | ||||
| 
 | ||||
|  |  | |||
|  | @ -926,7 +926,7 @@ void CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f | |||
|                 const ModelVolume &model_volume = *model_object.volumes[volume_idx]; | ||||
|                 for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { | ||||
|                     const ModelInstance &model_instance = *model_object.instances[instance_idx]; | ||||
|                     glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, true, false, true); | ||||
|                     glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, false, true); | ||||
|                     glvolume_collection.volumes.back()->set_render_color(new_color); | ||||
|                     glvolume_collection.volumes.back()->set_color(new_color); | ||||
|                     //glvolume_collection.volumes.back()->printable = model_instance.printable;
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 enricoturri1966
						enricoturri1966