1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
23 "author": "Guillaume Bouchard (Guillaum)",
25 "blender": (2, 80, 0),
26 "location": "File > Import-Export > Stl",
27 "description": "Import-Export STL files",
29 "wiki_url": "https://docs.blender.org/manual/en/latest/addons/io_mesh_stl.html",
30 "support": 'OFFICIAL',
31 "category": "Import-Export",
35 # @todo write the wiki page
38 Import-Export STL files (binary or ascii)
40 - Import automatically remove the doubles.
41 - Export can export with/without modifiers applied
46 - Does not handle endien
51 if "stl_utils" in locals():
52 importlib
.reload(stl_utils
)
53 if "blender_utils" in locals():
54 importlib
.reload(blender_utils
)
59 from bpy
.props
import (
66 from bpy_extras
.io_utils
import (
72 from bpy
.types
import (
74 OperatorFileListElement
,
78 @orientation_helper(axis_forward
='Y', axis_up
='Z')
79 class ImportSTL(Operator
, ImportHelper
):
80 """Load STL triangle mesh data"""
81 bl_idname
= "import_mesh.stl"
82 bl_label
= "Import STL"
87 filter_glob
: StringProperty(
91 files
: CollectionProperty(
93 type=OperatorFileListElement
,
95 directory
: StringProperty(
99 global_scale
: FloatProperty(
101 soft_min
=0.001, soft_max
=1000.0,
106 use_scene_unit
: BoolProperty(
108 description
="Apply current scene's unit (as defined by unit scale) to imported data",
112 use_facet_normal
: BoolProperty(
113 name
="Facet Normals",
114 description
="Use (import) facet normals (note that this will still give flat shading)",
118 def execute(self
, context
):
119 from . import stl_utils
120 from . import blender_utils
121 from mathutils
import Matrix
123 paths
= [os
.path
.join(self
.directory
, name
.name
)
124 for name
in self
.files
]
126 scene
= context
.scene
128 # Take into account scene's unit scale, so that 1 inch in Blender gives 1 inch elsewhere! See T42000.
129 global_scale
= self
.global_scale
130 if scene
.unit_settings
.system
!= 'NONE' and self
.use_scene_unit
:
131 global_scale
/= scene
.unit_settings
.scale_length
133 global_matrix
= axis_conversion(from_forward
=self
.axis_forward
,
134 from_up
=self
.axis_up
,
135 ).to_4x4() @ Matrix
.Scale(global_scale
, 4)
138 paths
.append(self
.filepath
)
140 if bpy
.ops
.object.mode_set
.poll():
141 bpy
.ops
.object.mode_set(mode
='OBJECT')
143 if bpy
.ops
.object.select_all
.poll():
144 bpy
.ops
.object.select_all(action
='DESELECT')
147 objName
= bpy
.path
.display_name(os
.path
.basename(path
))
148 tris
, tri_nors
, pts
= stl_utils
.read_stl(path
)
149 tri_nors
= tri_nors
if self
.use_facet_normal
else None
150 blender_utils
.create_and_link_mesh(objName
, tris
, tri_nors
, pts
, global_matrix
)
155 @orientation_helper(axis_forward
='Y', axis_up
='Z')
156 class ExportSTL(Operator
, ExportHelper
):
157 """Save STL triangle mesh data from the active object"""
158 bl_idname
= "export_mesh.stl"
159 bl_label
= "Export STL"
161 filename_ext
= ".stl"
162 filter_glob
: StringProperty(default
="*.stl", options
={'HIDDEN'})
164 use_selection
: BoolProperty(
165 name
="Selection Only",
166 description
="Export selected objects only",
169 global_scale
: FloatProperty(
171 min=0.01, max=1000.0,
175 use_scene_unit
: BoolProperty(
177 description
="Apply current scene's unit (as defined by unit scale) to exported data",
182 description
="Save the file in ASCII file format",
185 use_mesh_modifiers
: BoolProperty(
186 name
="Apply Modifiers",
187 description
="Apply the modifiers before saving",
190 batch_mode
: EnumProperty(
192 items
=(('OFF', "Off", "All data in one file"),
193 ('OBJECT', "Object", "Each object as a file"),
197 def check_extension(self
):
198 return self
.batch_mode
== 'OFF'
200 def execute(self
, context
):
201 from . import stl_utils
202 from . import blender_utils
204 from mathutils
import Matrix
205 keywords
= self
.as_keywords(ignore
=("axis_forward",
212 "use_mesh_modifiers",
216 scene
= context
.scene
217 if self
.use_selection
:
218 data_seq
= context
.selected_objects
220 data_seq
= scene
.objects
222 # Take into account scene's unit scale, so that 1 inch in Blender gives 1 inch elsewhere! See T42000.
223 global_scale
= self
.global_scale
224 if scene
.unit_settings
.system
!= 'NONE' and self
.use_scene_unit
:
225 global_scale
*= scene
.unit_settings
.scale_length
227 global_matrix
= axis_conversion(to_forward
=self
.axis_forward
,
229 ).to_4x4() @ Matrix
.Scale(global_scale
, 4)
231 if self
.batch_mode
== 'OFF':
232 faces
= itertools
.chain
.from_iterable(
233 blender_utils
.faces_from_mesh(ob
, global_matrix
, self
.use_mesh_modifiers
)
236 stl_utils
.write_stl(faces
=faces
, **keywords
)
237 elif self
.batch_mode
== 'OBJECT':
238 prefix
= os
.path
.splitext(self
.filepath
)[0]
239 keywords_temp
= keywords
.copy()
241 faces
= blender_utils
.faces_from_mesh(ob
, global_matrix
, self
.use_mesh_modifiers
)
242 keywords_temp
["filepath"] = prefix
+ bpy
.path
.clean_name(ob
.name
) + ".stl"
243 stl_utils
.write_stl(faces
=faces
, **keywords_temp
)
248 def menu_import(self
, context
):
249 self
.layout
.operator(ImportSTL
.bl_idname
, text
="Stl (.stl)")
252 def menu_export(self
, context
):
253 self
.layout
.operator(ExportSTL
.bl_idname
, text
="Stl (.stl)")
263 bpy
.utils
.register_class(cls
)
265 bpy
.types
.TOPBAR_MT_file_import
.append(menu_import
)
266 bpy
.types
.TOPBAR_MT_file_export
.append(menu_export
)
271 bpy
.utils
.unregister_class(cls
)
273 bpy
.types
.TOPBAR_MT_file_import
.remove(menu_import
)
274 bpy
.types
.TOPBAR_MT_file_export
.remove(menu_export
)
277 if __name__
== "__main__":