~ubuntu-branches/ubuntu/gutsy/blender/gutsy-security

« back to all changes in this revision

Viewing changes to release/scripts/obj_import.py

  • Committer: Bazaar Package Importer
  • Author(s): Lukas Fittl
  • Date: 2006-09-20 01:57:27 UTC
  • mfrom: (1.2.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20060920015727-gmoqlxwstx9wwqs3
Tags: 2.42a-1ubuntu1
* Merge from Debian unstable (Closes: Malone #55903). Remaining changes:
  - debian/genpot: Add python scripts from Lee June <blender@eyou.com> to
    generate a reasonable PO template from the sources. Since gettext is used
    in a highly nonstandard way, xgettext does not work for this job.
  - debian/rules: Call the scripts, generate po/blender.pot, and clean it up
    in the clean target.
  - Add a proper header to the generated PO template.
* debian/control: Build depend on libavformat-dev >= 3:0.cvs20060823-3.1,
  otherwise this package will FTBFS

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
8
8
"""
9
9
 
10
 
__author__ = "Campbell Barton"
11
 
__url__ = ["blender", "elysiun"]
12
 
__version__ = "1.0"
 
10
__author__= "Campbell Barton"
 
11
__url__= ["blender", "elysiun", "http://members.iinet.net.au/~cpbarton/ideasman/"]
 
12
__version__= "1.0"
13
13
 
14
 
__bpydoc__ = """\
 
14
__bpydoc__= """\
15
15
This script imports OBJ files to Blender.
16
16
 
17
17
Usage:
19
19
Run this script from "File->Import" menu and then load the desired OBJ file.
20
20
"""
21
21
 
22
 
# $Id: obj_import.py,v 1.29 2006/01/18 22:57:55 campbellbarton Exp $
 
22
# $Id: obj_import.py,v 1.44 2006/07/16 13:54:33 campbellbarton Exp $
23
23
#
24
24
# --------------------------------------------------------------------------
25
25
# OBJ Import v1.0 by Campbell Barton (AKA Ideasman)
48
48
# Return directory, where the file is          #
49
49
#==============================================#
50
50
def stripFile(path):
51
 
        lastSlash = max(path.rfind('\\'), path.rfind('/'))
 
51
        lastSlash= max(path.rfind('\\'), path.rfind('/'))
52
52
        if lastSlash != -1:
53
 
                path = path[:lastSlash]
 
53
                path= path[:lastSlash]
54
54
        return '%s%s' % (path, sys.sep)
55
55
 
56
56
#==============================================#
63
63
# Strips the prefix off the name before writing      #
64
64
#====================================================#
65
65
def stripExt(name): # name is a string
66
 
        index = name.rfind('.')
 
66
        index= name.rfind('.')
67
67
        if index != -1:
68
68
                return name[ : index ]
69
69
        else:
71
71
 
72
72
 
73
73
from Blender import *
74
 
 
75
 
 
76
 
 
77
 
# Adds a slash to the end of a path if its not there.
78
 
def addSlash(path):
79
 
        if path.endswith('\\') or path.endswith('/'):
80
 
                return path
81
 
        return path + sys.sep
82
 
        
83
 
 
84
 
def getExt(name):
85
 
        index = name.rfind('.')
86
 
        if index != -1:
87
 
                return name[index+1:]
88
 
        return name
 
74
import BPyImage # use for comprehensiveImageLoad
 
75
import BPyMesh # use for ngon
89
76
 
90
77
try:
91
78
        import os
92
79
except:
93
80
        # So we know if os exists.
94
81
        print 'Module "os" not found, install python to enable comprehensive image finding and batch loading.'
95
 
        os = None
96
 
 
97
 
#===========================================================================#
98
 
# Comprehansive image loader, will search and find the image                #
99
 
# Will return a blender image or none if the image is missing               #
100
 
#===========================================================================#
101
 
def comprehansiveImageLoad(imagePath, filePath):
102
 
        
103
 
        # When we have the file load it with this. try/except niceness.
104
 
        def imageLoad(path):
105
 
                try:
106
 
                        img = Image.Load(path)
107
 
                        print '\t\tImage loaded "%s"' % path
108
 
                        return img
109
 
                except:
110
 
                        print '\t\tImage failed loading "%s", mabe its not a format blender can read.' % (path)
111
 
                        return None
112
 
        
113
 
        # Image formats blender can read
114
 
        IMAGE_EXT = ['jpg', 'jpeg', 'png', 'tga', 'bmp', 'rgb', 'sgi', 'bw', 'iff', 'lbm', # Blender Internal
115
 
        'gif', 'psd', 'tif', 'tiff', 'pct', 'pict', 'pntg', 'qtif'] # Quacktime, worth a try.
116
 
        
117
 
        
118
 
        
119
 
        
120
 
        print '\tAttempting to load "%s"' % imagePath
121
 
        if sys.exists(imagePath):
122
 
                print '\t\tFile found where expected.'
123
 
                return imageLoad(imagePath)
124
 
        
125
 
        imageFileName =  stripPath(imagePath) # image path only
126
 
        imageFileName_lower =  imageFileName.lower() # image path only
127
 
        imageFileName_noext = stripExt(imageFileName) # With no extension.
128
 
        imageFileName_noext_lower = stripExt(imageFileName_lower) # With no extension.
129
 
        imageFilePath = stripFile(imagePath)
130
 
        
131
 
        # Remove relative path from image path
132
 
        if imageFilePath.startswith('./') or imageFilePath.startswith('.\\'):
133
 
                imageFilePath = imageFilePath[2:]
134
 
        
135
 
        
136
 
        # Attempt to load from obj path.
137
 
        tmpPath = stripFile(filePath) + stripFile(imageFilePath)
138
 
        if sys.exists(tmpPath):
139
 
                print '\t\tFile found in obj dir.'
140
 
                return imageLoad(imagePath)
141
 
        
142
 
        # OS NEEDED IF WE GO ANY FURTHER.
143
 
        if not os:
144
 
                return
145
 
        
146
 
        
147
 
        # We have os.
148
 
        # GATHER PATHS.
149
 
        paths = {} # Store possible paths we may use, dict for no doubles.
150
 
        tmpPath = addSlash(sys.expandpath('//')) # Blenders path
151
 
        if sys.exists(tmpPath):
152
 
                print '\t\tSearching in %s' % tmpPath
153
 
                paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
154
 
                paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
155
 
                paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
156
 
                
157
 
        tmpPath = imageFilePath
158
 
        if sys.exists(tmpPath):
159
 
                print '\t\tSearching in %s' % tmpPath
160
 
                paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
161
 
                paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
162
 
                paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
163
 
 
164
 
        tmpPath = stripFile(filePath)
165
 
        if sys.exists(tmpPath):
166
 
                print '\t\tSearching in %s' % tmpPath
167
 
                paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
168
 
                paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
169
 
                paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
170
 
        
171
 
        tmpPath = addSlash(Get('texturesdir'))
172
 
        if tmpPath and sys.exists(tmpPath):
173
 
                print '\t\tSearching in %s' % tmpPath
174
 
                paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
175
 
                paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
176
 
                paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
177
 
        
178
 
        # Add path if relative image patrh was given.
179
 
        for k in paths.iterkeys():
180
 
                tmpPath = k + imageFilePath
181
 
                if sys.exists(tmpPath):
182
 
                        paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
183
 
                        paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
184
 
                        paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
185
 
        # DONE
186
 
        
187
 
        
188
 
        # 
189
 
        for path, files in paths.iteritems():
190
 
                
191
 
                if sys.exists(path + imageFileName):
192
 
                        return imageLoad(path + imageFileName)
193
 
                
194
 
                # If the files not there then well do a case insensitive seek.
195
 
                filesOrigCase = files[0]
196
 
                filesLower = files[1]
197
 
                filesLowerNoExt = files[2]
198
 
                
199
 
                # We are going to try in index the file directly, if its not there just keep on
200
 
                index = None
201
 
                try:
202
 
                        # Is it just a case mismatch?
203
 
                        index = filesLower.index(imageFileName_lower)
204
 
                except:
205
 
                        try:
206
 
                                # Have the extensions changed?
207
 
                                index = filesLowerNoExt.index(imageFileName_noext_lower)
208
 
                                
209
 
                                ext = getExt( filesLower[index] ) # Get the extension of the file that matches all but ext.
210
 
                                
211
 
                                # Check that the ext is useable eg- not a 3ds file :)
212
 
                                if ext.lower() not in IMAGE_EXT:
213
 
                                        index = None
214
 
                        
215
 
                        except:
216
 
                                index = None
217
 
                
218
 
                if index != None:
219
 
                        tmpPath = path + filesOrigCase[index]
220
 
                        img = imageLoad( tmpPath )
221
 
                        if img != None:
222
 
                                print '\t\tImage Found "%s"' % tmpPath
223
 
                                return img
224
 
        
225
 
        
226
 
        # IMAGE NOT FOUND IN ANY OF THE DIRS!, DO A RECURSIVE SEARCH.
227
 
        print '\t\tImage Not Found in any of the dirs, doing a recusrive search'
228
 
        for path in paths.iterkeys():
229
 
                # Were not going to use files
230
 
                
231
 
                
232
 
                #------------------
233
 
                # finds the file starting at the root.
234
 
                #       def findImage(findRoot, imagePath):
235
 
                #W---------------
236
 
                
237
 
                # ROOT, DIRS, FILES
238
 
                pathWalk = os.walk(path)
239
 
                pathList = [True]
240
 
                
241
 
                matchList = [] # Store a list of (match, size), choose the biggest.
242
 
                while True:
243
 
                        try:
244
 
                                pathList  = pathWalk.next()
245
 
                        except:
246
 
                                break
247
 
                        
248
 
                        for file in pathList[2]:
249
 
                                file_lower = file.lower()
250
 
                                # FOUND A MATCH
251
 
                                if (file_lower == imageFileName_lower) or\
252
 
                                (stripExt(file_lower) == imageFileName_noext_lower and getExt(file_lower) in IMAGE_EXT):
253
 
                                        name = pathList[0] + sys.sep + file
254
 
                                        size = os.path.getsize(name)
255
 
                                        print '\t\t\tfound:', name 
256
 
                                        matchList.append( (name, size) )
257
 
                
258
 
                if matchList:
259
 
                        # Sort by file size
260
 
                        matchList.sort(lambda A, B: cmp(B[1], A[1]) )
261
 
                        
262
 
                        print '\t\tFound "%s"' % matchList[0][0]
263
 
                        
264
 
                        # Loop through all we have found
265
 
                        img = None
266
 
                        for match in matchList:
267
 
                                img = imageLoad(match[0]) # 0 - first, 0 - pathname
268
 
                                if img != None:
269
 
                                        break
270
 
                        return img
271
 
        
272
 
        # No go.
273
 
        print '\t\tImage Not Found "%s"' % imagePath
274
 
        return None
275
 
 
276
 
 
277
 
 
278
 
 
279
 
 
280
 
#==================================================================================#
281
 
# This function sets textures defined in .mtl file                                 #
282
 
#==================================================================================#
283
 
# ___ Replaced by comprehensive imahge get
 
82
        os= None
 
83
 
284
84
 
285
85
#==================================================================================#
286
86
# This function sets textures defined in .mtl file                                 #
287
87
#==================================================================================#
288
88
def loadMaterialImage(mat, img_fileName, type, meshDict, dir):
289
 
        TEX_ON_FLAG = NMesh.FaceModes['TEX']
 
89
        TEX_ON_FLAG= NMesh.FaceModes['TEX']
290
90
        
291
 
        texture = Texture.New(type)
 
91
        texture= Texture.New(type)
292
92
        texture.setType('Image')
293
93
        
294
94
        # Absolute path - c:\.. etc would work here
295
 
        image = comprehansiveImageLoad(img_fileName, dir)
 
95
        image= BPyImage.comprehensiveImageLoad(img_fileName, dir)
296
96
        
297
97
        if image:
298
 
                texture.image = image
 
98
                texture.image= image
299
99
                
300
100
        # adds textures to faces (Textured/Alt-Z mode)
301
101
        # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
307
107
                                        # the inline usemat command overides the material Image
308
108
                                        if not f.image:
309
109
                                                f.mode |= TEX_ON_FLAG
310
 
                                                f.image = image
 
110
                                                f.image= image
311
111
        
312
112
        # adds textures for materials (rendering)
313
113
        elif type == 'Ka':
323
123
                mat.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA)                               
324
124
        elif type == 'refl':
325
125
                mat.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF)                         
 
126
 
 
127
 
 
128
#===============================================================================#
 
129
# This gets a mat or creates one of the requested name if none exist.           #
 
130
#===============================================================================#
 
131
def getMat(matName, materialDict):
 
132
        # Make a new mat
 
133
        try: return materialDict[matName]
 
134
        except: pass # Better do any exception
326
135
        
 
136
        try: return materialDict[matName.lower()]
 
137
        except: pass
 
138
                
 
139
        # Do we realy need to keep the dict up to date?, not realy but keeps consuistant.
 
140
        mat= materialDict[matName]= Material.New(matName)
 
141
        return mat
 
142
 
327
143
 
328
144
#==================================================================================#
329
145
# This function loads materials from .mtl file (have to be defined in obj file)    #
330
146
#==================================================================================#
331
 
def load_mtl(dir, mtl_file, meshDict, materialDict):
 
147
def load_mtl(dir, IMPORT_USE_EXISTING_MTL, mtl_file, meshDict, materialDict):
332
148
        
333
 
        #===============================================================================#
334
 
        # This gets a mat or creates one of the requested name if none exist.           #
335
 
        #===============================================================================#
336
 
        def getMat(matName, materialDict):
337
 
                # Make a new mat
338
 
                try:
339
 
                        return materialDict[matName]
340
 
                #except NameError or KeyError:
341
 
                except: # Better do any exception
342
 
                        # Do we realy need to keep the dict up to date?, not realy but keeps consuistant.
343
 
                        materialDict[matName] = Material.New(matName)
344
 
                        return materialDict[matName]
345
 
                
346
 
                        
347
 
        mtl_file = stripPath(mtl_file)
348
 
        mtl_fileName = dir + mtl_file
 
149
        mtl_file= stripPath(mtl_file)
 
150
        mtl_fileName= dir + mtl_file
349
151
        
350
152
        try:
351
153
                fileLines= open(mtl_fileName, 'r').readlines()
356
158
        try:
357
159
                lIdx=0
358
160
                while lIdx < len(fileLines):
359
 
                        l = fileLines[lIdx].split()
 
161
                        l= fileLines[lIdx].split()
360
162
                        
361
163
                        # Detect a line that will be ignored
362
164
                        if len(l) == 0 or l[0].startswith('#'):
363
165
                                pass
364
166
                        elif l[0] == 'newmtl':
365
 
                                currentMat = getMat('_'.join(l[1:]), materialDict) # Material should alredy exist.
 
167
                                currentMat= getMat('_'.join(l[1:]), materialDict) # Material should alredy exist.
366
168
                        elif l[0] == 'Ka':
367
169
                                currentMat.setMirCol((float(l[1]), float(l[2]), float(l[3])))
368
170
                        elif l[0] == 'Kd':
378
180
                        elif l[0] == 'Tr':
379
181
                                currentMat.setAlpha(float(l[1]))
380
182
                        elif l[0] == 'map_Ka':
381
 
                                img_fileName = ' '.join(l[1:])
 
183
                                img_fileName= ' '.join(l[1:])
382
184
                                loadMaterialImage(currentMat, img_fileName, 'Ka', meshDict, dir)
383
185
                        elif l[0] == 'map_Ks':
384
 
                                img_fileName = ' '.join(l[1:])
 
186
                                img_fileName= ' '.join(l[1:])
385
187
                                loadMaterialImage(currentMat, img_fileName, 'Ks', meshDict, dir)
386
188
                        elif l[0] == 'map_Kd':
387
 
                                img_fileName = ' '.join(l[1:])
 
189
                                img_fileName= ' '.join(l[1:])
388
190
                                loadMaterialImage(currentMat, img_fileName, 'Kd', meshDict, dir)
389
191
                        
390
192
                        # new additions
391
193
                        elif l[0] == 'map_Bump': # Bumpmap
392
 
                                img_fileName = ' '.join(l[1:])                  
 
194
                                img_fileName= ' '.join(l[1:])                   
393
195
                                loadMaterialImage(currentMat, img_fileName, 'Bump', meshDict, dir)
394
196
                        elif l[0] == 'map_D': # Alpha map - Dissolve
395
 
                                img_fileName = ' '.join(l[1:])                  
 
197
                                img_fileName= ' '.join(l[1:])                   
396
198
                                loadMaterialImage(currentMat, img_fileName, 'D', meshDict, dir)
397
199
 
398
200
                        elif l[0] == 'refl': # Reflectionmap
399
 
                                img_fileName = ' '.join(l[1:])                  
 
201
                                img_fileName= ' '.join(l[1:])                   
400
202
                                loadMaterialImage(currentMat, img_fileName, 'refl', meshDict, dir)
401
203
                        
402
204
                        lIdx+=1
404
206
                print '\tERROR: Unable to parse MTL file: "%s"' % mtl_file
405
207
                return
406
208
        print '\tUsing MTL: "%s"' % mtl_file
407
 
#===========================================================================#
408
 
# Returns unique name of object/mesh (preserve overwriting existing meshes) #
409
 
#===========================================================================#
410
 
def getUniqueName(name):
411
 
        newName = name[:19] # 19 chars is the longest name.
412
 
        uniqueInt = 0
413
 
        while newName in getUniqueName.uniqueNames:
414
 
                newName = '%s.%.3i' % (name[:15], uniqueInt)
415
 
                uniqueInt +=1
416
 
        getUniqueName.uniqueNames.append(newName)
417
 
        return newName
418
 
getUniqueName.uniqueNames = []
419
209
 
