animation_animall: return to release: T68332 T63750 e6a1dfbe53be
[blender-addons.git] / io_mesh_stl / __init__.py
bloba5c61d9fc405b30c78fef1cdc8bd203cd7004aaa
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 #####
19 # <pep8-80 compliant>
21 bl_info = {
22 "name": "STL format",
23 "author": "Guillaume Bouchard (Guillaum)",
24 "version": (1, 1, 3),
25 "blender": (2, 80, 0),
26 "location": "File > Import-Export > Stl",
27 "description": "Import-Export STL files",
28 "warning": "",
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
37 """
38 Import-Export STL files (binary or ascii)
40 - Import automatically remove the doubles.
41 - Export can export with/without modifiers applied
43 Issues:
45 Import:
46 - Does not handle endien
47 """
49 if "bpy" in locals():
50 import importlib
51 if "stl_utils" in locals():
52 importlib.reload(stl_utils)
53 if "blender_utils" in locals():
54 importlib.reload(blender_utils)
56 import os
58 import bpy
59 from bpy.props import (
60 StringProperty,
61 BoolProperty,
62 CollectionProperty,
63 EnumProperty,
64 FloatProperty,
66 from bpy_extras.io_utils import (
67 ImportHelper,
68 ExportHelper,
69 orientation_helper,
70 axis_conversion,
72 from bpy.types import (
73 Operator,
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"
83 bl_options = {'UNDO'}
85 filename_ext = ".stl"
87 filter_glob: StringProperty(
88 default="*.stl",
89 options={'HIDDEN'},
91 files: CollectionProperty(
92 name="File Path",
93 type=OperatorFileListElement,
95 directory: StringProperty(
96 subtype='DIR_PATH',
99 global_scale: FloatProperty(
100 name="Scale",
101 soft_min=0.001, soft_max=1000.0,
102 min=1e-6, max=1e6,
103 default=1.0,
106 use_scene_unit: BoolProperty(
107 name="Scene Unit",
108 description="Apply current scene's unit (as defined by unit scale) to imported data",
109 default=False,
112 use_facet_normal: BoolProperty(
113 name="Facet Normals",
114 description="Use (import) facet normals (note that this will still give flat shading)",
115 default=False,
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)
137 if not paths:
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')
146 for path in paths:
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)
152 return {'FINISHED'}
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",
167 default=False,
169 global_scale: FloatProperty(
170 name="Scale",
171 min=0.01, max=1000.0,
172 default=1.0,
175 use_scene_unit: BoolProperty(
176 name="Scene Unit",
177 description="Apply current scene's unit (as defined by unit scale) to exported data",
178 default=False,
180 ascii: BoolProperty(
181 name="Ascii",
182 description="Save the file in ASCII file format",
183 default=False,
185 use_mesh_modifiers: BoolProperty(
186 name="Apply Modifiers",
187 description="Apply the modifiers before saving",
188 default=True,
190 batch_mode: EnumProperty(
191 name="Batch Mode",
192 items=(('OFF', "Off", "All data in one file"),
193 ('OBJECT', "Object", "Each object as a file"),
196 @property
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
203 import itertools
204 from mathutils import Matrix
205 keywords = self.as_keywords(ignore=("axis_forward",
206 "axis_up",
207 "use_selection",
208 "global_scale",
209 "check_existing",
210 "filter_glob",
211 "use_scene_unit",
212 "use_mesh_modifiers",
213 "batch_mode"
216 scene = context.scene
217 if self.use_selection:
218 data_seq = context.selected_objects
219 else:
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,
228 to_up=self.axis_up,
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)
234 for ob in data_seq)
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()
240 for ob in data_seq:
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)
245 return {'FINISHED'}
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)")
256 classes = (
257 ImportSTL,
258 ExportSTL
261 def register():
262 for cls in classes:
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)
269 def unregister():
270 for cls in classes:
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__":
278 register()