30
$\begingroup$

It is possible to export all object from a scene into different obj files (each object in a obj file separately)?

$\endgroup$

6 Answers 6

30
$\begingroup$

There doesn't exist a way to do this in the options of the obj importer so what you could do is run a script that loops over the items in your scene, check if it is a mesh and export the current selection to its own file relative to the path your blend file is saved.

Blender 3.1+

As of Blender 3.1.0 the name of the operator has been changed to obj_export() and is now part of the WindowManager namespace bpy.ops.wm.obj_export(filepath="//"):

import bpy
import os

# Get the path where the blend file is located
basedir = bpy.path.abspath('//')

# Deselect all objects
bpy.ops.object.select_all(action='DESELECT')    

# loop through all the objects in the scene
scene = bpy.context.scene
for ob in scene.objects:
    # Select each object
    ob.select_set(True)

    # Make sure that we only export meshes
    if ob.type == 'MESH':
        # Export the currently selected object to its own file based on its name
        bpy.ops.wm.obj_export(
            filepath=os.path.join(basedir, ob.name + '.obj'),
            export_selected_objects=True,
            )
    # Deselect the object and move on to another if any more are left
    ob.select_set(False)

Blender 2.8+

As of Blender 2.8.0 the selection attribute (Object.select) has been removed in favor of get/set functions Object.select_set(State) and Object.select_get() to set and get the current selection state of each object:

import bpy
import os

# Get the path where the blend file is located
basedir = bpy.path.abspath('//')

# Deselect all objects
bpy.ops.object.select_all(action='DESELECT')    

# loop through all the objects in the scene
scene = bpy.context.scene
for ob in scene.objects:
    # Select each object
    ob.select_set(True)

    # Make sure that we only export meshes
    if ob.type == 'MESH':
        # Export the currently selected object to its own file based on its name
        bpy.ops.export_scene.obj(
                filepath=os.path.join(basedir, ob.name + '.obj'),
                use_selection=True,
                )
    # Deselect the object and move on to another if any more are left
    ob.select_set(False)

Blender 2.7x

import bpy
import os

# Get the path where the blend file is located
basedir = bpy.path.abspath('//')

# Deselect all objects
bpy.ops.object.select_all(action='DESELECT')    

# Loop through all the objects in the scene
scene = bpy.context.scene
for ob in scene.objects:
    # Make the current object active and select it
    scene.objects.active = ob
    ob.select = True
    
    # Make sure that we only export meshes
    if ob.type == 'MESH':
        # Export the currently selected object to its own file based on its name
        bpy.ops.export_scene.obj(
                filepath=os.path.join(basedir, ob.name + '.obj'),
                use_selection=True,
                )
    # Deselect the object and move on to another if any more are left
    ob.select = False

To further tweak your export options, see the parameters that bpy.ops.export_scene_obj() accepts. For example, you can include normals with use_normals=True, change forward axis with axis_forward etc.

There are more stringent checks that could be made but for simple use cases and if used properly, this should suffice.

Also see How to batch import Wavefront OBJ files?.


To have it export only selected objects, comment line 7 and change line 11.

# Remove
bpy.ops.object.select_all(action='DESELECT')
# Change to this where instead of the entire scene, we only look through selected objects
for ob in bpy.context.selected_objects:

or keep line 11 and add another line

for ob in scene.objects:
    # Add the check to see if in selected objects here
    if ob in bpy.context.selected_objects:
        # adjust indentation
        ...
        ...

NB: Make sure the layers you have objects on are selected otherwise you will just get an empty file.

$\endgroup$
8
  • $\begingroup$ ok, thanks. And how i control options of export with your script (the scale, the axis...)? $\endgroup$
    – GabrielS
    Dec 8, 2013 at 22:46
  • $\begingroup$ @GabrielS all the options that you can pass to bpy.ops.export_scene.obj are here, blender.org/documentation/blender_python_api_2_69_release/… $\endgroup$
    – iKlsR
    Dec 8, 2013 at 22:47
  • $\begingroup$ @iKlsR, best only link to _release pages, we delete blender_python_api_2_69_4 dirs occasionally. $\endgroup$
    – ideasman42
    Dec 9, 2013 at 5:19
  • $\begingroup$ @iKlsR your script work fine and i have custome it to fit what i want. One more thing. how can i modify the script to export only selected objects? Thanks $\endgroup$
    – GabrielS
    Dec 9, 2013 at 11:09
  • $\begingroup$ @GabrielS updated answer. $\endgroup$
    – iKlsR
    Dec 9, 2013 at 11:27
