1 # SPDX-License-Identifier: GPL-2.0-or-later
6 "name": "Wavefront OBJ format",
7 "author": "Campbell Barton, Bastien Montagne",
10 "location": "File > Import-Export",
11 "description": "Import-Export OBJ, Import OBJ mesh, UV's, materials and textures",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/scene_obj.html",
14 "support": 'OFFICIAL',
15 "category": "Import-Export",
20 if "import_obj" in locals():
21 importlib
.reload(import_obj
)
22 if "export_obj" in locals():
23 importlib
.reload(export_obj
)
27 from bpy
.props
import (
33 from bpy_extras
.io_utils
import (
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'}
50 filter_glob
: StringProperty(
51 default
="*.obj;*.mtl",
55 use_edges
: BoolProperty(
57 description
="Import lines and faces with 2 verts as edge",
60 use_smooth_groups
: BoolProperty(
62 description
="Surround smooth groups by sharp edges",
66 use_split_objects
: BoolProperty(
68 description
="Import OBJ Objects into Blender Objects",
71 use_split_groups
: BoolProperty(
73 description
="Import OBJ Groups into Blender Objects",
77 use_groups_as_vgroups
: BoolProperty(
79 description
="Import OBJ groups as vertex groups",
83 use_image_search
: BoolProperty(
85 description
="Search subdirs for any associated images "
86 "(Warning, may be slow)",
90 split_mode
: EnumProperty(
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(
100 description
="Clamp bounds under this value (zero to disable)",
102 soft_min
=0.0, soft_max
=1000.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
114 self
.use_groups_as_vgroups
= False
116 keywords
= self
.as_keywords(
125 global_matrix
= axis_conversion(
126 from_forward
=self
.axis_forward
,
127 from_up
=self
.axis_up
,
129 keywords
["global_matrix"] = global_matrix
131 if bpy
.data
.is_saved
and context
.preferences
.filepaths
.use_relative_paths
:
133 keywords
["relpath"] = os
.path
.dirname(bpy
.data
.filepath
)
135 return import_obj
.load(context
, **keywords
)
137 def draw(self
, context
):
141 class OBJ_PT_import_include(bpy
.types
.Panel
):
142 bl_space_type
= 'FILE_BROWSER'
143 bl_region_type
= 'TOOL_PROPS'
145 bl_parent_id
= "FILE_PT_operator"
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
):
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"
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
):
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'}
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
):
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")
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",
241 use_selection
: BoolProperty(
242 name
="Selection Only",
243 description
="Export selected objects only",
246 use_animation
: BoolProperty(
248 description
="Write out an OBJ for each frame",
253 use_mesh_modifiers
: BoolProperty(
254 name
="Apply Modifiers",
255 description
="Apply modifiers",
259 use_edges
: BoolProperty(
260 name
="Include Edges",
264 use_smooth_groups
: BoolProperty(
265 name
="Smooth Groups",
266 description
="Write sharp edges as smooth groups",
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)",
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",
280 use_uvs
: BoolProperty(
282 description
="Write out the active UV coordinates",
285 use_materials
: BoolProperty(
286 name
="Write Materials",
287 description
="Write out the MTL file",
290 use_triangles
: BoolProperty(
291 name
="Triangulate Faces",
292 description
="Convert all faces to triangles",
295 use_nurbs
: BoolProperty(
297 description
="Write nurbs curves as OBJ nurbs rather than "
298 "converting to geometry",
301 use_vertex_groups
: BoolProperty(
308 use_blen_objects
: BoolProperty(
310 description
="Export Blender objects as OBJ objects",
313 group_by_object
: BoolProperty(
315 description
="Export Blender objects as OBJ groups",
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",
323 keep_vertex_order
: BoolProperty(
324 name
="Keep Vertex Order",
329 global_scale
: FloatProperty(
331 min=0.01, max=1000.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(
354 Matrix
.Scale(self
.global_scale
, 4) @
356 to_forward
=self
.axis_forward
,
361 keywords
["global_matrix"] = global_matrix
362 return export_obj
.save(context
, **keywords
)
364 def draw(self
, context
):
368 class OBJ_PT_export_include(bpy
.types
.Panel
):
369 bl_space_type
= 'FILE_BROWSER'
370 bl_region_type
= 'TOOL_PROPS'
372 bl_parent_id
= "FILE_PT_operator"
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
):
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')
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"
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
):
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'}
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
):
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)")
473 OBJ_PT_import_include
,
474 OBJ_PT_import_transform
,
475 OBJ_PT_import_geometry
,
477 OBJ_PT_export_include
,
478 OBJ_PT_export_transform
,
479 OBJ_PT_export_geometry
,
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)
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)
500 bpy
.utils
.unregister_class(cls
)
503 if __name__
== "__main__":