420
210
#==================================================================================#
421
211
# This loads data from .obj file                                                   #
422
212
#==================================================================================#
423
 
def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0):
 
213
def load_obj(\
 
214
        file,\
 
215
        IMPORT_MTL=1,\
 
216
        IMPORT_USE_EXISTING_MTL=0,\
 
217
        IMPORT_CONSTRAIN_BOUNDS=0.0,\
 
218
        IMPORT_ROTATE_X90=0,\
 
219
        IMPORT_EDGES=1,\
 
220
        IMPORT_SMOOTH_ALL=0,\
 
221
        IMPORT_FGON=1,\
 
222
        IMPORT_SMOOTH_GROUPS=0,\
 
223
        IMPORT_MTL_SPLIT=0,\
 
224
        IMPORT_AS_INSTANCE=0):
 
225
 
 
226
        global currentMesh,\
 
227
        currentUsedVertList,\
 
228
        currentUsedVertListSmoothGroup,\
 
229
        meshDict,\
 
230
        contextMeshMatIdx,\
 
231
        currentMaterialMeshMapping
424
232
        
425
233
        print '\nImporting OBJ file: "%s"' % file
426
234
        
427
 
        time1 = sys.time()
428
 
        
429
 
        getUniqueName.uniqueNames.extend( [ob.name for ob in Object.Get()] )
430
 
        getUniqueName.uniqueNames.extend( NMesh.GetNames() )
 
235
        time1= sys.time()
431
236
        
432
237
        # Deselect all objects in the scene.
433
238
        # do this first so we dont have to bother, with objects we import
434
239
        for ob in Scene.GetCurrent().getChildren():
435
 
                ob.sel = 0
436
 
        
437
 
        TEX_OFF_FLAG = ~NMesh.FaceModes['TEX']
 
240
                ob.sel= 0
 
241
        
 
242
        TEX_OFF_FLAG= ~NMesh.FaceModes['TEX']
 
243
        
 
244
        # Used for bounds scaling
 
245
        global BOUNDS
 
246
        BOUNDS= 0.0
438
247
        
439
248
        # Get the file name with no path or .obj
440
 
        fileName = stripExt( stripPath(file) )
441
 
 
442
 
        mtl_fileName = [] # Support multiple mtl files if needed.
443
 
 
444
 
        DIR = stripFile(file)
 
249
        fileName= stripExt( stripPath(file) )
 
250
 
 
251
        mtl_fileName= [] # Support multiple mtl files if needed.
 
252
 
 
253
        DIR= stripFile(file)
445
254
        
446
 
        tempFile = open(file, 'r')
447
 
        fileLines = tempFile.readlines()        
 
255
        tempFile= open(file, 'r')
 
256
        fileLines= tempFile.readlines() 
448
257
        tempFile.close()
449
258
        del tempFile
450
 
        uvMapList = [] # store tuple uv pairs here
 
259
        uvMapList= [] # store tuple uv pairs here
451
260
        
452
261
        # This dummy vert makes life a whole lot easier-
453
262
        # pythons index system then aligns with objs, remove later
454
 
        vertList = [] # Could havea vert but since this is a placeholder theres no Point
 
263
        vertList= [] # Could havea vert but since this is a placeholder theres no Point
455
264
        
456
265
        
457
266
        # Store all imported images in a dict, names are key
458
 
        imageDict = {}
 
267
        imageDict= {}
459
268
        
460
269
        # This stores the index that the current mesh has for the current material.
461
270
        # if the mesh does not have the material then set -1
462
 
        contextMeshMatIdx = -1
463
 
        
464
 
        # Keep this out of the dict for easy accsess.
465
 
        nullMat = Material.New('(null)')
466
 
        
467
 
        currentMat = nullMat # Use this mat.
468
 
        currentImg = None # Null image is a string, otherwise this should be set to an image object.\
 
271
        contextMeshMatIdx= -1
 
272
        
 
273
        
 
274
        currentImg= None # Null image is a string, otherwise this should be set to an image object.\
469
275
        if IMPORT_SMOOTH_ALL:
470
 
                currentSmooth = True
 
276
                currentSmooth= True
471
277
        else:
472
 
                currentSmooth = False
 
278
                currentSmooth= False
473
279
        
474
280
        # Store a list of unnamed names
475
 
        currentUnnamedGroupIdx = 1
476
 
        currentUnnamedObjectIdx = 1
477
 
        
478
 
        quadList = (0, 1, 2, 3)
479
 
        
480
 
        faceQuadVList = [None, None, None, None]
481
 
        faceTriVList = [None, None, None]
 
281
        currentUnnamedGroupIdx= 1
 
282
        currentUnnamedObjectIdx= 1
 
283
        
 
284
        quadList= (0, 1, 2, 3)
 
285
        
 
286
        faceQuadVList= [None, None, None, None]
 
287
        faceTriVList= [None, None, None]
482
288
        
483
289
        #==================================================================================#
484
290
        # Load all verts first (texture verts too)                                         #
485
291
        #==================================================================================#
486
 
 
487
292
        print '\tfile length: %d' % len(fileLines)
488
 
        
489
293
        # Ignore normals and comments.
490
 
        fileLines = [lsplit for l in fileLines if not l.startswith('vn') if not l.startswith('#') for lsplit in (l.split(),) if lsplit]
491
 
        Vert = NMesh.Vert
492
 
        vertList = [Vert(float(l[1]), float(l[2]), float(l[3]) ) for l in fileLines if l[0] == 'v']
493
 
        uvMapList = [(float(l[1]), float(l[2])) for l in fileLines if l[0] == 'vt']
494
 
        smoothingGroups =  dict([('_'.join(l[1:]), None) for l in fileLines if l[0] == 's' ])
495
 
        materialDict =     dict([('_'.join(l[1:]), None) for l in fileLines if l[0] == 'usemtl']) # Store all imported materials as unique dict, names are key
496
 
        print '\tvert:%i  texverts:%i  smoothgroups:%i  materials:%s' % (len(vertList), len(uvMapList), len(smoothingGroups), len(materialDict))
 
294
        fileLines= [lsplit for l in fileLines if not l.startswith('vn') if not l.startswith('#') for lsplit in (l.split(),) if lsplit]
 
295
        
 
296
        
 
297
        if IMPORT_CONSTRAIN_BOUNDS == 0.0:
 
298
                if IMPORT_ROTATE_X90:
 
299
                        def Vert(x,y,z):
 
300
                                return NMesh.Vert(x,-z,y) # rotate 90 about the x axis.
 
301
                else:
 
302
                        Vert= NMesh.Vert
 
303
        else:
 
304
                # Adding a vert also sets the bounds.
 
305
                if IMPORT_ROTATE_X90:
 
306
                        def Vert(x,y,z):
 
307
                                global BOUNDS
 
308
                                BOUNDS= max(BOUNDS, x,y,z)
 
309
                                return NMesh.Vert(x,-z,y) # Rotate X90 Deg.
 
310
                else:
 
311
                        def Vert(x,y,z):
 
312
                                global BOUNDS
 
313
                                BOUNDS= max(BOUNDS, x,y,z)
 
314
                                return NMesh.Vert(x,y,z)
 
315
        
 
316
        try:
 
317
                vertList= [Vert(float(l[1]), float(l[2]), float(l[3]) ) for l in fileLines if l[0] == 'v']
 
318
        except ValueError:
 
319
                # What??? Maya 7 uses "6,45" instead of "6.45"
 
320
                vertList= [Vert(float(l[1].replace(',', '.')), float(l[2].replace(',', '.')), float(l[3].replace(',', '.')) ) for l in fileLines if l[0] == 'v']
 
321
        
 
322
 
 
323
        try:
 
324
                uvMapList= [(float(l[1]), float(l[2])) for l in fileLines if l[0] == 'vt']
 
325
        except ValueError:
 
326
                # Same amazement as above. call that a float?
 
327
                uvMapList= [(float(l[1].replace(',', '.')), float(l[2].replace(',', '.'))) for l in fileLines if l[0] == 'vt']
 
328
                
 
329
        if IMPORT_SMOOTH_GROUPS:
 
330
                smoothingGroups=  dict([('_'.join(l[1:]), None) for l in fileLines if l[0] == 's' ])
 
331
        else:
 
332
                smoothingGroups= {}
 
333
        
 
334
        print '\tvert:%i  texverts:%i  smoothgroups:%i' % (len(vertList), len(uvMapList), len(smoothingGroups))
497
335
        
498
336
        # Replace filelines, Excluding v excludes "v ", "vn " and "vt "
499
 
        
500
337
        # Remove any variables we may have created.
501
338
        try: del _dummy
502
339
        except: pass
510
347
        except: pass
511
348
        del Vert
512
349
        
 
350
        
513
351
        # With negative values this is used a lot. make faster access.
514
 
        len_uvMapList = len(uvMapList)
515
 
        len_vertList = len(vertList)
 
352
        len_uvMapList= len(uvMapList)
 
353
        len_vertList= len(vertList)
516
354
        
517
355
        #  Only want unique keys anyway
