OBJ import support for nurbs curves
authorCampbell Barton <ideasman42@gmail.com>
Fri, 12 Jun 2009 04:16:12 +0000 (04:16 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 12 Jun 2009 04:16:12 +0000 (04:16 +0000)
Imports nurbs with orderU and endpointU (inferred from weights).

No support for vert-weights, surfaces, 2D curves or non-bspline's yet.

fix for exporting closed nurbs, was overlapping too much of the curve.

release/scripts/export_obj.py
release/scripts/import_obj.py

index 5a6d80f45f33947f484b8857c9e3f6422ace58af..a57788abf3f5b55324486188e42fd4ae52ebe168 100644 (file)
@@ -236,9 +236,8 @@ def write_nurb(file, ob, ob_mat):
                                pt_num += 1
                                curve_ls.append(-1)
                        else:
-                               val = DEG_ORDER_U - 1
-                               pt_num += val*2
-                               curve_ls = curve_ls[-val:] + curve_ls + curve_ls[0:val] # Blenders OrderU of 3 -> val==1, 4 -> 2 etc
+                               pt_num += DEG_ORDER_U-1
+                               curve_ls = curve_ls + curve_ls[0:DEG_ORDER_U]
                
                file.write('curv 0.0 1.0 %s\n' % (' '.join( [str(i) for i in curve_ls] ))) # Blender has no U and V values for the curve
                
index 31501173fdab7c76c1d65dcf8a2a5a12e3cc8dc2..5234fe80a91ee440bb540162d9e530468be8159b 100644 (file)
@@ -536,6 +536,96 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l
                me.addVertGroup(group_name)
                me.assignVertsToGroup(group_name, group_indicies,1.00, Mesh.AssignModes.REPLACE)
 
+
+def create_nurbs(scn, 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', None)
+       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', None)
+       
+       if cstype == 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')
+       nu = None
+       for pt in curv_idx:
+               
+               pt = vert_loc[pt]
+               pt = (pt[0], pt[1], pt[2], 1.0)
+               
+               if nu == None:
+                       nu = cu.appendNurb(pt)
+               else:
+                       nu.append(pt)
+       
+       nu.orderU = deg[0]+1
+       
+       # get for endpoint flag from the weighting
+       if curv_range and len(parm_u) > deg[0]+1:
+               do_endpoints = True
+               for i in xrange(deg[0]+1):
+                       
+                       if abs(parm_u[i]-curv_range[0]) > 0.0001:
+                               do_endpoints = False
+                               break
+                       
+                       if abs(parm_u[-(i+1)]-curv_range[1]) > 0.0001:
+                               do_endpoints = False
+                               break
+                       
+       else:
+               do_endpoints = False
+       
+       if do_endpoints:
+               nu.flagU |= 2
+       
+       
+       # 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.flagU |= 1
+       '''
+       
+       ob = scn.objects.new(cu)
+       new_objects.append(ob)
+       
+
+def strip_slash(line_split):
+       if line_split[-1][-1]== '\\':
+               if len(line_split[-1])==1:
+                       line_split.pop() # remove the \ item
+               else:
+                       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
@@ -590,6 +680,11 @@ def load_obj(filepath,
        context_smooth_group= None
        context_object= None
        context_vgroup = None
+       
+       # Nurbs
+       context_nurbs = {}
+       nurbs = []
+       context_parm = '' # used by nurbs too but could be used elsewhere
 
        has_ngons= False
        # has_smoothgroups= False - is explicit with len(unique_smooth_groups) being > 0
@@ -604,7 +699,7 @@ def load_obj(filepath,
        # it means they are multiline- 
        # since we use xreadline we cant skip to the next line
        # so we need to know weather 
-       multi_line_face= False
+       context_multi_line= ''
        
        print '\tparsing obj file "%s"...' % filepath,
        time_sub= sys.time()
@@ -627,12 +722,11 @@ def load_obj(filepath,
                
                # 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 (line.startswith('l ') and CREATE_EDGES) or multi_line_face:
+               elif line.startswith('f') or context_multi_line == 'f':
                        
-                       if multi_line_face:
+                       if context_multi_line:
                                # use face_vert_loc_indicies and face_vert_tex_indicies previously defined and used the obj_face
                                line_split= line.split()
-                               multi_line_face= False
                                
                        else:
                                line_split= line[2:].split()
@@ -648,14 +742,10 @@ def load_obj(filepath,
                                context_object\
                                ))
                        
-                       if line_split[-1][-1]== '\\':
-                               multi_line_face= True
-                               if len(line_split[-1])==1:
-                                       line_split.pop() # remove the \ item
-                               else:
-                                       line_split[-1]= line_split[-1][:-1] # remove the \ from the end last number
-                       
-                       isline= line.startswith('l')
+                       if strip_slash(line_split):
+                               context_multi_line = 'f'
+                       else:
+                               context_multi_line = ''
                        
                        for v in line_split:
                                obj_vert= v.split('/')
@@ -672,24 +762,60 @@ def load_obj(filepath,
                                
                                face_vert_loc_indicies.append(vert_loc_index)
                                
-                               if not isline:
-                                       if len(obj_vert)>1 and obj_vert[1]:
-                                               # formatting for faces with normals and textures us 
-                                               # loc_index/tex_index/nor_index
-                                               
-                                               vert_tex_index= int(obj_vert[1])-1
-                                               # Make relative negative vert indicies absolute
-                                               if vert_tex_index < 0:
-                                                       vert_tex_index= len(verts_tex) + vert_tex_index + 1
-                                               
-                                               face_vert_tex_indicies.append(vert_tex_index)
-                                       else:
-                                               # dummy
-                                               face_vert_tex_indicies.append(0)
+                               if len(obj_vert)>1 and obj_vert[1]:
+                                       # formatting for faces with normals and textures us 
+                                       # loc_index/tex_index/nor_index
+                                       
+                                       vert_tex_index= int(obj_vert[1])-1
+                                       # Make relative negative vert indicies absolute
+                                       if vert_tex_index < 0:
+                                               vert_tex_index= len(verts_tex) + vert_tex_index + 1
+                                       
+                                       face_vert_tex_indicies.append(vert_tex_index)
+                               else:
+                                       # dummy
+                                       face_vert_tex_indicies.append(0)
                        
                        if len(face_vert_loc_indicies) > 4:
                                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_indicies and face_vert_tex_indicies previously defined and used the obj_face
+                               line_split= line.split()
+                               
+                       else:
+                               line_split= line[2:].split()
+                               face_vert_loc_indicies= []
+                               face_vert_tex_indicies= []
+                               
+                               # Instance a face
+                               faces.append((\
+                               face_vert_loc_indicies,\
+                               face_vert_tex_indicies,\
+                               context_material,\
+                               context_smooth_group,\
+                               context_object\
+                               ))
+                       
+                       if strip_slash(line_split):
+                               context_multi_line = 'l'
+                       else:
+                               context_multi_line = ''
+                       
+                       isline= line.startswith('l')
+                       
+                       for v in line_split:
+                               vert_loc_index= int(v)-1
+                               
+                               # Make relative negative vert indicies absolute
+                               if vert_loc_index < 0:
+                                       vert_loc_index= len(verts_loc) + vert_loc_index + 1
+                               
+                               face_vert_loc_indicies.append(vert_loc_index)
+               
                elif line.startswith('s'):
                        if CREATE_SMOOTH_GROUPS:
                                context_smooth_group= line_value(line.split())
@@ -720,6 +846,62 @@ def load_obj(filepath,
                        unique_materials[context_material]= None
                elif line.startswith('mtllib'): # usemap or usemat
                        material_libs.extend( line.split()[1:] ) # can have multiple mtllib filenames per line
+                       
+                       
+                       # Nurbs support
+               elif line.startswith('cstype '):
+                       context_nurbs['cstype']= line_value(line.split()) # 'rat bspline' / 'bspline'
+               elif line.startswith('curv ') or context_multi_line == 'curv':
+                       line_split= line.split()
+                       
+                       curv_idx = context_nurbs['curv_idx'] = context_nurbs.get('curv_idx', []) # incase were multiline
+                       
+                       if not context_multi_line:
+                               context_nurbs['curv_range'] = float_func(line_split[1]), float_func(line_split[2])
+                               line_split[0:3] = [] # remove first 3 items
+                       
+                       if strip_slash(line_split):
+                               context_multi_line = 'curv'
+                       else:
+                               context_multi_line = ''
+                               
+                       
+                       for i in line_split:
+                               vert_loc_index = int(i)-1
+                               
+                               if vert_loc_index < 0:
+                                       vert_loc_index= len(verts_loc) + vert_loc_index + 1
+                               
+                               curv_idx.append(vert_loc_index)
+                       
+               elif line.startswith('parm') or context_multi_line == 'parm':
+                       line_split= line.split()
+                       
+                       if context_multi_line:
+                               context_multi_line = ''
+                       else:
+                               context_parm = line_split[1]
+                               line_split[0:2] = [] # remove first 2
+                       
+                       if strip_slash(line_split):
+                               context_multi_line = 'parm'
+                       else:
+                               context_multi_line = ''
+                       
+                       if context_parm.lower() == 'u':
+                               context_nurbs.setdefault('parm_u', []).extend( [float_func(f) for f in line_split] )
+                       elif context_parm.lower() == 'v': # surfaces not suported yet
+                               context_nurbs.setdefault('parm_v', []).extend( [float_func(f) for f in line_split] )
+                       # else: # may want to support other parm's ?
+               
+               elif line.startswith('deg '):
+                       context_nurbs['deg']= [int(i) for i in line.split()[1:]]
+               elif line.startswith('end'):
+                       # Add the nurbs curve
+                       context_nurbs['name'] = context_object
+                       nurbs.append(context_nurbs)
+                       context_nurbs = {}
+                       context_parm = ''
                
                ''' # How to use usemap? depricated?
                elif line.startswith('usema'): # usemap or usemat
@@ -754,6 +936,11 @@ def load_obj(filepath,
                # Create meshes from the data, warning 'vertex_groups' wont support splitting
                create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname)
        
+       # nurbs support
+       for context_nurbs in nurbs:
+               create_nurbs(scn, context_nurbs, verts_loc, new_objects)
+       
+       
        axis_min= [ 1000000000]*3
        axis_max= [-1000000000]*3
        
@@ -989,34 +1176,28 @@ if __name__=='__main__' and not DEBUG:
        else:
                Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj')
 
-
-'''
 # For testing compatibility
+'''
 else:
        # DEBUG ONLY
        TIME= sys.time()
+       DIR = '/fe/obj'
        import os
        print 'Searching for files'
-       os.system('find /fe/obj -iname "*.obj" > /tmp/temp3ds_list')
-       
-       print '...Done'
-       file= open('/tmp/temp3ds_list', 'rU')
-       lines= file.readlines()
-       file.close()
-
-       def between(v,a,b):
-               if v <= max(a,b) and v >= min(a,b):
-                       return True             
-               return False
-               
-       for i, _obj in enumerate(lines):
-               if between(i, 0,20):
-                       _obj= _obj[:-1]
-                       print 'Importing', _obj, '\nNUMBER', i, 'of', len(lines)
-                       _obj_file= _obj.split('/')[-1].split('\\')[-1]
-                       newScn= bpy.data.scenes.new(_obj_file)
+       def fileList(path):
+               for dirpath, dirnames, filenames in os.walk(path):
+                       for filename in filenames:
+                               yield os.path.join(dirpath, filename)
+       
+       files = [f for f in fileList(DIR) if f.lower().endswith('.obj')]
+       files.sort()
+       
+       for i, obj_file in enumerate(files):
+               if 0 < i < 20:
+                       print 'Importing', obj_file, '\nNUMBER', i, 'of', len(files)
+                       newScn= bpy.data.scenes.new(os.path.basename(obj_file))
                        newScn.makeCurrent()
-                       load_obj(_obj, False)
+                       load_obj(obj_file, False)
 
        print 'TOTAL TIME: %.6f' % (sys.time() - TIME)
 '''