Fix io_anim_camera error exporting cameras with quotes in their name
[blender-addons.git] / io_scene_obj / __init__.py
blob58f60ba102639eaa7a47f10b059a1d153d1d1e7f
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # <pep8-80 compliant>
5 bl_info = {
6 "name": "Wavefront OBJ format",
7 "author": "Campbell Barton, Bastien Montagne",
8 "version": (3, 9, 0),
9 "blender": (3, 0, 0),
10 "location": "File > Import-Export",
11 "description": "Import-Export OBJ, Import OBJ mesh, UV's, materials and textures",
12 "warning": "",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/scene_obj.html",
14 "support": 'OFFICIAL',
15 "category": "Import-Export",
18 if "bpy" in locals():
19 import importlib
20 if "import_obj" in locals():
21 importlib.reload(import_obj)
22 if "export_obj" in locals():
23 importlib.reload(export_obj)
26 import bpy
27 from bpy.props import (
28 BoolProperty,
29 FloatProperty,
30 StringProperty,
31 EnumProperty,
33 from bpy_extras.io_utils import (
34 ImportHelper,
35 ExportHelper,
36 orientation_helper,
37 path_reference_mode,
38 axis_conversion,
42 @orientation_helper(axis_forward='-Z', axis_up='Y')
43 class ImportOBJ(bpy.types.Operator, ImportHelper):
44 """Load a Wavefront OBJ File"""
45 bl_idname = "import_scene.obj"
46 bl_label = "Import OBJ"
47 bl_options = {'PRESET', 'UNDO'}
49 filename_ext = ".obj"
50 filter_glob: StringProperty(
51 default="*.obj;*.mtl",
52 options={'HIDDEN'},
55 use_edges: BoolProperty(
56 name="Lines",
57 description="Import lines and faces with 2 verts as edge",
58 default=True,
60 use_smooth_groups: BoolProperty(
61 name="Smooth Groups",
62 description="Surround smooth groups by sharp edges",
63 default=True,
66 use_split_objects: BoolProperty(
67 name="Object",
68 description="Import OBJ Objects into Blender Objects",
69 default=True,
71 use_split_groups: BoolProperty(
72 name="Group",
73 description="Import OBJ Groups into Blender Objects",
74 default=False,
77 use_groups_as_vgroups: BoolProperty(
78 name="Poly Groups",
79 description="Import OBJ groups as vertex groups",
80 default=False,
83 use_image_search: BoolProperty(
84 name="Image Search",
85 description="Search subdirs for any associated images "
86 "(Warning, may be slow)",
87 default=True,
90 split_mode: EnumProperty(
91 name="Split",
92 items=(
93 ('ON', "Split", "Split geometry, omits vertices unused by edges or faces"),
94 ('OFF', "Keep Vert Order", "Keep vertex order from file"),
98 global_clamp_size: FloatProperty(
99 name="Clamp Size",
100 description="Clamp bounds under this value (zero to disable)",
101 min=0.0, max=1000.0,
102 soft_min=0.0, soft_max=1000.0,
103 default=0.0,
106 def execute(self, context):
107 # print("Selected: " + context.active_object.name)
108 from . import import_obj
110 if self.split_mode == 'OFF':
111 self.use_split_objects = False
112 self.use_split_groups = False
113 else:
114 self.use_groups_as_vgroups = False
116 keywords = self.as_keywords(
117 ignore=(
118 "axis_forward",
119 "axis_up",
120 "filter_glob",
121 "split_mode",
125 global_matrix = axis_conversion(
126 from_forward=self.axis_forward,
127 from_up=self.axis_up,
128 ).to_4x4()
129 keywords["global_matrix"] = global_matrix
131 if bpy.data.is_saved and context.preferences.filepaths.use_relative_paths:
132 import os
133 keywords["relpath"] = os.path.dirname(bpy.data.filepath)
135 return import_obj.load(context, **keywords)
137 def draw(self, context):
138 pass
141 class OBJ_PT_import_include(bpy.types.Panel):
142 bl_space_type = 'FILE_BROWSER'
143 bl_region_type = 'TOOL_PROPS'
144 bl_label = "Include"
145 bl_parent_id = "FILE_PT_operator"
147 @classmethod
148 def poll(cls, context):
149 sfile = context.space_data
150 operator = sfile.active_operator
152 return operator.bl_idname == "IMPORT_SCENE_OT_obj"
154 def draw(self, context):
155 layout = self.layout
156 layout.use_property_split = True
157 layout.use_property_decorate = False # No animation.
159 sfile = context.space_data
160 operator = sfile.active_operator
162 layout.prop(operator, 'use_image_search')
163 layout.prop(operator, 'use_smooth_groups')
164 layout.prop(operator, 'use_edges')
167 class OBJ_PT_import_transform(bpy.types.Panel):
168 bl_space_type = 'FILE_BROWSER'
169 bl_region_type = 'TOOL_PROPS'
170 bl_label = "Transform"
171 bl_parent_id = "FILE_PT_operator"
173 @classmethod
174 def poll(cls, context):
175 sfile = context.space_data
176 operator = sfile.active_operator
178 return operator.bl_idname == "IMPORT_SCENE_OT_obj"
180 def draw(self, context):
181 layout = self.layout
182 layout.use_property_split = True
183 layout.use_property_decorate = False # No animation.
185 sfile = context.space_data
186 operator = sfile.active_operator
188 layout.prop(operator, "global_clamp_size")
189 layout.prop(operator, "axis_forward")
190 layout.prop(operator, "axis_up")
193 class OBJ_PT_import_geometry(bpy.types.Panel):
194 bl_space_type = 'FILE_BROWSER'
195 bl_region_type = 'TOOL_PROPS'
196 bl_label = "Geometry"
197 bl_parent_id = "FILE_PT_operator"
198 bl_options = {'DEFAULT_CLOSED'}
200 @classmethod
201 def poll(cls, context):
202 sfile = context.space_data
203 operator = sfile.active_operator
205 return operator.bl_idname == "IMPORT_SCENE_OT_obj"
207 def draw(self, context):
208 layout = self.layout
210 sfile = context.space_data
211 operator = sfile.active_operator
213 layout.row().prop(operator, "split_mode", expand=True)
215 layout.use_property_split = True
216 layout.use_property_decorate = False # No animation.
218 col = layout.column()
219 if operator.split_mode == 'ON':
220 col.prop(operator, "use_split_objects", text="Split by Object")
221 col.prop(operator, "use_split_groups", text="Split by Group")
222 else:
223 col.prop(operator, "use_groups_as_vgroups")
226 @orientation_helper(axis_forward='-Z', axis_up='Y')
227 class ExportOBJ(bpy.types.Operator, ExportHelper):
228 """Save a Wavefront OBJ File"""
230 bl_idname = "export_scene.obj"
231 bl_label = 'Export OBJ'
232 bl_options = {'PRESET'}
234 filename_ext = ".obj"
235 filter_glob: StringProperty(
236 default="*.obj;*.mtl",
237 options={'HIDDEN'},
240 # context group
241 use_selection: BoolProperty(
242 name="Selection Only",
243 description="Export selected objects only",
244 default=False,
246 use_animation: BoolProperty(
247 name="Animation",
248 description="Write out an OBJ for each frame",
249 default=False,
252 # object group
253 use_mesh_modifiers: BoolProperty(
254 name="Apply Modifiers",
255 description="Apply modifiers",
256 default=True,
258 # extra data group
259 use_edges: BoolProperty(
260 name="Include Edges",
261 description="",
262 default=True,
264 use_smooth_groups: BoolProperty(
265 name="Smooth Groups",
266 description="Write sharp edges as smooth groups",
267 default=False,
269 use_smooth_groups_bitflags: BoolProperty(
270 name="Bitflag Smooth Groups",
271 description="Same as 'Smooth Groups', but generate smooth groups IDs as bitflags "
272 "(produces at most 32 different smooth groups, usually much less)",
273 default=False,
275 use_normals: BoolProperty(
276 name="Write Normals",
277 description="Export one normal per vertex and per face, to represent flat faces and sharp edges",
278 default=True,
280 use_uvs: BoolProperty(
281 name="Include UVs",
282 description="Write out the active UV coordinates",
283 default=True,
285 use_materials: BoolProperty(
286 name="Write Materials",
287 description="Write out the MTL file",
288 default=True,
290 use_triangles: BoolProperty(
291 name="Triangulate Faces",
292 description="Convert all faces to triangles",
293 default=False,
295 use_nurbs: BoolProperty(
296 name="Write Nurbs",
297 description="Write nurbs curves as OBJ nurbs rather than "
298 "converting to geometry",
299 default=False,
301 use_vertex_groups: BoolProperty(
302 name="Polygroups",
303 description="",
304 default=False,
307 # grouping group
308 use_blen_objects: BoolProperty(
309 name="OBJ Objects",
310 description="Export Blender objects as OBJ objects",
311 default=True,
313 group_by_object: BoolProperty(
314 name="OBJ Groups",
315 description="Export Blender objects as OBJ groups",
316 default=False,
318 group_by_material: BoolProperty(
319 name="Material Groups",
320 description="Generate an OBJ group for each part of a geometry using a different material",
321 default=False,
323 keep_vertex_order: BoolProperty(
324 name="Keep Vertex Order",
325 description="",
326 default=False,
329 global_scale: FloatProperty(
330 name="Scale",
331 min=0.01, max=1000.0,
332 default=1.0,
335 path_mode: path_reference_mode
337 check_extension = True
339 def execute(self, context):
340 from . import export_obj
342 from mathutils import Matrix
343 keywords = self.as_keywords(
344 ignore=(
345 "axis_forward",
346 "axis_up",
347 "global_scale",
348 "check_existing",
349 "filter_glob",
353 global_matrix = (
354 Matrix.Scale(self.global_scale, 4) @
355 axis_conversion(
356 to_forward=self.axis_forward,
357 to_up=self.axis_up,
358 ).to_4x4()
361 keywords["global_matrix"] = global_matrix
362 return export_obj.save(context, **keywords)
364 def draw(self, context):
365 pass
368 class OBJ_PT_export_include(bpy.types.Panel):
369 bl_space_type = 'FILE_BROWSER'
370 bl_region_type = 'TOOL_PROPS'
371 bl_label = "Include"
372 bl_parent_id = "FILE_PT_operator"
374 @classmethod
375 def poll(cls, context):
376 sfile = context.space_data
377 operator = sfile.active_operator
379 return operator.bl_idname == "EXPORT_SCENE_OT_obj"
381 def draw(self, context):
382 layout = self.layout
383 layout.use_property_split = True
384 layout.use_property_decorate = False # No animation.
386 sfile = context.space_data
387 operator = sfile.active_operator
389 col = layout.column(heading="Limit to")
390 col.prop(operator, 'use_selection')
392 col = layout.column(heading="Objects as", align=True)
393 col.prop(operator, 'use_blen_objects')
394 col.prop(operator, 'group_by_object')
395 col.prop(operator, 'group_by_material')
397 layout.separator()
399 layout.prop(operator, 'use_animation')
402 class OBJ_PT_export_transform(bpy.types.Panel):
403 bl_space_type = 'FILE_BROWSER'
404 bl_region_type = 'TOOL_PROPS'
405 bl_label = "Transform"
406 bl_parent_id = "FILE_PT_operator"
408 @classmethod
409 def poll(cls, context):
410 sfile = context.space_data
411 operator = sfile.active_operator
413 return operator.bl_idname == "EXPORT_SCENE_OT_obj"
415 def draw(self, context):
416 layout = self.layout
417 layout.use_property_split = True
418 layout.use_property_decorate = False # No animation.
420 sfile = context.space_data
421 operator = sfile.active_operator
423 layout.prop(operator, 'global_scale')
424 layout.prop(operator, 'path_mode')
425 layout.prop(operator, 'axis_forward')
426 layout.prop(operator, 'axis_up')
429 class OBJ_PT_export_geometry(bpy.types.Panel):
430 bl_space_type = 'FILE_BROWSER'
431 bl_region_type = 'TOOL_PROPS'
432 bl_label = "Geometry"
433 bl_parent_id = "FILE_PT_operator"
434 bl_options = {'DEFAULT_CLOSED'}
436 @classmethod
437 def poll(cls, context):
438 sfile = context.space_data
439 operator = sfile.active_operator
441 return operator.bl_idname == "EXPORT_SCENE_OT_obj"
443 def draw(self, context):
444 layout = self.layout
445 layout.use_property_split = True
446 layout.use_property_decorate = False # No animation.
448 sfile = context.space_data
449 operator = sfile.active_operator
451 layout.prop(operator, 'use_mesh_modifiers')
452 layout.prop(operator, 'use_smooth_groups')
453 layout.prop(operator, 'use_smooth_groups_bitflags')
454 layout.prop(operator, 'use_normals')
455 layout.prop(operator, 'use_uvs')
456 layout.prop(operator, 'use_materials')
457 layout.prop(operator, 'use_triangles')
458 layout.prop(operator, 'use_nurbs', text="Curves as NURBS")
459 layout.prop(operator, 'use_vertex_groups')
460 layout.prop(operator, 'keep_vertex_order')
463 def menu_func_import(self, context):
464 self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj)")
467 def menu_func_export(self, context):
468 self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)")
471 classes = (
472 ImportOBJ,
473 OBJ_PT_import_include,
474 OBJ_PT_import_transform,
475 OBJ_PT_import_geometry,
476 ExportOBJ,
477 OBJ_PT_export_include,
478 OBJ_PT_export_transform,
479 OBJ_PT_export_geometry,
483 def register():
484 for cls in classes:
485 bpy.utils.register_class(cls)
487 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
488 # Disabling the menu entry for this python exporter now that
489 # there is a C++ exporter. For now, leaving the actual
490 # export_scene.obj pointing at the python version.
491 # bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
494 def unregister():
495 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
496 # See comment above about menu for the python exporter
497 # bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
499 for cls in classes:
500 bpy.utils.unregister_class(cls)
503 if __name__ == "__main__":
504 register()