Sun Position: Fix crash when Blender was started in background
[blender-addons.git] / io_scene_obj / __init__.py
blobcffb6d7f70aea7aae49bda4bd797dc84a51f72cc
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "Wavefront OBJ format (legacy)",
5 "author": "Campbell Barton, Bastien Montagne",
6 "version": (3, 9, 0),
7 "blender": (3, 0, 0),
8 "location": "File > Import-Export",
9 "description": "Import-Export OBJ, Import OBJ mesh, UVs, materials and textures",
10 "warning": "",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/scene_obj.html",
12 "support": 'OFFICIAL',
13 "category": "Import-Export",
16 if "bpy" in locals():
17 import importlib
18 if "import_obj" in locals():
19 importlib.reload(import_obj)
20 if "export_obj" in locals():
21 importlib.reload(export_obj)
24 import bpy
25 from bpy.props import (
26 BoolProperty,
27 FloatProperty,
28 StringProperty,
29 EnumProperty,
31 from bpy_extras.io_utils import (
32 ImportHelper,
33 ExportHelper,
34 orientation_helper,
35 path_reference_mode,
36 axis_conversion,
40 @orientation_helper(axis_forward='-Z', axis_up='Y')
41 class ImportOBJ(bpy.types.Operator, ImportHelper):
42 """Load a Wavefront OBJ File"""
43 bl_idname = "import_scene.obj"
44 bl_label = "Import OBJ"
45 bl_options = {'PRESET', 'UNDO'}
47 filename_ext = ".obj"
48 filter_glob: StringProperty(
49 default="*.obj;*.mtl",
50 options={'HIDDEN'},
53 use_edges: BoolProperty(
54 name="Lines",
55 description="Import lines and faces with 2 verts as edge",
56 default=True,
58 use_smooth_groups: BoolProperty(
59 name="Smooth Groups",
60 description="Surround smooth groups by sharp edges",
61 default=True,
64 use_split_objects: BoolProperty(
65 name="Object",
66 description="Import OBJ Objects into Blender Objects",
67 default=True,
69 use_split_groups: BoolProperty(
70 name="Group",
71 description="Import OBJ Groups into Blender Objects",
72 default=False,
75 use_groups_as_vgroups: BoolProperty(
76 name="Poly Groups",
77 description="Import OBJ groups as vertex groups",
78 default=False,
81 use_image_search: BoolProperty(
82 name="Image Search",
83 description="Search subdirs for any associated images "
84 "(Warning, may be slow)",
85 default=True,
88 split_mode: EnumProperty(
89 name="Split",
90 items=(
91 ('ON', "Split", "Split geometry, omits vertices unused by edges or faces"),
92 ('OFF', "Keep Vert Order", "Keep vertex order from file"),
96 global_clamp_size: FloatProperty(
97 name="Clamp Size",
98 description="Clamp bounds under this value (zero to disable)",
99 min=0.0, max=1000.0,
100 soft_min=0.0, soft_max=1000.0,
101 default=0.0,
104 def execute(self, context):
105 # print("Selected: " + context.active_object.name)
106 from . import import_obj
108 if self.split_mode == 'OFF':
109 self.use_split_objects = False
110 self.use_split_groups = False
111 else:
112 self.use_groups_as_vgroups = False
114 keywords = self.as_keywords(
115 ignore=(
116 "axis_forward",
117 "axis_up",
118 "filter_glob",
119 "split_mode",
123 global_matrix = axis_conversion(
124 from_forward=self.axis_forward,
125 from_up=self.axis_up,
126 ).to_4x4()
127 keywords["global_matrix"] = global_matrix
129 if bpy.data.is_saved and context.preferences.filepaths.use_relative_paths:
130 import os
131 keywords["relpath"] = os.path.dirname(bpy.data.filepath)
133 return import_obj.load(context, **keywords)
135 def draw(self, context):
136 pass
139 class OBJ_PT_import_include(bpy.types.Panel):
140 bl_space_type = 'FILE_BROWSER'
141 bl_region_type = 'TOOL_PROPS'
142 bl_label = "Include"
143 bl_parent_id = "FILE_PT_operator"
145 @classmethod
146 def poll(cls, context):
147 sfile = context.space_data
148 operator = sfile.active_operator
150 return operator.bl_idname == "IMPORT_SCENE_OT_obj"
152 def draw(self, context):
153 layout = self.layout
154 layout.use_property_split = True
155 layout.use_property_decorate = False # No animation.
157 sfile = context.space_data
158 operator = sfile.active_operator
160 layout.prop(operator, 'use_image_search')
161 layout.prop(operator, 'use_smooth_groups')
162 layout.prop(operator, 'use_edges')
165 class OBJ_PT_import_transform(bpy.types.Panel):
166 bl_space_type = 'FILE_BROWSER'
167 bl_region_type = 'TOOL_PROPS'
168 bl_label = "Transform"
169 bl_parent_id = "FILE_PT_operator"
171 @classmethod
172 def poll(cls, context):
173 sfile = context.space_data
174 operator = sfile.active_operator
176 return operator.bl_idname == "IMPORT_SCENE_OT_obj"
178 def draw(self, context):
179 layout = self.layout
180 layout.use_property_split = True
181 layout.use_property_decorate = False # No animation.
183 sfile = context.space_data
184 operator = sfile.active_operator
186 layout.prop(operator, "global_clamp_size")
187 layout.prop(operator, "axis_forward")
188 layout.prop(operator, "axis_up")
191 class OBJ_PT_import_geometry(bpy.types.Panel):
192 bl_space_type = 'FILE_BROWSER'
193 bl_region_type = 'TOOL_PROPS'
194 bl_label = "Geometry"
195 bl_parent_id = "FILE_PT_operator"
196 bl_options = {'DEFAULT_CLOSED'}
198 @classmethod
199 def poll(cls, context):
200 sfile = context.space_data
201 operator = sfile.active_operator
203 return operator.bl_idname == "IMPORT_SCENE_OT_obj"
205 def draw(self, context):
206 layout = self.layout
208 sfile = context.space_data
209 operator = sfile.active_operator
211 layout.row().prop(operator, "split_mode", expand=True)
213 layout.use_property_split = True
214 layout.use_property_decorate = False # No animation.
216 col = layout.column()
217 if operator.split_mode == 'ON':
218 col.prop(operator, "use_split_objects", text="Split by Object")
219 col.prop(operator, "use_split_groups", text="Split by Group")
220 else:
221 col.prop(operator, "use_groups_as_vgroups")
224 @orientation_helper(axis_forward='-Z', axis_up='Y')
225 class ExportOBJ(bpy.types.Operator, ExportHelper):
226 """Save a Wavefront OBJ File"""
228 bl_idname = "export_scene.obj"
229 bl_label = 'Export OBJ'
230 bl_options = {'PRESET'}
232 filename_ext = ".obj"
233 filter_glob: StringProperty(
234 default="*.obj;*.mtl",
235 options={'HIDDEN'},
238 # context group
239 use_selection: BoolProperty(
240 name="Selection Only",
241 description="Export selected objects only",
242 default=False,
244 use_animation: BoolProperty(
245 name="Animation",
246 description="Write out an OBJ for each frame",
247 default=False,
250 # object group
251 use_mesh_modifiers: BoolProperty(
252 name="Apply Modifiers",
253 description="Apply modifiers",
254 default=True,
256 # extra data group
257 use_edges: BoolProperty(
258 name="Include Edges",
259 description="",
260 default=True,
262 use_smooth_groups: BoolProperty(
263 name="Smooth Groups",
264 description="Write sharp edges as smooth groups",
265 default=False,
267 use_smooth_groups_bitflags: BoolProperty(
268 name="Bitflag Smooth Groups",
269 description="Same as 'Smooth Groups', but generate smooth groups IDs as bitflags "
270 "(produces at most 32 different smooth groups, usually much less)",
271 default=False,
273 use_normals: BoolProperty(
274 name="Write Normals",
275 description="Export one normal per vertex and per face, to represent flat faces and sharp edges",
276 default=True,
278 use_uvs: BoolProperty(
279 name="Include UVs",
280 description="Write out the active UV coordinates",
281 default=True,
283 use_materials: BoolProperty(
284 name="Write Materials",
285 description="Write out the MTL file",
286 default=True,
288 use_triangles: BoolProperty(
289 name="Triangulate Faces",
290 description="Convert all faces to triangles",
291 default=False,
293 use_nurbs: BoolProperty(
294 name="Write Nurbs",
295 description="Write nurbs curves as OBJ nurbs rather than "
296 "converting to geometry",
297 default=False,
299 use_vertex_groups: BoolProperty(
300 name="Polygroups",
301 description="",
302 default=False,
305 # grouping group
306 use_blen_objects: BoolProperty(
307 name="OBJ Objects",
308 description="Export Blender objects as OBJ objects",
309 default=True,
311 group_by_object: BoolProperty(
312 name="OBJ Groups",
313 description="Export Blender objects as OBJ groups",
314 default=False,
316 group_by_material: BoolProperty(
317 name="Material Groups",
318 description="Generate an OBJ group for each part of a geometry using a different material",
319 default=False,
321 keep_vertex_order: BoolProperty(
322 name="Keep Vertex Order",
323 description="",
324 default=False,
327 global_scale: FloatProperty(
328 name="Scale",
329 min=0.01, max=1000.0,
330 default=1.0,
333 path_mode: path_reference_mode
335 check_extension = True
337 def execute(self, context):
338 from . import export_obj
340 from mathutils import Matrix
341 keywords = self.as_keywords(
342 ignore=(
343 "axis_forward",
344 "axis_up",
345 "global_scale",
346 "check_existing",
347 "filter_glob",
351 global_matrix = (
352 Matrix.Scale(self.global_scale, 4) @
353 axis_conversion(
354 to_forward=self.axis_forward,
355 to_up=self.axis_up,
356 ).to_4x4()
359 keywords["global_matrix"] = global_matrix
360 return export_obj.save(context, **keywords)
362 def draw(self, context):
363 pass
366 class OBJ_PT_export_include(bpy.types.Panel):
367 bl_space_type = 'FILE_BROWSER'
368 bl_region_type = 'TOOL_PROPS'
369 bl_label = "Include"
370 bl_parent_id = "FILE_PT_operator"
372 @classmethod
373 def poll(cls, context):
374 sfile = context.space_data
375 operator = sfile.active_operator
377 return operator.bl_idname == "EXPORT_SCENE_OT_obj"
379 def draw(self, context):
380 layout = self.layout
381 layout.use_property_split = True
382 layout.use_property_decorate = False # No animation.
384 sfile = context.space_data
385 operator = sfile.active_operator
387 col = layout.column(heading="Limit to")
388 col.prop(operator, 'use_selection')
390 col = layout.column(heading="Objects as", align=True)
391 col.prop(operator, 'use_blen_objects')
392 col.prop(operator, 'group_by_object')
393 col.prop(operator, 'group_by_material')
395 layout.separator()
397 layout.prop(operator, 'use_animation')
400 class OBJ_PT_export_transform(bpy.types.Panel):
401 bl_space_type = 'FILE_BROWSER'
402 bl_region_type = 'TOOL_PROPS'
403 bl_label = "Transform"
404 bl_parent_id = "FILE_PT_operator"
406 @classmethod
407 def poll(cls, context):
408 sfile = context.space_data
409 operator = sfile.active_operator
411 return operator.bl_idname == "EXPORT_SCENE_OT_obj"
413 def draw(self, context):
414 layout = self.layout
415 layout.use_property_split = True
416 layout.use_property_decorate = False # No animation.
418 sfile = context.space_data
419 operator = sfile.active_operator
421 layout.prop(operator, 'global_scale')
422 layout.prop(operator, 'path_mode')
423 layout.prop(operator, 'axis_forward')
424 layout.prop(operator, 'axis_up')
427 class OBJ_PT_export_geometry(bpy.types.Panel):
428 bl_space_type = 'FILE_BROWSER'
429 bl_region_type = 'TOOL_PROPS'
430 bl_label = "Geometry"
431 bl_parent_id = "FILE_PT_operator"
432 bl_options = {'DEFAULT_CLOSED'}
434 @classmethod
435 def poll(cls, context):
436 sfile = context.space_data
437 operator = sfile.active_operator
439 return operator.bl_idname == "EXPORT_SCENE_OT_obj"
441 def draw(self, context):
442 layout = self.layout
443 layout.use_property_split = True
444 layout.use_property_decorate = False # No animation.
446 sfile = context.space_data
447 operator = sfile.active_operator
449 layout.prop(operator, 'use_mesh_modifiers')
450 layout.prop(operator, 'use_smooth_groups')
451 layout.prop(operator, 'use_smooth_groups_bitflags')
452 layout.prop(operator, 'use_normals')
453 layout.prop(operator, 'use_uvs')
454 layout.prop(operator, 'use_materials')
455 layout.prop(operator, 'use_triangles')
456 layout.prop(operator, 'use_nurbs', text="Curves as NURBS")
457 layout.prop(operator, 'use_vertex_groups')
458 layout.prop(operator, 'keep_vertex_order')
461 def menu_func_import(self, context):
462 self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj) (legacy)")
465 def menu_func_export(self, context):
466 self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj) (legacy)")
469 classes = (
470 ImportOBJ,
471 OBJ_PT_import_include,
472 OBJ_PT_import_transform,
473 OBJ_PT_import_geometry,
474 ExportOBJ,
475 OBJ_PT_export_include,
476 OBJ_PT_export_transform,
477 OBJ_PT_export_geometry,
481 def register():
482 for cls in classes:
483 bpy.utils.register_class(cls)
485 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
486 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
489 def unregister():
490 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
491 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
493 for cls in classes:
494 bpy.utils.unregister_class(cls)
497 if __name__ == "__main__":
498 register()