518
 
        smoothingGroups['(null)'] = None # Make sure we have at least 1.
519
 
        smoothingGroups = smoothingGroups.keys()
520
 
        print '\tfound %d smoothing groups.' % (len(smoothingGroups) -1)
 
356
        smoothingGroups['(null)']= None # Make sure we have at least 1.
 
357
        smoothingGroups= smoothingGroups.keys()
 
358
        
521
359
        
522
360
        # Add materials to Blender for later is in teh OBJ
523
 
        for k in materialDict.iterkeys():
524
 
                materialDict[k] = Material.New(k)
525
 
        
526
 
        
527
 
        # Make a list of all unused vert indicies that we can copy from
528
 
        VERT_USED_LIST = [0]*len_vertList
 
361
        
 
362
        # Keep this out of the dict for easy accsess.
 
363
        if IMPORT_MTL:
 
364
                materialDict= {}
 
365
                
 
366
                if IMPORT_USE_EXISTING_MTL:
 
367
                        # Add existing materials to the dict./
 
368
                        for mat in Material.Get():
 
369
                                mat_name= mat.name.lower()
 
370
                                
 
371
                                # Try add the name without the .001
 
372
                                if\
 
373
                                len(mat_name)>4 and\
 
374
                                mat_name[-4]=='.' and\
 
375
                                mat_name[-3:].isdigit():
 
376
                                        materialDict[mat_name[-4:]]= mat
 
377
                                
 
378
                                # Add the lower name
 
379
                                materialDict[mat_name]= mat
 
380
                
 
381
                currentMat= nullMat= getMat('(null)', materialDict)
 
382
                # Add all new materials allong the way...
 
383
        
 
384
        
 
385
        # Make a list of all unused vert indices that we can copy from
 
386
        VERT_USED_LIST= [-1]*len_vertList
529
387
        
530
388
        # Here we store a boolean list of which verts are used or not
531
389
        # no we know weather to add them to the current mesh
532
 
        # This is an issue with global vertex indicies being translated to per mesh indicies
 
390
        # This is an issue with global vertex indices being translated to per mesh indices
533
391
        # like blenders, we start with a dummy just like the vert.
534
392
        # -1 means unused, any other value refers to the local mesh index of the vert.
535
393
 
536
394
        # currentObjectName has a char in front of it that determins weather its a group or object.
537
395
        # We ignore it when naming the object.
538
 
        currentObjectName = 'unnamed_obj_0' # If we cant get one, use this
539
 
        
540
 
        #meshDict = {} # The 3 variables below are stored in a tuple within this dict for each mesh
541
 
        currentMesh = NMesh.GetRaw() # The NMesh representation of the OBJ group/Object
542
 
        #currentUsedVertList = {} # A Dict of smooth groups, each smooth group has a list of used verts and they are generated on demand so as to save memory.
543
 
        currentMaterialMeshMapping = {} # Used to store material indicies so we dont have to search the mesh for materials every time.
 
396
        currentObjectName= 'unnamed_obj_0' # If we cant get one, use this
 
397
        
 
398
        if IMPORT_MTL_SPLIT:
 
399
                currentObjectName_real= currentObjectName
 
400
        
 
401
        #meshDict= {} # The 3 variables below are stored in a tuple within this dict for each mesh
 
402
        currentMesh= NMesh.GetRaw() # The NMesh representation of the OBJ group/Object
 
403
        #currentUsedVertList= {} # A Dict of smooth groups, each smooth group has a list of used verts and they are generated on demand so as to save memory.
 
404
        currentMaterialMeshMapping= {} # Used to store material indices so we dont have to search the mesh for materials every time.
544
405
        
545
406
        # Every mesh has a null smooth group, this is used if there are no smooth groups in the OBJ file.
546
407
        # and when for faces where no smooth group is used.
547
 
        currentSmoothGroup = '(null)' # The Name of the current smooth group
 
408
        currentSmoothGroup= '(null)' # The Name of the current smooth group
548
409
        
549
410
        # For direct accsess to the Current Meshes, Current Smooth Groups- Used verts.
550
411
        # This is of course context based and changes on the fly.
551
412
        # Set the initial '(null)' Smooth group, every mesh has one.
552
 
        currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
 
413
        currentUsedVertListSmoothGroup= VERT_USED_LIST[:]
553
414
        currentUsedVertList= {currentSmoothGroup: currentUsedVertListSmoothGroup }
554
415
        
555
 
        
556
416
        # 0:NMesh, 1:SmoothGroups[UsedVerts[0,0,0,0]], 2:materialMapping['matname':matIndexForThisNMesh]
557
 
        meshDict = {currentObjectName: (currentMesh, currentUsedVertList, currentMaterialMeshMapping) }
 
417
        meshDict= {currentObjectName: (currentMesh, currentUsedVertList, currentMaterialMeshMapping) }
558
418
        
559
419
        # Only show the bad uv error once 
560
 
        badObjUvs = 0
561
 
        badObjFaceVerts = 0
562
 
        badObjFaceTexCo = 0
563
 
        
564
 
        
565
 
        #currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indicies where 1 is the first item.
 
420
        badObjUvs= 0
 
421
        badObjFaceVerts= 0
 
422
        badObjFaceTexCo= 0
 
423
        
 
424
        
 
425
        #currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indices where 1 is the first item.
566
426
        if len_uvMapList > 1:
567
427
                currentMesh.hasFaceUV(1) # Turn UV's on if we have ANY texture coords in this obj file.
568
428
        
569
429
        
 
430
        
 
431
        # Heres the code that gets a mesh, creating a new one if needed.
 
432
        # may_exist is used to avoid a dict looup.
 
433
        # if the mesh is unnamed then we generate a new name and dont bother looking
 
434
        # to see if its alredy there.
 
435
        def obj_getmesh(may_exist):
 
436
                global currentMesh,\
 
437
                currentUsedVertList,\
 
438
                currentUsedVertListSmoothGroup,\
 
439
                meshDict,\
 
440
                contextMeshMatIdx,\
 
441
                currentMaterialMeshMapping
 
442
                
 
443
                #print 'getting mesh,', currentObjectName
 
444
                
 
445
                # If we havnt written to this mesh before then do so.
 
446
                # if we have then we'll just keep appending to it, this is required for soem files.
 
447
                
 
448
                # If we are new, or we are not yet in the list of added meshes
 
449
                # then make us new mesh.
 
450
                if (not may_exist) or (not meshDict.has_key(currentObjectName)):
 
451
                        currentMesh= NMesh.GetRaw()
 
452
                        
 
453
                        currentUsedVertList= {}
 
454
                        
 
455
                        # SmoothGroup is a string
 
456
                        ########currentSmoothGroup= '(null)' # From examplesm changing the g/o shouldent change the smooth group.
 
457
                        currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]                                              
 
458
                        
 
459
                        currentMaterialMeshMapping= {}
 
460
                        meshDict[currentObjectName]= (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
 
461
                        currentMesh.hasFaceUV(1)
 
462
                        contextMeshMatIdx= -1
 
463
                        
 
464
                else: 
 
465
                        # Since we have this in Blender then we will check if the current Mesh has the material.
 
466
                        # set the contextMeshMatIdx to the meshs index but only if we have it.
 
467
                        currentMesh, currentUsedVertList, currentMaterialMeshMapping= meshDict[currentObjectName]
 
468
                        #getMeshMaterialIndex(currentMesh, currentMat)
 
469
                        
 
470
                        if IMPORT_MTL:
 
471
                                try:
 
472
                                        contextMeshMatIdx= currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
 
473
                                except KeyError:
 
474
                                        contextMeshMatIdx -1
 
475
                        
 
476
                        # For new meshes switch smoothing groups to null
 
477
                        ########currentSmoothGroup= '(null)'  # From examplesm changing the g/o shouldent change the smooth group.
 
478
                        try:
 
479
                                currentUsedVertListSmoothGroup= currentUsedVertList[currentSmoothGroup]
 
480
                        except:
 
481
                                currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]                                              
 
482
                
 
483
        
 
484
        
 
485
        
570
486
        #==================================================================================#
571
487
        # Load all faces into objects, main loop                                           #
572
488
        #==================================================================================#
573
 
        #lIdx = 0
574
 
        # Face and Object loading LOOP
575
 
        #while lIdx < len(fileLines):
576
 
        #       l = fileLines[lIdx]
577
 
        #for lIdx
578
 
        for l in fileLines:
 
489
        lIdx= 0
 
490
        EDGE_FGON_FLAG= NMesh.EdgeFlags['FGON']
 
491
        EDGE_DRAW_FLAG= NMesh.EdgeFlags['EDGEDRAW']
 
492
        
 
493
        REL_VERT_COUNT= REL_TVERT_COUNT=0
 
494
        
 
495
        while lIdx < len(fileLines):
 
496
                l= fileLines[lIdx]
 
497
                #for l in fileLines:
579
498
                if len(l) == 0:
580
499
                        continue
581
500
                # FACE
582
 
                elif l[0] == 'f': 
 
501
                elif l[0] == 'v':
 
502
                        REL_VERT_COUNT+=1
 
503
                elif l[0] == 'vt':
 
504
                        REL_TVERT_COUNT+=1
 
505
                
 
506
                elif l[0] == 'f' or l[0] == 'fo': # fo is not standard. saw it used once.
583
507
                        # Make a face with the correct material.
584
 
                        
585
508
                        # Add material to mesh
586
 
                        if contextMeshMatIdx == -1:
587
 
                                tmpMatLs = currentMesh.materials
588
 
                                
589
 
                                if len(tmpMatLs) == 16:
590
 
                                        contextMeshMatIdx = 0 # Use first material
591
 
                                        print 'material overflow, attempting to use > 16 materials. defaulting to first.'
592
 
                                else:
593
 
                                        contextMeshMatIdx = len(tmpMatLs)
594
 
                                        currentMaterialMeshMapping[currentMat.name] = contextMeshMatIdx
595
 
                                        currentMesh.addMaterial(currentMat)
596
 
                        
 
509
                        if IMPORT_MTL:
 
510
                                if contextMeshMatIdx == -1:
 
511
                                        tmpMatLs= currentMesh.materials                                 
 
512
                                        if len(tmpMatLs) == 16:
 
513
                                                contextMeshMatIdx= 0 # Use first material
 
514
                                                print 'material overflow, attempting to use > 16 materials. defaulting to first.'
 
515
                                        else:
 
516
                                                contextMeshMatIdx= len(tmpMatLs)
 
517
                                                currentMaterialMeshMapping[currentMat.name]= contextMeshMatIdx
 
518
                                                currentMesh.addMaterial(currentMat)
 
519
                
597
520
                        # Set up vIdxLs : Verts
598
521
                        # Set up vtIdxLs : UV
599
522
                        # Start with a dummy objects so python accepts OBJs 1 is the first index.
600
 
                        vIdxLs = []
601
 
                        vtIdxLs = []
602
 
                        
603
 
                        
604
 
                        fHasUV = len_uvMapList # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
605
 
                        for v in l[1:]:
606
 
                                # OBJ files can have // or / to seperate vert/texVert/normal
607
 
                                # this is a bit of a pain but we must deal with it.
608
 
                                objVert = v.split('/')
609
 
                                
610
 
                                # Vert Index - OBJ supports negative index assignment (like python)
611
 
                                index = int(objVert[0])-1
612
 
                                # Account for negative indicies.
613
 
                                if index < 0:
614
 
                                        index = len_vertList+index+1
615
 
                                
616
 
                                vIdxLs.append(index)
617
 
                                if fHasUV:
618
 
                                        # UV
619
 
                                        index = 0 # Dummy var
620
 
                                        if len(objVert) == 1:
621
 
                                                index = vIdxLs[-1]
622
 
                                        elif objVert[1]: # != '' # Its possible that theres no texture vert just he vert and normal eg 1//2
623
 
                                                index = int(objVert[1])-1
624
 
                                                if index < 0:
625
 
                                                        index = len_uvMapList+index+1
626
 
                                                
627
 
                                        if len_uvMapList > index:
628
 
                                                vtIdxLs.append(index) # Seperate UV coords
629
 
                                        else:
630
 
                                                # BAD FILE, I have found this so I account for it.
631
 
                                                # INVALID UV COORD
632
 
                                                # Could ignore this- only happens with 1 in 1000 files.
633
 
                                                badObjFaceTexCo +=1
634
 
                                                vtIdxLs.append(1)
635
 
                                                
636
 
                                                fHasUV = 0
637
 
        
638
 
                                        # Dont add a UV to the face if its larger then the UV coord list
639
 
                                        # The OBJ file would have to be corrupt or badly written for thi to happen
640
 
                                        # but account for it anyway.
641
 
                                        if len(vtIdxLs) > 0:
642
 
                                                if vtIdxLs[-1] > len_uvMapList:
643
 
                                                        fHasUV = 0
644
 
                                                        
645
 
                                                        badObjUvs +=1 # ERROR, Cont
 
523
                        vIdxLs= []
 
524
                        vtIdxLs= []
 
525
                        
 
526
                        fHasUV= len_uvMapList # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
 
527
                        
 
528
                        # Support stupid multiline faces
 
529
                        # not an obj spec but some objs exist that do this.
 
530
                        # f 1 2 3 \ 
 
531
                        #   4 5 6 \
 
532
                        # ..... instead of the more common and sane.
 
533
                        # f 1 2 3 4 5 6
 
534
                        #
 
535
                        # later lines are not modified, just skepped by advancing "lIdx"
 
536
                        while l[-1] == '\\':
 
537
                                l.pop()
 
538
                                lIdx+=1
 
539
                                l.extend(fileLines[lIdx])
 
540
                        # Done supporting crappy obj faces over multiple lines.
 
541
                        
 
542
                        
 
543
                        for v in l:
 
544
                                if not v.startswith('f'): # Only the first v will be f, any better ways to skip it?
 
545
                                        # OBJ files can have // or / to seperate vert/texVert/normal
 
546
                                        # this is a bit of a pain but we must deal with it.
 
547
                                        objVert= v.split('/')
 
548
                                        
 
549
                                        # Vert Index - OBJ supports negative index assignment (like python)
 
550
                                        index= int(objVert[0])-1
 
551
                                        
 
552
                                        # Account for negative indices.
 
553
                                        if index < 0:
 
554
                                                # Assume negative verts are relative.
 
555
                                                index= REL_VERT_COUNT+index+1
 
556
                                        
 
557
                                        vIdxLs.append(index)
 
558
                                        if fHasUV:
 
559
                                                # UV
 
560
                                                index= 0 # Dummy var
 
561
                                                if len(objVert) == 1:
 
562
                                                        index= vIdxLs[-1]
 
563
                                                elif objVert[1]: # != '' # Its possible that theres no texture vert just he vert and normal eg 1//2
 
564
                                                        index= int(objVert[1])-1
 
565
                                                        if index < 0:
 
566
                                                                # Assume negative verts are relative
 
567
                                                                index= REL_TVERT_COUNT+index+1
 
568
                                                        
 
569
                                                if len_uvMapList > index:
 
570
                                                        vtIdxLs.append(index) # Seperate UV coords
 
571
                                                else:
 
572
                                                        # BAD FILE, I have found this so I account for it.
 
573
                                                        # INVALID UV COORD
 
574
                                                        # Could ignore this- only happens with 1 in 1000 files.
 
575
                                                        badObjFaceTexCo +=1
 
576
                                                        vtIdxLs.append(1)
 
577
                                                        
 
578
                                                        fHasUV= 0
 
579
                
 
580
                                                # Dont add a UV to the face if its larger then the UV coord list
 
581
                                                # The OBJ file would have to be corrupt or badly written for thi to happen
 
582
                                                # but account for it anyway.
 
583
                                                if len(vtIdxLs) > 0:
 
584
                                                        if vtIdxLs[-1] > len_uvMapList:
 
585
                                                                fHasUV= 0
 
586
                                                                
 
587
                                                                badObjUvs +=1 # ERROR, Cont
 
588
                        
646
589
                        # Quads only, we could import quads using the method below but it polite to import a quad as a quad.
647
590
                        #print lIdx, len(vIdxLs), len(currentUsedVertListSmoothGroup)
648
591
                        #print fileLines[lIdx]
649
 
                        if len(vIdxLs) == 2:
650
 
                                if IMPORT_EDGES:
 
592
                        
 
593
                        # Add all the verts we need,
 
594
                        # dont edd edge verts if were not importing them.
 
595
                        face_vert_count= len(vIdxLs)
 
596
                        if (not IMPORT_EDGES) and face_vert_count == 2:
 
597
                                pass
 
598
                        else:
 
599
                                # Add the verts that arnt alredy added.
 
600
                                for i in vIdxLs:
 
601
                                        if currentUsedVertListSmoothGroup[i] == -1:
 
602
                                                v= vertList[i]
 
603
                                                currentMesh.verts.append(v)
 
604
                                                currentUsedVertListSmoothGroup[i]= len(currentMesh.verts)-1
 
605
                        
 
606
                        if face_vert_count == 2:
 
607
                                if IMPORT_EDGES and vIdxLs[0]!=vIdxLs[1]:
651
608
                                        # Edge
652
 
                                        for i in (0,1):
653
 
                                                if currentUsedVertListSmoothGroup[vIdxLs[i]] == 0:
654
 
                                                        faceQuadVList[i] = vertList[vIdxLs[i]]
655
 
                                                        currentMesh.verts.append(faceQuadVList[i])
656
 
                                                        currentUsedVertListSmoothGroup[vIdxLs[i]] = len(currentMesh.verts)-1
657
 
                                                else:
658
 
                                                        faceQuadVList[i] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
659
 
                                                        
660
 
                                        currentMesh.addEdge(faceQuadVList[0], faceQuadVList[1]) 
661
 
                        elif len(vIdxLs) == 4:
 
609
                                        currentMesh.addEdge(\
 
610
                                        currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[0]]],\
 