25
$\begingroup$

Following Add-on allows to batch export the current selection or simply all scene geometry objects as separate wavefront .obj files. All usual options like Apply Modifiers or Write Materials are available in the Properties Panel N too.

Repository: github.com/p2or/blender-batch-export-wavefront-obj

enter image description here

io_batch_export_objs.py (updated for 3.6+)

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>

bl_info = {
    "name": "OBJ Batch Export",
    "author": "p2or, brockmann, trippeljojo",
    "version": (0, 5, 0),
    "blender": (3, 6, 0),
    "location": "File > Import-Export",
    "description": "Export multiple OBJ files, their UVs and Materials",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Import-Export"}


import bpy
import os
from bpy_extras.io_utils import ExportHelper

from bpy.props import (BoolProperty,
                       IntProperty,
                       FloatProperty,
                       StringProperty,
                       EnumProperty,
                       CollectionProperty
                       )


class WM_OT_batchExportObjs(bpy.types.Operator, ExportHelper):
    """Batch export the scene to separate obj files"""
    bl_idname = "export_scene.batch_obj"
    bl_label = "Batch export OBJ's"
    bl_options = {'PRESET', 'UNDO'}

    # ExportHelper mixin class uses this
    filename_ext = ".obj"

    filter_glob = StringProperty(
            default="*.obj;*.mtl",
            options={'HIDDEN'},)

    selection_only: BoolProperty(
            name="Selection Only",
            description="Export selected objects only",
            default=True,)

    scale_factor: FloatProperty(
            name="Scale",
            min=0.01, max=1000.0,
            default=1.0,)
            
    axis_forward: EnumProperty(
            name="Forward Axis",
            items=(('X', "X", "Positive X Axis"),
                   ('Y', "Y", "Positive Y Axis"),
                   ('Z', "Z", "Positive Z Axis"),
                   ('NEGATIVE_X', "-X", "Negative X Axis"),
                   ('NEGATIVE_Y', "-Y", "Negative Y Axis"),
                   ('NEGATIVE_Z', "-Z (Default)", "Negative Z Axis"),),
            default='NEGATIVE_Z')
            
    axis_up: EnumProperty(
            name="Up Axis",
            items=(('X', "X Up", "Positive X Axis"),
                   ('Y', "Y Up (Default)", "Positive Y Axis"),
                   ('Z', "Z Up", "Positive Z Axis"),
                   ('NEGATIVE_X', "-X Up", "Negative X Axis"),
                   ('NEGATIVE_Y', "-Y Up", "Negative Y Axis"),
                   ('NEGATIVE_Z', "-Z Up", "Negative Z Axis"),),
            default='Y')

    modifiers_apply: BoolProperty(
            name="Apply Modifiers",
            description="Apply modifiers to exported meshes",
            default=False)
            
    eval_mode: EnumProperty(
            name="Properties",
            description="Determines properties like object visibility, "
                        "modifiers etc., where they differ for Render and Viewport",
            items=(('DAG_EVAL_VIEWPORT', 
                    "Viewport (Default)", 
                    "Export objects as they appear in the viewport"),
                   ('DAG_EVAL_RENDER', 
                    "Render", 
                    "Export objects as they appear in render"),),
            default='DAG_EVAL_VIEWPORT')
            
    # GEOMETRY 
    write_uvs: BoolProperty(
            name="UV Coordinates",
            description="Export the UV coordinates",
            default=True)

    write_normals: BoolProperty(
            name="Normals",
            description="Export per-face normals if the face is flat-shaded, "
                        "per-face-per-loop normals if smooth-shaded",
            default=True)
    
    write_colors: BoolProperty(
            name="Colors",
            description="Export per-vertex colors",
            default=False)

    triangulate_faces: BoolProperty(
            name="Triangulated Mesh",
            description='All ngons with four or more vertices will be triangulated. '
                        'Meshes in the scene will not be affected. Behaves like '
                        'Triangulate Modifier with ngon-method: "Beauty", quad-method: '
                        '"Shortest Diagonal", min vertices: 4.',
            default=False)
    
    write_nurbs: BoolProperty(
            name="Curves as NURBS",
            description="Export curves in parametric form instead of exporting as mesh",
            default=False)
        
    # MATERIALS 
    write_materials: BoolProperty(
            name="Export Materials",
            description="Export MTL library. There must be a Principled-BSDF "
                        "node for image textures to be exported to the MTL file.",
            default=True)
    
    write_pbr: BoolProperty(
            name="PBR Extensions",
            description="Export MTL library using PBR extensions (roughness, metallic, "
                        "sheen, coat, anisotropy, transmission)",
            default=False) 
    
    path_mode: EnumProperty(
            name="Path Mode",
            description="Method used to reference paths",
            items=(('AUTO', "Auto", "Use relative paths with subdirectories only"),
                   ('ABSOLUTE', "Absolute", "Always write absolute paths"),
                   ('RELATIVE', "Relative", "Write relative paths where possible"),
                   ('MATCH', "Match", "OMatch absolute/relative setting with input path"),
                   ('STRIP', "Strip", "Write filename only"),
                   ('COPY', "Copy", "Copy the file to the destination path"),
                   ),
            default='AUTO')
    
    # GROUPING 
    group_by_object: BoolProperty(
            name="Object Groups",
            description="Append mesh name to object name, separated by a '_'",
            default=False)
    
    group_by_material: BoolProperty(
            name="Material Groups",
            description="Generate an OBK group for each part of a geometry "
                        "using a different material",
            default=False)
    
    group_by_vertex: BoolProperty(
            name="Vertex Groups",
            description="Export the name of the vertex group of a face. "
                        "It is approximated by choosing the vertex group "
                        "with the most members amoung the vertices of a face",
            default=False)
    
    smoothing_groups: BoolProperty(
            name="Smooth Groups",
            description='Every smooth-shaded face is assigned group "1" and '
                        'every flat-shaded face "off".',
            default=False)

    smoothing_group_bitflags: BoolProperty(
            name="Smooth Group Bitflags",
            description="Same as 'Smooth Groups', but generate smooth groups IDs as bitflags "
                        "(produces at most 32 different smooth groups, usually much less)",
            default=False)

    def execute(self, context):
                       
        # Get the current folder
        folder_path = os.path.dirname(self.filepath)
        
        # Get all objects selected in the viewport
        viewport_selection = candidates = context.selected_objects
        if self.selection_only == False:
            candidates = [o for o in context.scene.objects]

        # Deselect all objects
        bpy.ops.object.select_all(action='DESELECT')
        
        for obj in [o for o in candidates if o.type == 'MESH']:
            obj.select_set(True)
            
            file_path = os.path.join(folder_path, "{}.obj".format(obj.name))
            bpy.ops.wm.obj_export(
                    filepath=file_path,
                    export_animation=False,
                    #start_frame=-2147483648, 
                    # end_frame=2147483647,
                    forward_axis=self.axis_forward,
                    up_axis=self.axis_up,
                    global_scale=self.scale_factor,
                    apply_modifiers=self.modifiers_apply,
                    export_eval_mode=self.eval_mode,
                    export_selected_objects=True,
                    export_uv=self.write_uvs,
                    export_normals=self.write_normals,
                    export_colors=self.write_colors,
                    export_materials=self.write_materials,
                    export_pbr_extensions=self.write_pbr,
                    path_mode=self.path_mode,
                    export_triangulated_mesh=self.triangulate_faces,
                    export_curves_as_nurbs=self.write_nurbs,
                    export_object_groups=self.group_by_object,
                    export_material_groups=self.group_by_material,
                    export_vertex_groups=self.group_by_vertex,
                    export_smooth_groups=self.smoothing_groups,
                    smooth_group_bitflags=self.smoothing_group_bitflags
            )
            obj.select_set(False)

        # Restore Viewport Selection
        for obj in viewport_selection:
            obj.select_set(True)
            
        return {'FINISHED'}


def menu_func_import(self, context):
    self.layout.operator(WM_OT_batchExportObjs.bl_idname, text="Wavefront Batch (.obj)")

def register():
    bpy.utils.register_class(WM_OT_batchExportObjs)
    #bpy.types.TOPBAR_MT_file_export.append(menu_func_import)

def unregister():
    bpy.utils.unregister_class(WM_OT_batchExportObjs)
    #bpy.types.TOPBAR_MT_file_export.remove(menu_func_import)


if __name__ == "__main__":
    register()

    # test call
    #bpy.ops.export_scene.batch_obj('INVOKE_DEFAULT')

Note: The add-on is just a wrapper of wm.obj_export() or export_scene.obj() operator in versions prior to Blender 3.2.0 until exporting multiple files is officially supported. To import multiple obj files see this answer: How to batch import Wavefront OBJ files?

Versions

To keep track of all the changes over the years I decided to create a dedicated repository instead of posting each version here, which makes it easier to download as well:

Blender Addon Version Release
Blender 2.74+ 0.1.0 Download
Blender 2.80+ 0.2.0 Download
Blender 3.1+ 0.3.0 Download
Blender 3.3+ 0.4.0 Download
Blender 3.6+ 0.5.0 Download
$\endgroup$
4
  • $\begingroup$ Is there a way to export the object with the center of object for the mesh instead of the global origin? $\endgroup$
    – GammaGames
    Jan 21, 2020 at 4:15
  • $\begingroup$ I figured it out, I added a use_mesh_as_center option that sets the mesh's location to (0, 0, 0) before exporting and resets it back to its original after $\endgroup$
    – GammaGames
    Jan 21, 2020 at 5:28
  • $\begingroup$ Hi @p2or, thanks for the great answer. Do you know if there is a way to save modifiers along with the .obj, so that importing them later will preserve them? (I have a relevant question here: blender.stackexchange.com/questions/181143/…) $\endgroup$ Jun 5, 2020 at 14:17
  • 1
    $\begingroup$ .obj is a pretty old file format and doesn't allow to preserve any modifiers. Even when using alembic or usd, there is no way to preserve that (in 2020). If there is a serious reason to do that, I'd suggest to save all the modifier properties in a json string as text file and check for that file when loading the data back in. Cheers @ComputerScientist $\endgroup$
    – p2or
    Jun 6, 2020 at 12:58
5
$\begingroup$

Working example for Blender 2.8 to batch export, just change the path

import bpy

#example path to store files
path = 'path\subpath\name'

#store selection
obs = bpy.context.selected_objects

for ob in obs:
    #deselect all but just one object and make it active
    bpy.ops.object.select_all(action='DESELECT')
    ob.select_set(state=True)
    bpy.context.view_layer.objects.active = ob

    #store object location then zero it out
    location = ob.location.copy()
    bpy.ops.object.location_clear()

    #export fbx
    filename = path + ob.name + '.obj'
    bpy.ops.export_scene.obj(filepath=filename, use_selection=True)

    #restore location
    ob.location = location

#reselect originally selected objects  
for ob in obs:
    ob.select_set(state=True)
$\endgroup$
2
$\begingroup$

For me iKlsR's method did not work when trying to export multiple meshes. I got all meshes in the first exported file. The other exports were rather random, some contained nothing, others contained 3 objects. I have changed some code around and it works for me now.


Method for exporting a selection.

import bpy
import os

# get the current path and make a new folder for the exported meshes
path = bpy.path.abspath('//objs' + os.sep)
os.makedirs(path)

for object in bpy.context.selected_objects:

    # deselect all meshes
    bpy.ops.object.select_all(action='DESELECT')

    # select the object
    object.select = True

    # export object with its name as file name
    bpy.ops.export_scene.obj(
            filepath=path + object.name + '.obj',
            use_selection=True,
            )
$\endgroup$
2
  • $\begingroup$ thanks for directing me here. I had to modify your version a little more to get it to work on OSX. path = bpy.path.abspath('//') #os.makedirs(path) It was creating a folder but not putting the .obj files into it and it was also prefixing all the names with "objs/" Also, it crashed upon second run unless I deleted the empty folder it's trying to re-create. $\endgroup$ Mar 8, 2014 at 14:56
  • $\begingroup$ feel free edit my answer, however the code i posted worked for me $\endgroup$
    – Vader
    Mar 8, 2014 at 15:21
0
$\begingroup$

For Blender 2.92.1

multiobj_exporter.py

import bpy
import os

basedir = 'absolute path'

sel_objs = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH']
bpy.ops.object.select_all(action='DESELECT')
for obj in sel_objs:
    bpy.context.view_layer.objects.active = obj
    obj.select_set(True)
    bpy.ops.export_scene.obj(
        filepath=os.path.join(basedir, obj.name + '.obj'),
        use_selection=True,
    )
    obj.select_set(False)

the code worked with only background option like below because an context error occurred. If anyone know the reason why, please tell me.

$ blender --background --python multiobj_exporter.py

$\endgroup$
-1
$\begingroup$

Just Set This: enter image description here

Untick All Other Settings And Set Only Obj Groups

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .