Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
# ##### 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": "Batch Import Wavefront (.obj)",
"author": "p2or, JuhaW",
"version": (0, 1, 0),
"blender": (2, 80, 0),
"location": "File > Import-Export",
"description": "Import multiple OBJ files, UV's, materials",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Import-Export"}
import bpy
import os
from bpy_extras.io_utils import ImportHelper
from bpy.props import (BoolProperty,
FloatProperty,
StringProperty,
EnumProperty,
CollectionProperty
)
class ImportMultipleObjs(bpy.types.Operator, ImportHelper):
"""Batch Import Wavefront obj"""
bl_idname = "import_scene.multiple_objs"
bl_label = "Import multiple OBJ's"
bl_options = {'PRESET', 'UNDO'}
# ImportHelper mixin class uses this
filename_ext = ".obj"
filter_glob = StringProperty(
default="*.obj",
options={'HIDDEN'},
)
# Selected files
files: CollectionProperty(type=bpy.types.PropertyGroup)
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
edges_setting: BoolProperty(
name="Lines",
description="Import lines and faces with 2 verts as edge",
default=True,
)
smooth_groups_setting: BoolProperty(
name="Smooth Groups",
description="Surround smooth groups by sharp edges",
default=True,
)
split_objects_setting: BoolProperty(
name="Object",
description="Import OBJ Objects into Blender Objects",
default=True,
)
split_groups_setting: BoolProperty(
name="Group",
description="Import OBJ Groups into Blender Objects",
default=True,
)
groups_as_vgroups_setting: BoolProperty(
name="Poly Groups",
description="Import OBJ groups as vertex groups",
default=False,
)
image_search_setting: BoolProperty(
name="Image Search",
description="Search subdirs for any associated images "
"(Warning, may be slow)",
default=True,
)
split_mode_setting: EnumProperty(
name="Split",
items=(('ON', "Split", "Split geometry, omits unused verts"),
('OFF', "Keep Vert Order", "Keep vertex order from file"),
),
)
clamp_size_setting: FloatProperty(
name="Clamp Size",
description="Clamp bounds under this value (zero to disable)",
min=0.0, max=1000.0,
soft_min=0.0, soft_max=1000.0,
default=0.0,
)
axis_forward_setting: EnumProperty(
name="Forward",
items=(('X', "X Forward", ""),
('Y', "Y Forward", ""),
('Z', "Z Forward", ""),
('-X', "-X Forward", ""),
('-Y', "-Y Forward", ""),
('-Z', "-Z Forward", ""),
),
default='-Z',
)
axis_up_setting: EnumProperty(
name="Up",
items=(('X', "X Up", ""),
('Y', "Y Up", ""),
('Z', "Z Up", ""),
('-X', "-X Up", ""),
('-Y', "-Y Up", ""),
('-Z', "-Z Up", ""),
),
default='Y',
)
scale_setting: FloatProperty(
name="Scale",
description="Scale objects",
min=0.0, max=1000.0,
soft_min=0.0, soft_max=1000.0,
default=1.0,
)
center_origin: BoolProperty(
name = "Center Origin",
default=False
)
def draw(self, context):
layout = self.layout
row = layout.row(align=True)
row.prop(self, "smooth_groups_setting")
row.prop(self, "edges_setting")
box = layout.box()
row = box.row()
row.prop(self, "split_mode_setting", expand=True)
row = box.row()
if self.split_mode_setting == 'ON':
row.label(text="Split by:")
row.prop(self, "split_objects_setting")
row.prop(self, "split_groups_setting")
else:
row.prop(self, "groups_as_vgroups_setting")
row = layout.split(factor=0.67)
row.prop(self, "clamp_size_setting")
layout.prop(self, "axis_forward_setting")
layout.prop(self, "axis_up_setting")
layout.prop(self, "image_search_setting")
row = layout.split(factor=0.5)
row.prop(self, "scale_setting")
row.prop(self, "center_origin")
def execute(self, context):
# get the folder
folder = (os.path.dirname(self.filepath))
# iterate through the selected files
for j, i in enumerate(self.files):
# generate full path to file
path_to_file = (os.path.join(folder, i.name))
# call obj operator and assign ui values
bpy.ops.import_scene.obj(
filepath = path_to_file,
axis_forward = self.axis_forward_setting,
axis_up = self.axis_up_setting,
use_edges = self.edges_setting,
use_smooth_groups = self.smooth_groups_setting,
use_split_objects = self.split_objects_setting,
use_split_groups = self.split_groups_setting,
use_groups_as_vgroups = self.groups_as_vgroups_setting,
use_image_search = self.image_search_setting,
split_mode = self.split_mode_setting,
global_clight_size = self.clamp_size_setting)
if self.center_origin: bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
bpy.ops.transform.resize(value=(self.scale_setting,self.scale_setting,self.scale_setting), constraint_axis=(False, False, False))
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
return {'FINISHED'}
# Only needed if you want to add into a dynamic menu
def menu_func_import(self, context):
self.layout.operator(ImportMultipleObjs.bl_idname, text="Wavefront Batch (.obj)")
def register():
bpy.utils.register_class(ImportMultipleObjs)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
def unregister():
bpy.utils.unregister_class(ImportMultipleObjs)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
if __name__ == "__main__":
register()
# test call
#bpy.ops.import_scene.multiple_objs('INVOKE_DEFAULT')
@tapir

This comment has been minimized.

Copy link

tapir commented Oct 10, 2016

Messes up transformations. Coordinates from previous obj file is accumulated or something.

@manfredg

This comment has been minimized.

Copy link

manfredg commented Oct 31, 2016

@tapir Have a look at line 203. If you remove the j*5 and replace it with 0 your objects will be placed in their original location, which should be the default IMO, even though it will sometimes result in objects being on top of each other.

@pl1307

This comment has been minimized.

Copy link

pl1307 commented Dec 2, 2016

Thank you for sharing this code. I have a question concerning the exporter (I haven't found the exporter on github) - it seems that the exporter didn't process colons (name of objects) successfully. i think if a model is to large, blender seems to split the object into multiple objects and is naming them witch a colon. Is it possible to modify the exporter to process colons successfully or is this impossible within the wrapper?

@chiboreache

This comment has been minimized.

Copy link

chiboreache commented Apr 5, 2017

2.78.4 works well, thank you a lot, just wondering why it is not a standard feature

@DonJorris

This comment has been minimized.

Copy link

DonJorris commented Nov 15, 2017

Doesn't seem to work in Blender 2.79. Would be great if you could fix it.

@p2or

This comment has been minimized.

Copy link
Owner Author

p2or commented Nov 20, 2017

Fixed @DonJorris. You can also try the original addon without all that scale jazz.

@REscanDon

This comment has been minimized.

Copy link

REscanDon commented May 8, 2019

In case it's of interest I've updated the original (non-scaling) version to work with the current 2.80 beta

Filename: io_import_multiple_objs.py

# original source https://blender.stackexchange.com/questions/5064/how-to-batch-import-wavefront-obj-files/31825#31825
# updated to 2.80 beta by don

# ##### 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": "Import multiple OBJ files",
    "author": "poor",
    "version": (0, 1, 0),
    "blender": (2, 80, 0),
    "location": "File > Import > Wavefront Batch (.obj)",
    "description": "Import multiple OBJ files, UV's, materials",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Import-Export"}


import bpy
import os

from bpy_extras.io_utils import ImportHelper

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


class ImportMultipleObjs(bpy.types.Operator, ImportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "import_scene.multiple_objs"
    bl_label = "Import multiple OBJs"
    bl_options = {'PRESET', 'UNDO'}

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

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

    # Selected files
    files = CollectionProperty(type=bpy.types.PropertyGroup)

    # List of operator properties, the attributes will be assigned
    # to the class instance from the operator settings before calling.
    ngons_setting = BoolProperty(
            name="NGons",
            description="Import faces with more than 4 verts as ngons",
            default=True,
            )
    edges_setting = BoolProperty(
            name="Lines",
            description="Import lines and faces with 2 verts as edge",
            default=True,
            )
    smooth_groups_setting = BoolProperty(
            name="Smooth Groups",
            description="Surround smooth groups by sharp edges",
            default=True,
            )

    split_objects_setting = BoolProperty(
            name="Object",
            description="Import OBJ Objects into Blender Objects",
            default=True,
            )
    split_groups_setting = BoolProperty(
            name="Group",
            description="Import OBJ Groups into Blender Objects",
            default=True,
            )

    groups_as_vgroups_setting = BoolProperty(
            name="Poly Groups",
            description="Import OBJ groups as vertex groups",
            default=False,
            )

    image_search_setting = BoolProperty(
            name="Image Search",
            description="Search subdirs for any associated images "
                        "(Warning, may be slow)",
            default=True,
            )

    split_mode_setting = EnumProperty(
            name="Split",
            items=(('ON', "Split", "Split geometry, omits unused verts"),
                   ('OFF', "Keep Vert Order", "Keep vertex order from file"),
                   ),
            )

    clamp_size_setting = FloatProperty(
            name="Clamp Size",
            description="Clamp bounds under this value (zero to disable)",
            min=0.0, max=1000.0,
            soft_min=0.0, soft_max=1000.0,
            default=0.0,
            )
    axis_forward_setting = EnumProperty(
            name="Forward",
            items=(('X', "X Forward", ""),
                   ('Y', "Y Forward", ""),
                   ('Z', "Z Forward", ""),
                   ('-X', "-X Forward", ""),
                   ('-Y', "-Y Forward", ""),
                   ('-Z', "-Z Forward", ""),
                   ),
            default='-Z',
            )

    axis_up_setting = EnumProperty(
            name="Up",
            items=(('X', "X Up", ""),
                   ('Y', "Y Up", ""),
                   ('Z', "Z Up", ""),
                   ('-X', "-X Up", ""),
                   ('-Y', "-Y Up", ""),
                   ('-Z', "-Z Up", ""),
                   ),
            default='Y',
            )

    def draw(self, context):
        layout = self.layout

        row = layout.row(align=True)
        row.prop(self, "ngons_setting")
        row.prop(self, "edges_setting")

        layout.prop(self, "smooth_groups_setting")

        box = layout.box()
        row = box.row()
        row.prop(self, "split_mode_setting", expand=True)

        row = box.row()
        if self.split_mode_setting == 'ON':
            row.label(text="Split by:")
            row.prop(self, "split_objects_setting")
            row.prop(self, "split_groups_setting")
        else:
            row.prop(self, "groups_as_vgroups_setting")

        row = layout.split(factor=0.67)
        row.prop(self, "clamp_size_setting")
        layout.prop(self, "axis_forward_setting")
        layout.prop(self, "axis_up_setting")

        layout.prop(self, "image_search_setting")

    def execute(self, context):

        # get the folder
        folder = (os.path.dirname(self.filepath))

        # iterate through the selected files
        for i in self.files:

            # generate full path to file
            path_to_file = (os.path.join(folder, i.name))

            # call obj operator and assign ui values                  
            bpy.ops.import_scene.obj(filepath = path_to_file,
                                axis_forward = self.axis_forward_setting,
                                axis_up = self.axis_up_setting, 
                                use_edges = self.edges_setting,
                                use_smooth_groups = self.smooth_groups_setting, 
                                use_split_objects = self.split_objects_setting,
                                use_split_groups = self.split_groups_setting,
                                use_groups_as_vgroups = self.groups_as_vgroups_setting,
                                use_image_search = self.image_search_setting,
                                split_mode = self.split_mode_setting)

        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(ImportMultipleObjs.bl_idname, text="Wavefont Batch (.obj)")


def register():
    bpy.utils.register_class(ImportMultipleObjs)
    bpy.types.TOPBAR_MT_file_import.append(menu_func)


def unregister():
    bpy.utils.unregister_class(ImportMultipleObjs)
    bpy.types.TOPBAR_MT_file_import.remove(menu_func)


if __name__ == "__main__":
    register()

    # test call
    #bpy.ops.import_scene.multiple_objs('INVOKE_DEFAULT')
@p2or

This comment has been minimized.

Copy link
Owner Author

p2or commented Aug 27, 2019

Hi @REscanDon,

you forgot the annotations, updated based on your version here.

Cheers,
Christian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.