Skip to content
Snippets Groups Projects
import_obj.py 44 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### 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>
    
    # Script copyright (C) Campbell Barton
    # Contributors: Campbell Barton, Jiri Hnidek, Paolo Ciccone
    
    """
    This script imports a Wavefront OBJ files to Blender.
    
    Usage:
    Run this script from "File->Import" menu and then load the desired OBJ file.
    Note, This loads mesh objects and materials only, nurbs and curves are not supported.
    
    http://wiki.blender.org/index.php/Scripts/Manual/Import/wavefront_obj
    """
    
    import os
    import time
    import bpy
    import mathutils
    from mathutils.geometry import tesselate_polygon
    from io_utils import load_image, unpack_list, unpack_face_list
    
    
    
    Campbell Barton's avatar
    Campbell Barton committed
    def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS=True):
    
        '''
        Takes a polyline of indices (fgon)
        and returns a list of face indicie lists.
        Designed to be used for importers that need indices for an fgon to create from existing verts.
    
        from_data: either a mesh, or a list/tuple of vectors.
    
        indices: a list of indices to use this list is the ordered closed polyline to fill, and can be a subset of the data given.
    
        PREF_FIX_LOOPS: If this is enabled polylines that use loops to make multiple polylines are delt with correctly.
        '''
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        if not set:  # Need sets for this, otherwise do a normal fill.
            PREF_FIX_LOOPS = False
    
    Campbell Barton's avatar
    Campbell Barton committed
        Vector = mathutils.Vector
    
    Campbell Barton's avatar
    Campbell Barton committed
        def rvec(co):
            return round(co.x, 6), round(co.y, 6), round(co.z, 6)
    
        def mlen(co):
            return abs(co[0]) + abs(co[1]) + abs(co[2])  # manhatten length of a vector, faster then length
    
    
        def vert_treplet(v, i):
            return v, rvec(v), i, mlen(v)
    
        def ed_key_mlen(v1, v2):
            if v1[3] > v2[3]:
                return v2[1], v1[1]
            else:
                return v1[1], v2[1]
    
        if not PREF_FIX_LOOPS:
            '''
            Normal single concave loop filling
            '''
            if type(from_data) in (tuple, list):
    
    Campbell Barton's avatar
    Campbell Barton committed
                verts = [Vector(from_data[i]) for ii, i in enumerate(indices)]
    
    Campbell Barton's avatar
    Campbell Barton committed
                verts = [from_data.vertices[i].co for ii, i in enumerate(indices)]
    
    Campbell Barton's avatar
    Campbell Barton committed
            for i in range(len(verts) - 1, 0, -1):  # same as reversed(xrange(1, len(verts))):
                if verts[i][1] == verts[i - 1][0]:
                    verts.pop(i - 1)
    
    Campbell Barton's avatar
    Campbell Barton committed
            fill = fill_polygon([verts])
    
    
        else:
            '''
            Seperate this loop into multiple loops be finding edges that are used twice
            This is used by lightwave LWO files a lot
            '''
    
            if type(from_data) in (tuple, list):
    
    Campbell Barton's avatar
    Campbell Barton committed
                verts = [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)]
    
    Campbell Barton's avatar
    Campbell Barton committed
                verts = [vert_treplet(from_data.vertices[i].co, ii) for ii, i in enumerate(indices)]
    
    Campbell Barton's avatar
    Campbell Barton committed
            edges = [(i, i - 1) for i in range(len(verts))]
    
    Campbell Barton's avatar
    Campbell Barton committed
                edges[0] = (0, len(verts) - 1)
    
    Campbell Barton's avatar
    Campbell Barton committed
            edges_used = set()
            edges_doubles = set()
    
            # We need to check if any edges are used twice location based.
            for ed in edges:
    
    Campbell Barton's avatar
    Campbell Barton committed
                edkey = ed_key_mlen(verts[ed[0]], verts[ed[1]])
    
                if edkey in edges_used:
                    edges_doubles.add(edkey)
                else:
                    edges_used.add(edkey)
    
            # Store a list of unconnected loop segments split by double edges.
            # will join later
    
    Campbell Barton's avatar
    Campbell Barton committed
            loop_segments = []
    
    Campbell Barton's avatar
    Campbell Barton committed
            v_prev = verts[0]
            context_loop = [v_prev]
            loop_segments = [context_loop]
    
    Campbell Barton's avatar
    Campbell Barton committed
                if v != v_prev:
    
                    # Are we crossing an edge we removed?
                    if ed_key_mlen(v, v_prev) in edges_doubles:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        context_loop = [v]
    
                        loop_segments.append(context_loop)
                    else:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        if context_loop and context_loop[-1][1] == v[1]:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    v_prev = v
    
    Campbell Barton's avatar
    Campbell Barton committed
            def join_seg(s1, s2):
                if s2[-1][1] == s1[0][1]:
                    s1, s2 = s2, s1
                elif s1[-1][1] == s2[0][1]:
    
                    pass
                else:
                    return False
    
                # If were stuill here s1 and s2 are 2 segments in the same polyline
    
    Campbell Barton's avatar
    Campbell Barton committed
                s1.pop()  # remove the last vert from s1
                s1.extend(s2)  # add segment 2 to segment 1
    
    Campbell Barton's avatar
    Campbell Barton committed
                if s1[0][1] == s1[-1][1]:  # remove endpoints double
    
    Campbell Barton's avatar
    Campbell Barton committed
                s2[:] = []  # Empty this segment s2 so we dont use it again.
    
    Campbell Barton's avatar
    Campbell Barton committed
            joining_segments = True
    
    Campbell Barton's avatar
    Campbell Barton committed
                joining_segments = False
                segcount = len(loop_segments)
    
    Campbell Barton's avatar
    Campbell Barton committed
                for j in range(segcount - 1, -1, -1):  # reversed(range(segcount)):
                    seg_j = loop_segments[j]
    
    Campbell Barton's avatar
    Campbell Barton committed
                        for k in range(j - 1, -1, -1):  # reversed(range(j)):
    
    Campbell Barton's avatar
    Campbell Barton committed
                            seg_k = loop_segments[k]
    
    
                            if seg_k and join_seg(seg_j, seg_k):
    
    Campbell Barton's avatar
    Campbell Barton committed
                                joining_segments = True
    
    Campbell Barton's avatar
    Campbell Barton committed
            loop_list = loop_segments
    
    Campbell Barton's avatar
    Campbell Barton committed
                while verts and verts[0][1] == verts[-1][1]:
    
    Campbell Barton's avatar
    Campbell Barton committed
            loop_list = [verts for verts in loop_list if len(verts) > 2]
    
            # DONE DEALING WITH LOOP FIXING
    
            # vert mapping
    
    Campbell Barton's avatar
    Campbell Barton committed
            vert_map = [None] * len(indices)
            ii = 0
    
    Campbell Barton's avatar
    Campbell Barton committed
                if len(verts) > 2:
    
                    for i, vert in enumerate(verts):
    
    Campbell Barton's avatar
    Campbell Barton committed
                        vert_map[i + ii] = vert[2]
                    ii += len(verts)
    
    Campbell Barton's avatar
    Campbell Barton committed
            fill = tesselate_polygon([[v[0] for v in loop] for loop in loop_list])
    
            #draw_loops(loop_list)
            #raise 'done loop'
    
            # map to original indices
    
    Campbell Barton's avatar
    Campbell Barton committed
            fill = [[vert_map[i] for i in reversed(f)] for f in fill]
    
    
        if not fill:
            print('Warning Cannot scanfill, fallback on a triangle fan.')
    
    Campbell Barton's avatar
    Campbell Barton committed
            fill = [[0, i - 1, i] for i in range(2, len(indices))]
    
        else:
            # Use real scanfill.
            # See if its flipped the wrong way.
    
    Campbell Barton's avatar
    Campbell Barton committed
            flip = None
    
            for fi in fill:
                if flip != None:
                    break
                for i, vi in enumerate(fi):
    
    Campbell Barton's avatar
    Campbell Barton committed
                    if vi == 0 and fi[i - 1] == 1:
                        flip = False
    
    Campbell Barton's avatar
    Campbell Barton committed
                    elif vi == 1 and fi[i - 1] == 0:
                        flip = True
    
                        break
    
            if not flip:
                for i, fi in enumerate(fill):
    
    Campbell Barton's avatar
    Campbell Barton committed
                    fill[i] = tuple([ii for ii in reversed(fi)])
    
    def line_value(line_split):
        '''
        Returns 1 string represneting the value for this line
        None will be returned if theres only 1 word
        '''
    
    Campbell Barton's avatar
    Campbell Barton committed
        length = len(line_split)
    
        if length == 1:
            return None
    
        elif length == 2:
            return line_split[1]
    
        elif length > 2:
    
    Campbell Barton's avatar
    Campbell Barton committed
            return ' '.join(line_split[1:])
    
    
    
    def obj_image_load(imagepath, DIR, IMAGE_SEARCH):
        if '_' in imagepath:
    
    Campbell Barton's avatar
    Campbell Barton committed
            image = load_image(imagepath.replace('_', ' '), DIR)
    
            if image:
                return image
    
        image = load_image(imagepath, DIR)
        if image:
            return image
    
    
        print("failed to load %r doesn't exist" % imagepath)
    
        return None
    
    # def obj_image_load(imagepath, DIR, IMAGE_SEARCH):
    #     '''
    #     Mainly uses comprehensiveImageLoad
    #     but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores.
    #     '''
    
    #     if '_' in imagepath:
    #         image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
    #         if image: return image
    #         # Did the exporter rename the image?
    #         image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
    #         if image: return image
    
    #     # Return an image, placeholder if it dosnt exist
    #     image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH)
    #     return image
    
    
    def create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH):
        '''
        Create all the used materials in this obj,
        assign colors and images to the materials from all referenced material libs
        '''
    
    Campbell Barton's avatar
    Campbell Barton committed
        DIR = os.path.dirname(filepath)
    
    
        #==================================================================================#
        # This function sets textures defined in .mtl file                                 #
        #==================================================================================#
        def load_material_image(blender_material, context_material_name, imagepath, type):
    
    
    Campbell Barton's avatar
    Campbell Barton committed
            texture = bpy.data.textures.new(name=type, type='IMAGE')
    
    
            # Absolute path - c:\.. etc would work here
            image = obj_image_load(imagepath, DIR, IMAGE_SEARCH)
            has_data = False
    
            if image:
                texture.image = image
                has_data = image.has_data
    
            # Adds textures for materials (rendering)
            if type == 'Kd':
                if has_data and image.depth == 32:
                    # Image has alpha
    
                    mtex = blender_material.texture_slots.add()
                    mtex.texture = texture
                    mtex.texture_coords = 'UV'
                    mtex.use_map_color_diffuse = True
                    mtex.use_map_alpha = True
    
    
                    texture.use_mipmap = True
                    texture.use_interpolation = True
    
                    texture.use_alpha = True
                    blender_material.use_transparency = True
                    blender_material.alpha = 0.0
                else:
                    mtex = blender_material.texture_slots.add()
                    mtex.texture = texture
                    mtex.texture_coords = 'UV'
                    mtex.use_map_color_diffuse = True
    
                # adds textures to faces (Textured/Alt-Z mode)
                # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
    
    Campbell Barton's avatar
    Campbell Barton committed
                unique_material_images[context_material_name] = image, has_data  # set the texface image
    
    
            elif type == 'Ka':
                mtex = blender_material.texture_slots.add()
                mtex.texture = texture
                mtex.texture_coords = 'UV'
                mtex.use_map_ambient = True
    
            elif type == 'Ks':
                mtex = blender_material.texture_slots.add()
                mtex.texture = texture
                mtex.texture_coords = 'UV'
                mtex.use_map_specular = True
    
            elif type == 'Bump':
                mtex = blender_material.texture_slots.add()
                mtex.texture = texture
                mtex.texture_coords = 'UV'
                mtex.use_map_normal = True
    
            elif type == 'D':
                mtex = blender_material.texture_slots.add()
                mtex.texture = texture
                mtex.texture_coords = 'UV'
                mtex.use_map_alpha = True
                blender_material.use_transparency = True
                blender_material.transparency_method = 'Z_TRANSPARENCY'
                blender_material.alpha = 0.0
                # Todo, unset deffuse material alpha if it has an alpha channel
    
            elif type == 'refl':
                mtex = blender_material.texture_slots.add()
                mtex.texture = texture
                mtex.texture_coords = 'UV'
                mtex.use_map_reflect = True
    
                
            else:
                raise Exception("invalid type '%s'" % type)
    
    
        # Add an MTL with the same name as the obj if no MTLs are spesified.
        temp_mtl = os.path.splitext((os.path.basename(filepath)))[0] + '.mtl'
    
        if os.path.exists(os.path.join(DIR, temp_mtl)) and temp_mtl not in material_libs:
    
    Campbell Barton's avatar
    Campbell Barton committed
            material_libs.append(temp_mtl)
    
    Campbell Barton's avatar
    Campbell Barton committed
        for name in unique_materials:  # .keys()
    
    Campbell Barton's avatar
    Campbell Barton committed
                unique_materials[name] = bpy.data.materials.new(name)
                unique_material_images[name] = None, False  # assign None to all material images to start with, add to later.
    
    Campbell Barton's avatar
    Campbell Barton committed
        unique_materials[None] = None
        unique_material_images[None] = None, False
    
    Campbell Barton's avatar
    Campbell Barton committed
            mtlpath = os.path.join(DIR, libname)
    
            if not os.path.exists(mtlpath):
                print ("\tError Missing MTL: '%s'" % mtlpath)
            else:
                #print '\t\tloading mtl: "%s"' % mtlpath
    
    Campbell Barton's avatar
    Campbell Barton committed
                context_material = None
                mtl = open(mtlpath, 'rU')
                for line in mtl:  # .readlines():
    
    Campbell Barton's avatar
    Campbell Barton committed
                        context_material_name = line_value(line.split())
    
                        if context_material_name in unique_materials:
    
    Campbell Barton's avatar
    Campbell Barton committed
                            context_material = unique_materials[context_material_name]
    
                        else:
                            context_material = None
    
                    elif context_material:
                        # we need to make a material to assign properties to it.
    
    Campbell Barton's avatar
    Campbell Barton committed
                        line_split = line.split()
                        line_lower = line.lower().lstrip()
    
                        if line_lower.startswith('ka'):
                            context_material.mirror_color = float(line_split[1]), float(line_split[2]), float(line_split[3])
                        elif line_lower.startswith('kd'):
                            context_material.diffuse_color = float(line_split[1]), float(line_split[2]), float(line_split[3])
                        elif line_lower.startswith('ks'):
                            context_material.specular_color = float(line_split[1]), float(line_split[2]), float(line_split[3])
                        elif line_lower.startswith('ns'):
                            context_material.specular_hardness = int((float(line_split[1]) * 0.51))
    
    Campbell Barton's avatar
    Campbell Barton committed
                        elif line_lower.startswith('ni'):  # Refraction index
    
                            context_material.raytrace_transparency.ior = max(1, min(float(line_split[1]), 3))  # between 1 and 3
                        elif line_lower.startswith('d') or line_lower.startswith('tr'):
                            context_material.alpha = float(line_split[1])
                            context_material.use_transparency = True
                            context_material.transparency_method = 'Z_TRANSPARENCY'
                        elif line_lower.startswith('map_ka'):
    
    Campbell Barton's avatar
    Campbell Barton committed
                            img_filepath = line_value(line.split())
    
                            if img_filepath:
                                load_material_image(context_material, context_material_name, img_filepath, 'Ka')
                        elif line_lower.startswith('map_ks'):
    
    Campbell Barton's avatar
    Campbell Barton committed
                            img_filepath = line_value(line.split())
    
                            if img_filepath:
                                load_material_image(context_material, context_material_name, img_filepath, 'Ks')
                        elif line_lower.startswith('map_kd'):
    
    Campbell Barton's avatar
    Campbell Barton committed
                            img_filepath = line_value(line.split())
    
                            if img_filepath:
                                load_material_image(context_material, context_material_name, img_filepath, 'Kd')
    
                        elif line_lower.startswith('map_bump') or line_lower.startswith('bump'): # 'bump' is incorrect but some files use it.
    
    Campbell Barton's avatar
    Campbell Barton committed
                            img_filepath = line_value(line.split())
    
                            if img_filepath:
                                load_material_image(context_material, context_material_name, img_filepath, 'Bump')
                        elif line_lower.startswith('map_d') or line_lower.startswith('map_tr'):  # Alpha map - Dissolve
    
    Campbell Barton's avatar
    Campbell Barton committed
                            img_filepath = line_value(line.split())
    
                            if img_filepath:
                                load_material_image(context_material, context_material_name, img_filepath, 'D')
    
                        elif line_lower.startswith('refl'):  # reflectionmap
    
    Campbell Barton's avatar
    Campbell Barton committed
                            img_filepath = line_value(line.split())
    
                            if img_filepath:
                                load_material_image(context_material, context_material_name, img_filepath, 'refl')
                mtl.close()
    
    
    def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP):
        '''
        Takes vert_loc and faces, and separates into multiple sets of
        (verts_loc, faces, unique_materials, dataname)
        '''
    
        filename = os.path.splitext((os.path.basename(filepath)))[0]
    
        if not SPLIT_OB_OR_GROUP:
            # use the filename for the object name since we arnt chopping up the mesh.
            return [(verts_loc, faces, unique_materials, filename)]
    
        def key_to_name(key):
            # if the key is a tuple, join it to make a string
            if not key:
    
    Campbell Barton's avatar
    Campbell Barton committed
                return filename  # assume its a string. make sure this is true if the splitting code is changed
    
            else:
                return key
    
        # Return a key that makes the faces unique.
    
    Campbell Barton's avatar
    Campbell Barton committed
        face_split_dict = {}
    
    Campbell Barton's avatar
    Campbell Barton committed
        oldkey = -1  # initialize to a value that will never match the key
    
    Campbell Barton's avatar
    Campbell Barton committed
            key = face[4]
    
    
            if oldkey != key:
                # Check the key has changed.
                try:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    verts_split, faces_split, unique_materials_split, vert_remap = face_split_dict[key]
    
    Campbell Barton's avatar
    Campbell Barton committed
                    faces_split = []
                    verts_split = []
                    unique_materials_split = {}
                    vert_remap = [-1] * len(verts_loc)
    
    Campbell Barton's avatar
    Campbell Barton committed
                    face_split_dict[key] = (verts_split, faces_split, unique_materials_split, vert_remap)
    
    Campbell Barton's avatar
    Campbell Barton committed
                oldkey = key
    
    Campbell Barton's avatar
    Campbell Barton committed
            face_vert_loc_indices = face[0]
    
    
            # Remap verts to new vert list and add where needed
    
            for enum, i in enumerate(face_vert_loc_indices):
    
    Campbell Barton's avatar
    Campbell Barton committed
                    new_index = len(verts_split)
                    vert_remap[i] = new_index  # set the new remapped index so we only add once and can reference next time.
                    face_vert_loc_indices[enum] = new_index  # remap to the local index
                    verts_split.append(verts_loc[i])  # add the vert to the local verts
    
    Campbell Barton's avatar
    Campbell Barton committed
                    face_vert_loc_indices[enum] = vert_remap[i]  # remap to the local index
    
    Campbell Barton's avatar
    Campbell Barton committed
                matname = face[2]
    
                if matname and matname not in unique_materials_split:
                    unique_materials_split[matname] = unique_materials[matname]
    
            faces_split.append(face)
    
        # remove one of the itemas and reorder
        return [(value[0], value[1], value[2], key_to_name(key)) for key, value in list(face_split_dict.items())]
    
    
    def create_mesh(new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname):
        '''
        Takes all the data gathered and generates a mesh, adding the new object to new_objects
        deals with fgons, sharp edges and assigning materials
        '''
        if not has_ngons:
    
    Campbell Barton's avatar
    Campbell Barton committed
            CREATE_FGONS = False
    
    Campbell Barton's avatar
    Campbell Barton committed
            sharp_edges = {}
    
            smooth_group_users = {context_smooth_group: {} for context_smooth_group in list(unique_smooth_groups.keys())}
    
    Campbell Barton's avatar
    Campbell Barton committed
            context_smooth_group_old = -1
    
    Campbell Barton's avatar
    Campbell Barton committed
        fgon_edges = {}  # Used for storing fgon keys
    
    Campbell Barton's avatar
    Campbell Barton committed
            edges = []
    
    Campbell Barton's avatar
    Campbell Barton committed
        context_object = None
    
        # reverse loop through face indices
    
    Campbell Barton's avatar
    Campbell Barton committed
        for f_idx in range(len(faces) - 1, -1, -1):
    
            face_vert_loc_indices,\
            face_vert_tex_indices,\
    
            context_material,\
            context_smooth_group,\
    
    Campbell Barton's avatar
    Campbell Barton committed
            context_object = faces[f_idx]
    
            len_face_vert_loc_indices = len(face_vert_loc_indices)
    
    Campbell Barton's avatar
    Campbell Barton committed
            if len_face_vert_loc_indices == 1:
                faces.pop(f_idx)  # cant add single vert faces
    
    Campbell Barton's avatar
    Campbell Barton committed
            elif not face_vert_tex_indices or len_face_vert_loc_indices == 2:  # faces that have no texture coords are lines
    
                if CREATE_EDGES:
                    # generators are better in python 2.4+ but can't be used in 2.3
    
                    # edges.extend( (face_vert_loc_indices[i], face_vert_loc_indices[i+1]) for i in xrange(len_face_vert_loc_indices-1) )
    
    Campbell Barton's avatar
    Campbell Barton committed
                    edges.extend([(face_vert_loc_indices[i], face_vert_loc_indices[i + 1]) for i in range(len_face_vert_loc_indices - 1)])
    
    
                faces.pop(f_idx)
            else:
    
                # Smooth Group
                if unique_smooth_groups and context_smooth_group:
                    # Is a part of of a smooth group and is a face
                    if context_smooth_group_old is not context_smooth_group:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        edge_dict = smooth_group_users[context_smooth_group]
                        context_smooth_group_old = context_smooth_group
    
                    for i in range(len_face_vert_loc_indices):
    
    Campbell Barton's avatar
    Campbell Barton committed
                        i1 = face_vert_loc_indices[i]
                        i2 = face_vert_loc_indices[i - 1]
                        if i1 > i2:
                            i1, i2 = i2, i1
    
    Campbell Barton's avatar
    Campbell Barton committed
                            edge_dict[i1, i2] += 1
    
    Campbell Barton's avatar
    Campbell Barton committed
                            edge_dict[i1, i2] = 1
    
                if has_ngons and len_face_vert_loc_indices > 4:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    ngon_face_indices = BPyMesh_ngon(verts_loc, face_vert_loc_indices)
    
    Campbell Barton's avatar
    Campbell Barton committed
                        [face_vert_loc_indices[ngon[0]], face_vert_loc_indices[ngon[1]], face_vert_loc_indices[ngon[2]]],
                        [face_vert_tex_indices[ngon[0]], face_vert_tex_indices[ngon[1]], face_vert_tex_indices[ngon[2]]],
    
                        context_material,
                        context_smooth_group,
                        context_object)
                        for ngon in ngon_face_indices]
                    )
    
                    # edges to make fgons
                    if CREATE_FGONS:
    
    Campbell Barton's avatar
    Campbell Barton committed
                        edge_users = {}
    
    Campbell Barton's avatar
    Campbell Barton committed
                            for i in (0, 1, 2):
                                i1 = face_vert_loc_indices[ngon[i]]
                                i2 = face_vert_loc_indices[ngon[i - 1]]
                                if i1 > i2:
                                    i1, i2 = i2, i1
    
    Campbell Barton's avatar
    Campbell Barton committed
                                    edge_users[i1, i2] += 1
    
    Campbell Barton's avatar
    Campbell Barton committed
                                    edge_users[i1, i2] = 1
    
    
                        for key, users in edge_users.items():
    
    Campbell Barton's avatar
    Campbell Barton committed
                            if users > 1:
                                fgon_edges[key] = None
    
    
                    # remove all after 3, means we dont have to pop this one.
                    faces.pop(f_idx)
    
        # Build sharp edges
        if unique_smooth_groups:
            for edge_dict in list(smooth_group_users.values()):
                for key, users in list(edge_dict.items()):
    
    Campbell Barton's avatar
    Campbell Barton committed
                    if users == 1:  # This edge is on the boundry of a group
                        sharp_edges[key] = None
    
    
        # map the material names to an index
    
    Campbell Barton's avatar
    Campbell Barton committed
        material_mapping = {name: i for i, name in enumerate(unique_materials)}  # enumerate over unique_materials keys()
    
    Campbell Barton's avatar
    Campbell Barton committed
        materials = [None] * len(unique_materials)
    
    
        for name, index in list(material_mapping.items()):
    
    Campbell Barton's avatar
    Campbell Barton committed
            materials[index] = unique_materials[name]
    
    Campbell Barton's avatar
    Campbell Barton committed
        me = bpy.data.meshes.new(dataname)
    
    
        # make sure the list isnt too big
        for material in materials:
            me.materials.append(material)
    
        me.vertices.add(len(verts_loc))
        me.faces.add(len(faces))
    
        # verts_loc is a list of (x, y, z) tuples
        me.vertices.foreach_set("co", unpack_list(verts_loc))
    
        # faces is a list of (vert_indices, texco_indices, ...) tuples
        # XXX faces should contain either 3 or 4 verts
        # XXX no check for valid face indices
        me.faces.foreach_set("vertices_raw", unpack_face_list([f[0] for f in faces]))
    
        if verts_tex and me.faces:
            me.uv_textures.new()
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        context_material_old = -1  # avoid a dict lookup
        mat = 0  # rare case it may be un-initialized.
        me_faces = me.faces
    
    
        for i, face in enumerate(faces):
            if len(face[0]) < 2:
    
    Campbell Barton's avatar
    Campbell Barton committed
                pass  # raise "bad face"
            elif len(face[0]) == 2:
    
                if CREATE_EDGES:
                    edges.append(face[0])
            else:
    
                    blender_face = me.faces[i]
    
    
                    face_vert_loc_indices,\
                    face_vert_tex_indices,\
    
                    context_material,\
                    context_smooth_group,\
    
    Campbell Barton's avatar
    Campbell Barton committed
                    context_object = face
    
    
                    if context_smooth_group:
                        blender_face.use_smooth = True
    
                    if context_material:
                        if context_material_old is not context_material:
    
    Campbell Barton's avatar
    Campbell Barton committed
                            mat = material_mapping[context_material]
                            context_material_old = context_material
    
    Campbell Barton's avatar
    Campbell Barton committed
                        blender_face.material_index = mat
    
    Campbell Barton's avatar
    Campbell Barton committed
                        blender_tface = me.uv_textures[0].data[i]
    
    
                        if context_material:
                            image, has_data = unique_material_images[context_material]
    
    Campbell Barton's avatar
    Campbell Barton committed
                            if image:  # Can be none if the material dosnt have an image.
    
                                blender_tface.image = image
                                blender_tface.use_image = True
                                if has_data and image.depth == 32:
                                    blender_tface.blend_type = 'ALPHA'
    
                        # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled.
    
    Campbell Barton's avatar
    Campbell Barton committed
                        if len(face_vert_loc_indices) == 4:
                            if face_vert_loc_indices[2] == 0 or face_vert_loc_indices[3] == 0:
                                face_vert_tex_indices = face_vert_tex_indices[2], face_vert_tex_indices[3], face_vert_tex_indices[0], face_vert_tex_indices[1]
                        else:  # length of 3
                            if face_vert_loc_indices[2] == 0:
                                face_vert_tex_indices = face_vert_tex_indices[1], face_vert_tex_indices[2], face_vert_tex_indices[0]
    
                        # END EEEKADOODLE FIX
    
                        # assign material, uv's and image
    
    Campbell Barton's avatar
    Campbell Barton committed
                        blender_tface.uv1 = verts_tex[face_vert_tex_indices[0]]
                        blender_tface.uv2 = verts_tex[face_vert_tex_indices[1]]
                        blender_tface.uv3 = verts_tex[face_vert_tex_indices[2]]
    
    Campbell Barton's avatar
    Campbell Barton committed
                        if len(face_vert_loc_indices) == 4:
                            blender_tface.uv4 = verts_tex[face_vert_tex_indices[3]]
    
    
    #                     for ii, uv in enumerate(blender_face.uv):
    
    #                         uv.x, uv.y=  verts_tex[face_vert_tex_indices[ii]]
    
        del me_faces
    #     del ALPHA
    
        if CREATE_EDGES and not edges:
            CREATE_EDGES = False
    
        if CREATE_EDGES:
            me.edges.add(len(edges))
    
            # edges should be a list of (a, b) tuples
            me.edges.foreach_set("vertices", unpack_list(edges))
    #         me_edges.extend( edges )
    
    #     del me_edges
    
        # Add edge faces.
    #     me_edges= me.edges
    
        def edges_match(e1, e2):
            return (e1[0] == e2[0] and e1[1] == e2[1]) or (e1[0] == e2[1] and e1[1] == e2[0])
    
        # XXX slow
    #     if CREATE_FGONS and fgon_edges:
    #         for fgon_edge in fgon_edges.keys():
    #             for ed in me.edges:
    #                 if edges_match(fgon_edge, ed.vertices):
    #                     ed.is_fgon = True
    
    #     if CREATE_FGONS and fgon_edges:
    #         FGON= Mesh.EdgeFlags.FGON
    #         for ed in me.findEdges( fgon_edges.keys() ):
    #             if ed is not None:
    #                 me_edges[ed].flag |= FGON
    #         del FGON
    
        # XXX slow
    #     if unique_smooth_groups and sharp_edges:
    #         for sharp_edge in sharp_edges.keys():
    #             for ed in me.edges:
    #                 if edges_match(sharp_edge, ed.vertices):
    #                     ed.use_edge_sharp = True
    
    #     if unique_smooth_groups and sharp_edges:
    #         SHARP= Mesh.EdgeFlags.SHARP
    #         for ed in me.findEdges( sharp_edges.keys() ):
    #             if ed is not None:
    #                 me_edges[ed].flag |= SHARP
    #         del SHARP
    
        me.update(calc_edges=CREATE_EDGES)
    #     me.calcNormals()
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        ob = bpy.data.objects.new("Mesh", me)
    
        new_objects.append(ob)
    
        # Create the vertex groups. No need to have the flag passed here since we test for the
        # content of the vertex_groups. If the user selects to NOT have vertex groups saved then
        # the following test will never run
    
        for group_name, group_indices in vertex_groups.items():
    
            group = ob.vertex_groups.new(group_name)
    
            group.add(group_indices, 1.0, 'REPLACE')
    
    
    
    def create_nurbs(context_nurbs, vert_loc, new_objects):
        '''
        Add nurbs object to blender, only support one type at the moment
        '''
        deg = context_nurbs.get('deg', (3,))
        curv_range = context_nurbs.get('curv_range')
        curv_idx = context_nurbs.get('curv_idx', [])
        parm_u = context_nurbs.get('parm_u', [])
        parm_v = context_nurbs.get('parm_v', [])
        name = context_nurbs.get('name', 'ObjNurb')
        cstype = context_nurbs.get('cstype')
    
        if cstype is None:
            print('\tWarning, cstype not found')
            return
        if cstype != 'bspline':
            print('\tWarning, cstype is not supported (only bspline)')
            return
        if not curv_idx:
            print('\tWarning, curv argument empty or not set')
            return
        if len(deg) > 1 or parm_v:
            print('\tWarning, surfaces not supported')
            return
    
        cu = bpy.data.curves.new(name, 'CURVE')
        cu.dimensions = '3D'
    
        nu = cu.splines.new('NURBS')
    
    Campbell Barton's avatar
    Campbell Barton committed
        nu.points.add(len(curv_idx) - 1)  # a point is added to start with
    
        nu.points.foreach_set("co", [co_axis for vt_idx in curv_idx for co_axis in (vert_loc[vt_idx] + (1.0,))])
    
        nu.order_u = deg[0] + 1
    
        # get for endpoint flag from the weighting
    
    Campbell Barton's avatar
    Campbell Barton committed
        if curv_range and len(parm_u) > deg[0] + 1:
    
    Campbell Barton's avatar
    Campbell Barton committed
            for i in range(deg[0] + 1):
    
    Campbell Barton's avatar
    Campbell Barton committed
                if abs(parm_u[i] - curv_range[0]) > 0.0001:
    
    Campbell Barton's avatar
    Campbell Barton committed
                if abs(parm_u[-(i + 1)] - curv_range[1]) > 0.0001:
    
                    do_endpoints = False
                    break
    
        else:
            do_endpoints = False
    
        if do_endpoints:
            nu.use_endpoint_u = True
    
        # close
        '''
        do_closed = False
        if len(parm_u) > deg[0]+1:
            for i in xrange(deg[0]+1):
                #print curv_idx[i], curv_idx[-(i+1)]
    
                if curv_idx[i]==curv_idx[-(i+1)]:
                    do_closed = True
                    break
    
        if do_closed:
            nu.use_cyclic_u = True
        '''
    
    Campbell Barton's avatar
    Campbell Barton committed
    
        ob = bpy.data.objects.new("Nurb", cu)
    
    
        new_objects.append(ob)
    
    
    def strip_slash(line_split):
    
    Campbell Barton's avatar
    Campbell Barton committed
        if line_split[-1][-1] == "\\":
            if len(line_split[-1]) == 1:
                line_split.pop()  # remove the \ item
    
    Campbell Barton's avatar
    Campbell Barton committed
                line_split[-1] = line_split[-1][:-1]  # remove the \ from the end last number
    
            return True
        return False
    
    
    def get_float_func(filepath):
        '''
        find the float function for this obj file
        - whether to replace commas or not
        '''
    
    Campbell Barton's avatar
    Campbell Barton committed
        file = open(filepath, 'rU')
        for line in file:  # .readlines():
    
    Campbell Barton's avatar
    Campbell Barton committed
            if line.startswith('v'):  # vn vt v
    
                if ',' in line:
                    return lambda f: float(f.replace(',', '.'))
                elif '.' in line:
                    return float
    
        # incase all vert values were ints
        return float
    
    
    def load(operator, context, filepath,
    
    Campbell Barton's avatar
    Campbell Barton committed
             CLAMP_SIZE=0.0,
             CREATE_FGONS=True,
             CREATE_SMOOTH_GROUPS=True,
             CREATE_EDGES=True,
             SPLIT_OBJECTS=True,
             SPLIT_GROUPS=True,
             ROTATE_X90=True,
    
             IMAGE_SEARCH=True,
             POLYGROUPS=False):
        '''
        Called by the user interface or another script.
        load_obj(path) - should give acceptable results.
        This function passes the file and sends the data off
            to be split into objects and then converted into mesh objects
        '''
        print('\nimporting obj %r' % filepath)
    
        if SPLIT_OBJECTS or SPLIT_GROUPS:
            POLYGROUPS = False
    
    
    Campbell Barton's avatar
    Campbell Barton committed
        time_main = time.time()
    
    Campbell Barton's avatar
    Campbell Barton committed
        verts_loc = []
        verts_tex = []
        faces = []  # tuples of the faces
        material_libs = []  # filanems to material libs this uses
        vertex_groups = {}  # when POLYGROUPS is true
    
    
        # Get the string to float conversion func for this file- is 'float' for almost all files.
    
    Campbell Barton's avatar
    Campbell Barton committed
        float_func = get_float_func(filepath)
    
    Campbell Barton's avatar
    Campbell Barton committed
        context_material = None
        context_smooth_group = None
        context_object = None
    
        context_vgroup = None
    
        # Nurbs
        context_nurbs = {}
        nurbs = []
    
    Campbell Barton's avatar
    Campbell Barton committed
        context_parm = ''  # used by nurbs too but could be used elsewhere
    
    Campbell Barton's avatar
    Campbell Barton committed
        has_ngons = False
    
        # has_smoothgroups= False - is explicit with len(unique_smooth_groups) being > 0
    
        # Until we can use sets
    
    Campbell Barton's avatar
    Campbell Barton committed
        unique_materials = {}
        unique_material_images = {}
        unique_smooth_groups = {}
    
        # unique_obects= {} - no use for this variable since the objects are stored in the face.
    
        # when there are faces that end with \
        # it means they are multiline-
        # since we use xreadline we cant skip to the next line
        # so we need to know whether
    
    Campbell Barton's avatar
    Campbell Barton committed
        context_multi_line = ""
    
    Campbell Barton's avatar
    Campbell Barton committed
        time_sub = time.time()
    
    Campbell Barton's avatar
    Campbell Barton committed
        file = open(filepath, 'rU')
        for line in file:  # .readlines():
            line = line.lstrip()  # rare cases there is white space at the start of the line
    
    Campbell Barton's avatar
    Campbell Barton committed
            if line.startswith("v "):
                line_split = line.split()
    
    Campbell Barton's avatar
    Campbell Barton committed
                verts_loc.append((float_func(line_split[1]), -float_func(line_split[3]), float_func(line_split[2])))
    
    Campbell Barton's avatar
    Campbell Barton committed
            elif line.startswith("vn "):
    
    Campbell Barton's avatar
    Campbell Barton committed
            elif line.startswith("vt "):
                line_split = line.split()
                verts_tex.append((float_func(line_split[1]), float_func(line_split[2])))
    
    
            # Handel faces lines (as faces) and the second+ lines of fa multiline face here
            # use 'f' not 'f ' because some objs (very rare have 'fo ' for faces)
            elif line.startswith('f') or context_multi_line == 'f':
    
                if context_multi_line:
    
                    # use face_vert_loc_indices and face_vert_tex_indices previously defined and used the obj_face
    
    Campbell Barton's avatar
    Campbell Barton committed
                    line_split = line.split()
    
    Campbell Barton's avatar
    Campbell Barton committed
                    line_split = line[2:].split()
                    face_vert_loc_indices = []
                    face_vert_tex_indices = []
    
                    face_vert_loc_indices,\
                    face_vert_tex_indices,\
    
                    context_material,\
                    context_smooth_group,\
                    context_object\
                    ))
    
                if strip_slash(line_split):
                    context_multi_line = 'f'
                else:
                    context_multi_line = ''
    
                for v in line_split:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    obj_vert = v.split('/')
    
    Campbell Barton's avatar
    Campbell Barton committed
                    vert_loc_index = int(obj_vert[0]) - 1
    
                    # Add the vertex to the current group
                    # *warning*, this wont work for files that have groups defined around verts
                    if    POLYGROUPS and context_vgroup:
                        vertex_groups[context_vgroup].append(vert_loc_index)
    
    
                    # Make relative negative vert indices absolute
    
    Campbell Barton's avatar
    Campbell Barton committed
                        vert_loc_index = len(verts_loc) + vert_loc_index + 1
    
                    face_vert_loc_indices.append(vert_loc_index)
    
    Campbell Barton's avatar
    Campbell Barton committed
                    if len(obj_vert) > 1 and obj_vert[1]:
    
                        # formatting for faces with normals and textures us
                        # loc_index/tex_index/nor_index
    
    
    Campbell Barton's avatar
    Campbell Barton committed
                        vert_tex_index = int(obj_vert[1]) - 1
    
                        # Make relative negative vert indices absolute
    
    Campbell Barton's avatar
    Campbell Barton committed
                            vert_tex_index = len(verts_tex) + vert_tex_index + 1
    
                        face_vert_tex_indices.append(vert_tex_index)
    
                        face_vert_tex_indices.append(0)
    
                if len(face_vert_loc_indices) > 4:
    
    Campbell Barton's avatar
    Campbell Barton committed
                    has_ngons = True
    
    
            elif CREATE_EDGES and (line.startswith('l ') or context_multi_line == 'l'):
                # very similar to the face load function above with some parts removed
    
                if context_multi_line:
    
                    # use face_vert_loc_indices and face_vert_tex_indices previously defined and used the obj_face
    
    Campbell Barton's avatar
    Campbell Barton committed
                    line_split = line.split()