611
                                        currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[0]]]) 
 
612
                                        
 
613
                        elif face_vert_count == 4:
662
614
                                
663
615
                                # Have found some files where wach face references the same vert
664
616
                                # - This causes a bug and stopts the import so lets check here
671
623
                                        badObjFaceVerts+=1
672
624
                                else:
673
625
                                        for i in quadList: #  quadList == [0,1,2,3] 
674
 
                                                if currentUsedVertListSmoothGroup[vIdxLs[i]] == 0:
675
 
                                                        faceQuadVList[i] = vertList[vIdxLs[i]]
676
 
                                                        currentMesh.verts.append(faceQuadVList[i])
677
 
                                                        currentUsedVertListSmoothGroup[vIdxLs[i]] = len(currentMesh.verts)-1
678
 
                                                else:
679
 
                                                        faceQuadVList[i] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
 
626
                                                faceQuadVList[i]= currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
680
627
                                        
681
 
                                        f = NMesh.Face(faceQuadVList)
 
628
                                        f= NMesh.Face(faceQuadVList)
682
629
                                        # UV MAPPING
683
630
                                        if fHasUV:
684
 
                                                f.uv = [uvMapList[ vtIdxLs[0] ],uvMapList[ vtIdxLs[1] ],uvMapList[ vtIdxLs[2] ],uvMapList[ vtIdxLs[3] ]]
 
631
                                                f.uv= [uvMapList[ vtIdxLs[0] ],uvMapList[ vtIdxLs[1] ],uvMapList[ vtIdxLs[2] ],uvMapList[ vtIdxLs[3] ]]
685
632
                                                if currentImg:
686
 
                                                        f.image = currentImg
 
633
                                                        f.image= currentImg
687
634
                                                else:
688
635
                                                        f.mode &= TEX_OFF_FLAG
689
 
                                        
690
 
                                        f.mat = contextMeshMatIdx
691
 
                                        f.smooth = currentSmooth
 
636
                                        if IMPORT_MTL:
 
637
                                                f.mat= contextMeshMatIdx
 
638
                                        f.smooth= currentSmooth
692
639
                                        currentMesh.faces.append(f) # move the face onto the mesh
693
640
                        
694
 
                        elif len(vIdxLs) >= 3: # This handles tri's and fans
695
 
                                for i in range(len(vIdxLs)-2):
 
641
                        elif face_vert_count == 3: # This handles tri's and fans, dont use fans anymore.
 
642
                                for i in range(face_vert_count-2):
696
643
                                        if vIdxLs[0] == vIdxLs[i+1] or\
697
644
                                        vIdxLs[0] == vIdxLs[i+2] or\
698
645
                                        vIdxLs[i+1] == vIdxLs[i+2]:
699
646
                                                badObjFaceVerts+=1
700
647
                                        else:
701
648
                                                for k, j in [(0,0), (1,i+1), (2,i+2)]:
702
 
                                                        if currentUsedVertListSmoothGroup[vIdxLs[j]] == 0:
703
 
                                                                faceTriVList[k] = vertList[vIdxLs[j]]
704
 
                                                                currentMesh.verts.append(faceTriVList[k])
705
 
                                                                currentUsedVertListSmoothGroup[vIdxLs[j]] = len(currentMesh.verts)-1
706
 
                                                        else:
707
 
                                                                faceTriVList[k] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[j]]]  
 
649
                                                        faceTriVList[k]= currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[j]]]   
708
650
                                                
709
 
                                                f = NMesh.Face(faceTriVList)    
 
651
                                                f= NMesh.Face(faceTriVList)     
710
652
                                                
711
653
                                                # UV MAPPING
712
654
                                                if fHasUV:
713
 
                                                        f.uv = [uvMapList[vtIdxLs[0]], uvMapList[vtIdxLs[i+1]], uvMapList[vtIdxLs[i+2]]]
 
655
                                                        f.uv= [uvMapList[vtIdxLs[0]], uvMapList[vtIdxLs[i+1]], uvMapList[vtIdxLs[i+2]]]
714
656
                                                        if currentImg:
715
 
                                                                f.image = currentImg
 
657
                                                                f.image= currentImg
716
658
                                                        else:
717
659
                                                                f.mode &= TEX_OFF_FLAG
718
 
                                                
719
 
                                                f.mat = contextMeshMatIdx
720
 
                                                f.smooth = currentSmooth
 
660
                                                if IMPORT_MTL:
 
661
                                                        f.mat= contextMeshMatIdx
 
662
                                                f.smooth= currentSmooth
721
663
                                                currentMesh.faces.append(f) # move the face onto the mesh
722
 
                
 
664
                        
 
665
                        elif face_vert_count > 4: # NGons.
 
666
                                # we need to map indices to uv coords.
 
667
                                currentMeshRelativeIdxs= [currentUsedVertListSmoothGroup[i] for i in vIdxLs]
 
668
                                
 
669
                                if fHasUV:
 
670
                                        vert2UvMapping=dict( [ (currentMeshRelativeIdxs[i],vtIdxLs[i]) for i in xrange(face_vert_count)] )
 
671
                                
 
672
                                ngon_face_indices= BPyMesh.ngon(currentMesh, currentMeshRelativeIdxs)
 
673
                                
 
674
                                
 
675
                                # At the moment scanfill always makes tri's but dont count on it
 
676
                                for fillFace in ngon_face_indices:
 
677
                                        f= NMesh.Face([currentMesh.verts[currentMeshRelativeIdxs[i]] for i in fillFace])
 
678
                                        
 
679
                                        if fHasUV:
 
680
                                                f.uv= [uvMapList[vert2UvMapping[currentMeshRelativeIdxs[i]]] for i in fillFace]
 
681
                                                if currentImg:
 
682
                                                        f.image= currentImg
 
683
                                                else:
 
684
                                                        f.mode &= TEX_OFF_FLAG
 
685
                                        if IMPORT_MTL:
 
686
                                                f.mat= contextMeshMatIdx
 
687
                                        f.smooth= currentSmooth
 
688
                                        currentMesh.faces.append(f) # move the face onto the mesh
 
689
                                
 
690
                                # Set fgon flag.
 
691
                                if IMPORT_FGON:
 
692
                                        edgeUsers={}
 
693
                                        for fillFace in ngon_face_indices:
 
694
                                                for i in xrange(len(fillFace)): # Should always be 3
 
695
                                                        i1= currentMeshRelativeIdxs[fillFace[i]]
 
696
                                                        i2= currentMeshRelativeIdxs[fillFace[i-1]]
 
697
                                                        
 
698
                                                        # Sort the pair so thet always match.
 
699
                                                        if i1>i2: i1,i2=i2,i1
 
700
                                                                
 
701
                                                        try:
 
702
                                                                edgeUsers[i1,i2]+= 1
 
703
                                                        except:
 
704
                                                                edgeUsers[i1,i2]= 0
 
705
                                        
 
706
                                        for edgeVerts, users in edgeUsers.iteritems():
 
707
                                                if users:
 
708
                                                        i1,i2= edgeVerts
 
709
                                                        if i1!=i2:
 
710
                                                                ed= currentMesh.addEdge(\
 
711
                                                                 currentMesh.verts[i1],\
 
712
                                                                 currentMesh.verts[i2])
 
713
                                                        
 
714
                                                        ed.flag|= EDGE_FGON_FLAG
 
715
                        
723
716
                # FACE SMOOTHING
724
 
                elif l[0] == 's':
 
717
                elif l[0] == 's' and IMPORT_SMOOTH_GROUPS:
725
718
                        # No value? then turn on.
726
719
                        if len(l) == 1:
727
 
                                currentSmooth = True
728
 
                                currentSmoothGroup = '(null)'
 
720
                                currentSmooth= True
 
721
                                currentSmoothGroup= '(null)'
729
722
                        else:
730
723
                                if l[1] == 'off': # We all have a null group so dont need to try, will try anyway to avoid code duplication.
731
724
                                        if not IMPORT_SMOOTH_ALL:
732
 
                                                currentSmooth = False
733
 
                                        currentSmoothGroup = '(null)'
 
725
                                                currentSmooth= False
 
726
                                        currentSmoothGroup= '(null)'
734
727
                                else: 
735
 
                                        currentSmooth = True
736
 
                                        currentSmoothGroup = '_'.join(l[1:])
 
728
                                        currentSmooth= True
 
729
                                        currentSmoothGroup= '_'.join(l[1:])
737
730
                        try:
738
 
                                currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
 
731
                                currentUsedVertListSmoothGroup= currentUsedVertList[currentSmoothGroup]
739
732
                        except KeyError:
740
 
                                currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
 
733
                                currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]
741
734
                
742
735
                
743
736
                # OBJECT / GROUP
744
737
                elif l[0] == 'o' or l[0] == 'g':
745
738
                        
746
739
                        # Forget about the current image
747
 
                        currentImg = None
 
740
                        currentImg= None
748
741
                        
749
742
                        # This makes sure that if an object and a group have the same name then
750
743
                        # they are not put into the same object.
751
744
                        
752
745
                        # Only make a new group.object name if the verts in the existing object have been used, this is obscure
753
746
                        # but some files face groups seperating verts and faces which results in silly things. (no groups have names.)
754
 
                        if len(l) > 1:
755
 
                                currentObjectName = '_'.join(l[1:])
756
 
                        else: # No name given
 
747
                        if len(l) == 1:
757
748
                                # Make a new empty name
758
749
                                if l[0] == 'g': # Make a blank group name
759
 
                                        currentObjectName = 'unnamed_grp_%.4d' % currentUnnamedGroupIdx
 
750
                                        currentObjectName= 'unnamed_grp_%.4d' % currentUnnamedGroupIdx
760
751
                                        currentUnnamedGroupIdx +=1
761
752
                                else: # is an object.
762
 
                                        currentObjectName = 'unnamed_ob_%.4d' % currentUnnamedObjectIdx
 
753
                                        currentObjectName= 'unnamed_ob_%.4d' % currentUnnamedObjectIdx
763
754
                                        currentUnnamedObjectIdx +=1
764
 
                        
765
 
                        
766
 
                        # If we havnt written to this mesh before then do so.
767
 
                        # if we have then we'll just keep appending to it, this is required for soem files.
768
 
                        
769
 
                        # If we are new, or we are not yet in the list of added meshes
770
 
                        # then make us new mesh.
771
 
                        if len(l) == 1 or (not meshDict.has_key(currentObjectName)):
772
 
                                currentMesh = NMesh.GetRaw()
773
 
                                
774
 
                                currentUsedVertList = {}
775
 
                                
776
 
                                # Sg is a string
777
 
                                ########currentSmoothGroup = '(null)' # From examplesm changing the g/o shouldent change the smooth group.
778
 
                                currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup = VERT_USED_LIST[:]                                            
779
 
                                
780
 
                                currentMaterialMeshMapping = {}
781
 
                                meshDict[currentObjectName] = (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
782
 
                                currentMesh.hasFaceUV(1)
783
 
                                contextMeshMatIdx = -1
784
 
                                
785
 
                        else: 
786
 
                                # Since we have this in Blender then we will check if the current Mesh has the material.
787
 
                                # set the contextMeshMatIdx to the meshs index but only if we have it.
788
 
                                currentMesh, currentUsedVertList, currentMaterialMeshMapping = meshDict[currentObjectName]
789
 
                                #getMeshMaterialIndex(currentMesh, currentMat)
790
 
                                
791
 
                                try:
792
 
                                        contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
793
 
                                except KeyError:
794
 
                                        contextMeshMatIdx -1
795
 
                                
796
 
                                # For new meshes switch smoothing groups to null
797
 
                                ########currentSmoothGroup = '(null)'  # From examplesm changing the g/o shouldent change the smooth group.
798
 
                                try:
799
 
                                        currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
800
 
                                except:
801
 
                                        currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup = VERT_USED_LIST[:]                                            
802
 
                                        
 
755
                                may_exist= False # we know the model is new.
 
756
                        else: # No name given
 
757
                                currentObjectName= '_'.join(l[1:])
 
758
                                may_exist= True
 
759
                        
 
760
                        if IMPORT_MTL_SPLIT:
 
761
                                currentObjectName_real= currentObjectName
 
762
                                currentObjectName += '_'+currentMat.name
 
763
                        
 
764
                        obj_getmesh(may_exist)
803
765
                                
804
766
                
805
767
                # MATERIAL
806
 
                elif l[0] == 'usemtl':
 
768
                elif l[0] == 'usemtl' and IMPORT_MTL:
807
769
                        if len(l) == 1 or l[1] == '(null)':
808
 
                                currentMat = nullMat # We know we have a null mat.
 
770
                                currentMat= nullMat # We know we have a null mat.
809
771
                        else:
810
 
                                currentMat = materialDict['_'.join(l[1:])]
 
772
                                currentMat= getMat('_'.join(l[1:]), materialDict)
811
773
                                try:
812
 
                                        contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name]
 
774
                                        contextMeshMatIdx= currentMaterialMeshMapping[currentMat.name]
813
775
                                except KeyError:
814
 
                                        contextMeshMatIdx = -1 #getMeshMaterialIndex(currentMesh, currentMat)
 
776
                                        contextMeshMatIdx= -1 #getMeshMaterialIndex(currentMesh, currentMat)
 
777
                        
 
778
                        # Check if we are splitting by material.
 
779
                        if IMPORT_MTL_SPLIT:
 
780
                                currentObjectName= '%s_%s' % (currentObjectName_real, currentMat.name)
 
781
                                obj_getmesh(True)
 
782
                        
815
783
                        
816
784
                # IMAGE
817
785
                elif l[0] == 'usemat' or l[0] == 'usemap':
818
786
                        if len(l) == 1 or l[1] == '(null)' or l[1] == 'off':
819
 
                                currentImg = None
 
787
                                currentImg= None
820
788
                        else:
821
789
                                # Load an image.
822
 
                                newImgName = stripPath(' '.join(l[1:])) # Use space since its a file name.
 
790
                                newImgName= stripPath(' '.join(l[1:])) # Use space since its a file name.
823
791
                                
824
792
                                try:
825
793
                                        # Assume its alredy set in the dict (may or maynot be loaded)
826
 
                                        currentImg = imageDict[newImgName]
 
794
                                        currentImg= imageDict[newImgName]
827
795
                                
828
796
                                except KeyError: # Not in dict, add for first time.
829
797
                                        # Image has not been added, Try and load the image
830
 
                                        currentImg = comprehansiveImageLoad(newImgName, DIR) # Use join in case of spaces 
831
 
                                        imageDict[newImgName] = currentImg
 
798
                                        currentImg= BPyImage.comprehensiveImageLoad(newImgName, DIR) # Use join in case of spaces 
 
799
                                        imageDict[newImgName]= currentImg
832
800
                                        # These may be None, thats okay.
833
 
                                        
834
 
                                        
835
801
                
836
802
                # MATERIAL FILE
837
 
                elif l[0] == 'mtllib':
838
 
                        mtl_fileName.append(' '.join(l[1:]) ) # SHOULD SUPPORT MULTIPLE MTL?
839
 
                #lIdx+=1
840
 
        
 
803
                elif l[0] == 'mtllib' and IMPORT_MTL and len(l)>1:
 
804
                        mtl_fileName.append(' '.join(l[1:]) ) # Support for multiple MTL's
 
805
                lIdx+=1
 
806
                
841
807
        # Applies material properties to materials alredy on the mesh as well as Textures.
842
808
        if IMPORT_MTL:
843
809
                for mtl in mtl_fileName:
844
 
                        load_mtl(DIR, mtl, meshDict, materialDict)      
845
 
        
846
 
        
847
 
        importedObjects = []
 
810
                        load_mtl(DIR, IMPORT_USE_EXISTING_MTL, mtl, meshDict, materialDict)     
 
811
        
 
812
        # Get a new scale factor if set as an option
 
813
        SCALE=1.0
 
814
        if IMPORT_CONSTRAIN_BOUNDS != 0.0:
 
815
                while (BOUNDS*SCALE) > IMPORT_CONSTRAIN_BOUNDS:
 
816
                        SCALE/=10
 
817
        
 
818
        importedObjects= []
 
819
        
 
820
        scn= Scene.GetCurrent()
 
821
        
 
822
        # DeSelect all
 
823
        for ob in scn.getChildren():
 
824
                ob.sel=0
 
825
        
 
826
        # Create objects for each mesh.
848
827
        for mk, me in meshDict.iteritems():
849
 
                nme = me[0]
 
828
                nme= me[0]
850
829
                
851
830
                # Ignore no vert meshes.
852
831
                if not nme.verts: # == []
853
832
                        continue
854
833
                
855
 
                name = getUniqueName(mk)
856
 
                ob = NMesh.PutRaw(nme, name)
857
 
                ob.name = name
858
 
                
 
834
                ob= Object.New('Mesh', fileName)
 
835
                nme.name= mk
 
836
                ob.link(nme)
 
837
                ob.setSize(SCALE, SCALE, SCALE)
859
838
                importedObjects.append(ob)
860
839
        
861
 
        # Select all imported objects.
862
 
        for ob in importedObjects:
863
 
                ob.sel = 1
 
840
        Layers= scn.Layers
 
841
        if IMPORT_AS_INSTANCE:
 
842
                # Create a group for this import.
 
843
                group_scn= Scene.New(fileName)
 
844
                for ob in importedObjects:
 
845
                        group_scn.link(ob) # dont worry about the layers
 
846
                
 
847
                grp= Group.New(fileName)
 
848
                grp.objects= importedObjects
 
849
                
 
850
                grp_ob= Object.New('Empty', fileName)
 
851
                grp_ob.enableDupGroup= True
 
852
                grp_ob.DupGroup= grp
 
853
                scn.link(grp_ob)
 
854
                grp_ob.Layers= Layers
 
855
                grp_ob.sel= 1
 
856
                
 
857
        else:
 
858
                # Select all imported objects.
 
859
                for ob in importedObjects:
 
860
                        scn.link(ob)
 
861
                        ob.Layers= Layers
 
862
                        ob.sel= 1
 
863
        
 
864
        
864
865
        if badObjUvs > 0:
865
866
                print '\tERROR: found %d faces with badly formatted UV coords. everything else went okay.' % badObjUvs
866
867
        
875
876
 
876
877
def load_obj_ui(file):
877
878
        
878
 
        IMPORT_MTL = Draw.Create(1)
879
 
        IMPORT_DIR = Draw.Create(0)
880
 
        IMPORT_NEW_SCENE = Draw.Create(0)
881
 
        IMPORT_EDGES = Draw.Create(1)
882
 
        IMPORT_SMOOTH_ALL = Draw.Create(0)
 
879
        IMPORT_DIR= Draw.Create(0)
 
880
        IMPORT_NEW_SCENE= Draw.Create(0)
 
881
        IMPORT_MTL= Draw.Create(1)
 
882
        IMPORT_USE_EXISTING_MTL= Draw.Create(1)
883
883
        
 
884
        IMPORT_CONSTRAIN_BOUNDS= Draw.Create(10.0)
 
885
        IMPORT_ROTATE_X90= Draw.Create(1)
 
886
        IMPORT_EDGES= Draw.Create(1)
 
887
        IMPORT_SMOOTH_ALL= Draw.Create(1)
 
888
        IMPORT_FGON= Draw.Create(1)
 
889
        IMPORT_SMOOTH_GROUPS= Draw.Create(0)
 
890
        IMPORT_MTL_SPLIT= Draw.Create(0)
 
891
        IMPORT_AS_INSTANCE= Draw.Create(0)
884
892
        
885
893
        # Get USER Options
886
 
        pup_block = [\
887
 
        ('Material (*.mtl)', IMPORT_MTL, 'Imports material settings and images from the obj\'s .mtl file'),\
 
894
        pup_block= [\
888
895
        ('All *.obj\'s in dir', IMPORT_DIR, 'Import all obj files in this dir (avoid overlapping data with "Create scene")'),\
889
 
        ('Create scene', IMPORT_NEW_SCENE, 'Imports each obj into its own scene, named from the file'),\
 
896
        ('Create Scene', IMPORT_NEW_SCENE, 'Imports each obj into its own scene, named from the file'),\
 
897
        ('Group Instance', IMPORT_AS_INSTANCE, 'Import objects into a new scene and group, creating an instance in the current scene.'),\
 
898
        'Materials...',\
 
899
        ('Import (*.mtl)', IMPORT_MTL, 'Imports material settings and images from the obj\'s .mtl file'),\
 
900
        ('Re-Use Existing', IMPORT_USE_EXISTING_MTL, 'Use materials from the current blend where names match.'),\
890
901
        'Geometry...',\
 
902
        ('Size Constraint:', IMPORT_CONSTRAIN_BOUNDS, 0.0, 1000.0, 'Scale the model by 10 until it reacehs the size constraint. Zero Disables.'),\
 
903
        ('Rotate X90', IMPORT_ROTATE_X90, 'Rotate X-Up to Blenders Z-Up'),\
891
904
        ('Edges', IMPORT_EDGES, 'Import faces with 2 verts as in edge'),\
892
905
        ('Smooths all faces', IMPORT_SMOOTH_ALL, 'Smooth all faces even if they are not in a smoothing group'),\
 
906
        ('Create FGons', IMPORT_FGON, 'Import faces with more then 4 verts as fgons.'),\
 
907
        ('Smooth Groups', IMPORT_SMOOTH_GROUPS, 'Only Share verts within smooth groups. (Warning, Hogs Memory)'),\
 
908
        ('Split by Material', IMPORT_MTL_SPLIT, 'Import each material into a seperate mesh (Avoids >16 meterials per mesh problem)'),\
893
909
        ]
894
910
        
895
911
        if not os:
896
 
                pup_block.pop(1) # Make sure this is the IMPORT_DIR option that requires OS
 
912
                pup_block.pop(0) # Make sure this is the IMPORT_DIR option that requires OS
897
913
        
898
 
        if not Draw.PupBlock('Import...', pup_block):
 
914
        if not Draw.PupBlock('Import OBJ...', pup_block):
899
915
                return
900
916
        
901
917
        Window.WaitCursor(1)
902
918
        Window.DrawProgressBar(0, '')
903
 
        time = sys.time()
904
 
        
905
 
        IMPORT_MTL = IMPORT_MTL.val
906
 
        IMPORT_DIR = IMPORT_DIR.val
907
 
        IMPORT_NEW_SCENE = IMPORT_NEW_SCENE.val
908
 
        IMPORT_EDGES = IMPORT_EDGES.val
909
 
        IMPORT_SMOOTH_ALL = IMPORT_SMOOTH_ALL.val
910
 
        
911
 
        #orig_scene = Scene.GetCurrent()
912
 
        
913
 
        obj_dir = stripFile(file)
 
919
        time= sys.time()
 
920
        
 
921
        IMPORT_DIR= IMPORT_DIR.val
 
922
        IMPORT_NEW_SCENE= IMPORT_NEW_SCENE.val
 
923
        IMPORT_MTL= IMPORT_MTL.val
 
924
        IMPORT_USE_EXISTING_MTL= IMPORT_USE_EXISTING_MTL.val
 
925
        
 
926
        IMPORT_CONSTRAIN_BOUNDS= IMPORT_CONSTRAIN_BOUNDS.val
 
927
        IMPORT_ROTATE_X90= IMPORT_ROTATE_X90.val
 
928
        IMPORT_EDGES= IMPORT_EDGES.val
 
929
        IMPORT_SMOOTH_ALL= IMPORT_SMOOTH_ALL.val
 
930
        IMPORT_FGON= IMPORT_FGON.val
 
931
        IMPORT_SMOOTH_GROUPS= IMPORT_SMOOTH_GROUPS.val
 
932
        IMPORT_MTL_SPLIT= IMPORT_MTL_SPLIT.val
 
933
        IMPORT_AS_INSTANCE= IMPORT_AS_INSTANCE.val
 
934
        orig_scene= Scene.GetCurrent()
 
935
        
 
936
        # Dont do material split if we dont import material
 
937
        if not IMPORT_MTL:
 
938
                IMPORT_MTL_SPLIT=0
 
939
        
 
940
        
 
941
        obj_dir= stripFile(file)
914
942
        if IMPORT_DIR:
915
 
                obj_files = [(obj_dir,f) for f in os.listdir(obj_dir) if f.lower().endswith('obj')]
 
943
                obj_files= [(obj_dir,f) for f in os.listdir(obj_dir) if f.lower().endswith('obj')]
916
944
        else:
917
 
                obj_files = [(obj_dir,stripPath(file))]
 
945
                obj_files= [(obj_dir,stripPath(file))]
918
946
        
919
 
        obj_len = len(obj_files)
920
 
        count = 0
 
947
        obj_len= len(obj_files)
 
948
        count= 0
921
949
        for d, f in obj_files:
922
950
                count+= 1
923
951
                if not sys.exists(d+f):
924
952
                        print 'Error: "%s%s" does not exist' % (d,f)
925
953
                else:
926
954
                        if IMPORT_NEW_SCENE:
927
 
                                scn = Scene.New('.'.join(f.split('.')[0:-1]))
 
955
                                scn= Scene.New('.'.join(f.split('.')[0:-1]))
928
956
                                scn.makeCurrent()
929
957
                        
930
 
                        
931
958
                        Window.DrawProgressBar((float(count)/obj_len) - 0.01, '%s: %i of %i' % (f, count, obj_len))
932
 
                        load_obj(d+f, IMPORT_MTL, IMPORT_EDGES, IMPORT_SMOOTH_ALL)
 
959
                        load_obj(d+f, IMPORT_MTL, IMPORT_USE_EXISTING_MTL, IMPORT_CONSTRAIN_BOUNDS, IMPORT_ROTATE_X90, IMPORT_EDGES, IMPORT_SMOOTH_ALL, IMPORT_FGON, IMPORT_SMOOTH_GROUPS, IMPORT_MTL_SPLIT, IMPORT_AS_INSTANCE)
933
960
                        
934
961
        
935
 
        #orig_scene.makeCurrent() # We can leave them in there new scene.
 
962
        orig_scene.makeCurrent() # We can leave them in there new scene.
936
963
        Window.DrawProgressBar(1, '')
937
964
        Window.WaitCursor(0)
938
965
        
939
 
        if count > 1:
940
 
                print 'Total obj import "%s" dir: %.2f' % (obj_dir, sys.time() - time)
941
 
        
 
966
        print 'Total obj import "%s" dir: %.2f' % (obj_dir, sys.time() - time)
 
967
 
942
968
 
943
969
def main():
944
 
        Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ')
945
 
                
946
 
 
 
970
        Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj')
947
971
 
948
972
if __name__ == '__main__':
949
973
        main()
 
 
b'\\ No newline at end of file'