73
73
from Blender import *
77
# Adds a slash to the end of a path if its not there.
79
if path.endswith('\\') or path.endswith('/'):
85
index = name.rfind('.')
74
import BPyImage # use for comprehensiveImageLoad
75
import BPyMesh # use for ngon
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.'
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):
103
# When we have the file load it with this. try/except niceness.
106
img = Image.Load(path)
107
print '\t\tImage loaded "%s"' % path
110
print '\t\tImage failed loading "%s", mabe its not a format blender can read.' % (path)
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.
120
print '\tAttempting to load "%s"' % imagePath
121
if sys.exists(imagePath):
122
print '\t\tFile found where expected.'
123
return imageLoad(imagePath)
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)
131
# Remove relative path from image path
132
if imageFilePath.startswith('./') or imageFilePath.startswith('.\\'):
133
imageFilePath = imageFilePath[2:]
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)
142
# OS NEEDED IF WE GO ANY FURTHER.
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
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
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
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
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
189
for path, files in paths.iteritems():
191
if sys.exists(path + imageFileName):
192
return imageLoad(path + imageFileName)
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]
199
# We are going to try in index the file directly, if its not there just keep on
202
# Is it just a case mismatch?
203
index = filesLower.index(imageFileName_lower)
206
# Have the extensions changed?
207
index = filesLowerNoExt.index(imageFileName_noext_lower)
209
ext = getExt( filesLower[index] ) # Get the extension of the file that matches all but ext.
211
# Check that the ext is useable eg- not a 3ds file :)
212
if ext.lower() not in IMAGE_EXT:
219
tmpPath = path + filesOrigCase[index]
220
img = imageLoad( tmpPath )
222
print '\t\tImage Found "%s"' % tmpPath
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
233
# finds the file starting at the root.
234
# def findImage(findRoot, imagePath):
238
pathWalk = os.walk(path)
241
matchList = [] # Store a list of (match, size), choose the biggest.
244
pathList = pathWalk.next()
248
for file in pathList[2]:
249
file_lower = file.lower()
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) )
260
matchList.sort(lambda A, B: cmp(B[1], A[1]) )
262
print '\t\tFound "%s"' % matchList[0][0]
264
# Loop through all we have found
266
for match in matchList:
267
img = imageLoad(match[0]) # 0 - first, 0 - pathname
273
print '\t\tImage Not Found "%s"' % imagePath
280
#==================================================================================#
281
# This function sets textures defined in .mtl file #
282
#==================================================================================#
283
# ___ Replaced by comprehensive imahge get
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']
291
texture = Texture.New(type)
91
texture= Texture.New(type)
292
92
texture.setType('Image')
294
94
# Absolute path - c:\.. etc would work here
295
image = comprehansiveImageLoad(img_fileName, dir)
95
image= BPyImage.comprehensiveImageLoad(img_fileName, dir)
298
texture.image = image
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.
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)
128
#===============================================================================#
129
# This gets a mat or creates one of the requested name if none exist. #
130
#===============================================================================#
131
def getMat(matName, materialDict):
133
try: return materialDict[matName]
134
except: pass # Better do any exception
136
try: return materialDict[matName.lower()]
139
# Do we realy need to keep the dict up to date?, not realy but keeps consuistant.
140
mat= materialDict[matName]= Material.New(matName)
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):
333
#===============================================================================#
334
# This gets a mat or creates one of the requested name if none exist. #
335
#===============================================================================#
336
def getMat(matName, materialDict):
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]
347
mtl_file = stripPath(mtl_file)
348
mtl_fileName = dir + mtl_file
149
mtl_file= stripPath(mtl_file)
150
mtl_fileName= dir + mtl_file
351
153
fileLines= open(mtl_fileName, 'r').readlines()
404
206
print '\tERROR: Unable to parse MTL file: "%s"' % mtl_file
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.
413
while newName in getUniqueName.uniqueNames:
414
newName = '%s.%.3i' % (name[:15], uniqueInt)
416
getUniqueName.uniqueNames.append(newName)
418
getUniqueName.uniqueNames = []
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):
216
IMPORT_USE_EXISTING_MTL=0,\
217
IMPORT_CONSTRAIN_BOUNDS=0.0,\
218
IMPORT_ROTATE_X90=0,\
220
IMPORT_SMOOTH_ALL=0,\
222
IMPORT_SMOOTH_GROUPS=0,\
224
IMPORT_AS_INSTANCE=0):
227
currentUsedVertList,\
228
currentUsedVertListSmoothGroup,\
231
currentMaterialMeshMapping
425
233
print '\nImporting OBJ file: "%s"' % file
429
getUniqueName.uniqueNames.extend( [ob.name for ob in Object.Get()] )
430
getUniqueName.uniqueNames.extend( NMesh.GetNames() )
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():
437
TEX_OFF_FLAG = ~NMesh.FaceModes['TEX']
242
TEX_OFF_FLAG= ~NMesh.FaceModes['TEX']
244
# Used for bounds scaling
439
248
# Get the file name with no path or .obj
440
fileName = stripExt( stripPath(file) )
442
mtl_fileName = [] # Support multiple mtl files if needed.
444
DIR = stripFile(file)
249
fileName= stripExt( stripPath(file) )
251
mtl_fileName= [] # Support multiple mtl files if needed.
446
tempFile = open(file, 'r')
447
fileLines = tempFile.readlines()
255
tempFile= open(file, 'r')
256
fileLines= tempFile.readlines()
450
uvMapList = [] # store tuple uv pairs here
259
uvMapList= [] # store tuple uv pairs here
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
457
266
# Store all imported images in a dict, names are key
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
464
# Keep this out of the dict for easy accsess.
465
nullMat = Material.New('(null)')
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
274
currentImg= None # Null image is a string, otherwise this should be set to an image object.\
469
275
if IMPORT_SMOOTH_ALL:
472
currentSmooth = False
474
280
# Store a list of unnamed names
475
currentUnnamedGroupIdx = 1
476
currentUnnamedObjectIdx = 1
478
quadList = (0, 1, 2, 3)
480
faceQuadVList = [None, None, None, None]
481
faceTriVList = [None, None, None]
281
currentUnnamedGroupIdx= 1
282
currentUnnamedObjectIdx= 1
284
quadList= (0, 1, 2, 3)
286
faceQuadVList= [None, None, None, None]
287
faceTriVList= [None, None, None]
483
289
#==================================================================================#
484
290
# Load all verts first (texture verts too) #
485
291
#==================================================================================#
487
292
print '\tfile length: %d' % len(fileLines)
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]
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]
297
if IMPORT_CONSTRAIN_BOUNDS == 0.0:
298
if IMPORT_ROTATE_X90:
300
return NMesh.Vert(x,-z,y) # rotate 90 about the x axis.
304
# Adding a vert also sets the bounds.
305
if IMPORT_ROTATE_X90:
308
BOUNDS= max(BOUNDS, x,y,z)
309
return NMesh.Vert(x,-z,y) # Rotate X90 Deg.
313
BOUNDS= max(BOUNDS, x,y,z)
314
return NMesh.Vert(x,y,z)
317
vertList= [Vert(float(l[1]), float(l[2]), float(l[3]) ) for l in fileLines if l[0] == 'v']
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']
324
uvMapList= [(float(l[1]), float(l[2])) for l in fileLines if l[0] == 'vt']
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']
329
if IMPORT_SMOOTH_GROUPS:
330
smoothingGroups= dict([('_'.join(l[1:]), None) for l in fileLines if l[0] == 's' ])
334
print '\tvert:%i texverts:%i smoothgroups:%i' % (len(vertList), len(uvMapList), len(smoothingGroups))
498
336
# Replace filelines, Excluding v excludes "v ", "vn " and "vt "
500
337
# Remove any variables we may have created.
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)
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()
522
360
# Add materials to Blender for later is in teh OBJ
523
for k in materialDict.iterkeys():
524
materialDict[k] = Material.New(k)
527
# Make a list of all unused vert indicies that we can copy from
528
VERT_USED_LIST = [0]*len_vertList
362
# Keep this out of the dict for easy accsess.
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()
371
# Try add the name without the .001
374
mat_name[-4]=='.' and\
375
mat_name[-3:].isdigit():
376
materialDict[mat_name[-4:]]= mat
379
materialDict[mat_name]= mat
381
currentMat= nullMat= getMat('(null)', materialDict)
382
# Add all new materials allong the way...
385
# Make a list of all unused vert indices that we can copy from
386
VERT_USED_LIST= [-1]*len_vertList
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.
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
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
399
currentObjectName_real= currentObjectName
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.
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
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 }
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) }
559
419
# Only show the bad uv error once
565
#currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indicies where 1 is the first item.
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.
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):
437
currentUsedVertList,\
438
currentUsedVertListSmoothGroup,\
441
currentMaterialMeshMapping
443
#print 'getting mesh,', currentObjectName
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.
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()
453
currentUsedVertList= {}
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[:]
459
currentMaterialMeshMapping= {}
460
meshDict[currentObjectName]= (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
461
currentMesh.hasFaceUV(1)
462
contextMeshMatIdx= -1
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)
472
contextMeshMatIdx= currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
476
# For new meshes switch smoothing groups to null
477
########currentSmoothGroup= '(null)' # From examplesm changing the g/o shouldent change the smooth group.
479
currentUsedVertListSmoothGroup= currentUsedVertList[currentSmoothGroup]
481
currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]
570
486
#==================================================================================#
571
487
# Load all faces into objects, main loop #
572
488
#==================================================================================#
574
# Face and Object loading LOOP
575
#while lIdx < len(fileLines):
576
# l = fileLines[lIdx]
490
EDGE_FGON_FLAG= NMesh.EdgeFlags['FGON']
491
EDGE_DRAW_FLAG= NMesh.EdgeFlags['EDGEDRAW']
493
REL_VERT_COUNT= REL_TVERT_COUNT=0
495
while lIdx < len(fileLines):
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.
585
508
# Add material to mesh
586
if contextMeshMatIdx == -1:
587
tmpMatLs = currentMesh.materials
589
if len(tmpMatLs) == 16:
590
contextMeshMatIdx = 0 # Use first material
591
print 'material overflow, attempting to use > 16 materials. defaulting to first.'
593
contextMeshMatIdx = len(tmpMatLs)
594
currentMaterialMeshMapping[currentMat.name] = contextMeshMatIdx
595
currentMesh.addMaterial(currentMat)
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.'
516
contextMeshMatIdx= len(tmpMatLs)
517
currentMaterialMeshMapping[currentMat.name]= contextMeshMatIdx
518
currentMesh.addMaterial(currentMat)
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.
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.
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('/')
610
# Vert Index - OBJ supports negative index assignment (like python)
611
index = int(objVert[0])-1
612
# Account for negative indicies.
614
index = len_vertList+index+1
619
index = 0 # Dummy var
620
if len(objVert) == 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
625
index = len_uvMapList+index+1
627
if len_uvMapList > index:
628
vtIdxLs.append(index) # Seperate UV coords
630
# BAD FILE, I have found this so I account for it.
632
# Could ignore this- only happens with 1 in 1000 files.
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.
642
if vtIdxLs[-1] > len_uvMapList:
645
badObjUvs +=1 # ERROR, Cont
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.
528
# Support stupid multiline faces
529
# not an obj spec but some objs exist that do this.
532
# ..... instead of the more common and sane.
535
# later lines are not modified, just skepped by advancing "lIdx"
539
l.extend(fileLines[lIdx])
540
# Done supporting crappy obj faces over multiple lines.
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('/')
549
# Vert Index - OBJ supports negative index assignment (like python)
550
index= int(objVert[0])-1
552
# Account for negative indices.
554
# Assume negative verts are relative.
555
index= REL_VERT_COUNT+index+1
561
if len(objVert) == 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
566
# Assume negative verts are relative
567
index= REL_TVERT_COUNT+index+1
569
if len_uvMapList > index:
570
vtIdxLs.append(index) # Seperate UV coords
572
# BAD FILE, I have found this so I account for it.
574
# Could ignore this- only happens with 1 in 1000 files.
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.
584
if vtIdxLs[-1] > len_uvMapList:
587
badObjUvs +=1 # ERROR, Cont
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]
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:
599
# Add the verts that arnt alredy added.
601
if currentUsedVertListSmoothGroup[i] == -1:
603
currentMesh.verts.append(v)
604
currentUsedVertListSmoothGroup[i]= len(currentMesh.verts)-1
606
if face_vert_count == 2:
607
if IMPORT_EDGES and vIdxLs[0]!=vIdxLs[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
658
faceQuadVList[i] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
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]]])
613
elif face_vert_count == 4:
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
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
679
faceQuadVList[i] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
626
faceQuadVList[i]= currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
681
f = NMesh.Face(faceQuadVList)
628
f= NMesh.Face(faceQuadVList)
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] ]]
688
635
f.mode &= TEX_OFF_FLAG
690
f.mat = contextMeshMatIdx
691
f.smooth = currentSmooth
637
f.mat= contextMeshMatIdx
638
f.smooth= currentSmooth
692
639
currentMesh.faces.append(f) # move the face onto the mesh
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
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
707
faceTriVList[k] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[j]]]
649
faceTriVList[k]= currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[j]]]
709
f = NMesh.Face(faceTriVList)
651
f= NMesh.Face(faceTriVList)
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]]]
717
659
f.mode &= TEX_OFF_FLAG
719
f.mat = contextMeshMatIdx
720
f.smooth = currentSmooth
661
f.mat= contextMeshMatIdx
662
f.smooth= currentSmooth
721
663
currentMesh.faces.append(f) # move the face onto the mesh
665
elif face_vert_count > 4: # NGons.
666
# we need to map indices to uv coords.
667
currentMeshRelativeIdxs= [currentUsedVertListSmoothGroup[i] for i in vIdxLs]
670
vert2UvMapping=dict( [ (currentMeshRelativeIdxs[i],vtIdxLs[i]) for i in xrange(face_vert_count)] )
672
ngon_face_indices= BPyMesh.ngon(currentMesh, currentMeshRelativeIdxs)
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])
680
f.uv= [uvMapList[vert2UvMapping[currentMeshRelativeIdxs[i]]] for i in fillFace]
684
f.mode &= TEX_OFF_FLAG
686
f.mat= contextMeshMatIdx
687
f.smooth= currentSmooth
688
currentMesh.faces.append(f) # move the face onto the mesh
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]]
698
# Sort the pair so thet always match.
699
if i1>i2: i1,i2=i2,i1
706
for edgeVerts, users in edgeUsers.iteritems():
710
ed= currentMesh.addEdge(\
711
currentMesh.verts[i1],\
712
currentMesh.verts[i2])
714
ed.flag|= EDGE_FGON_FLAG
717
elif l[0] == 's' and IMPORT_SMOOTH_GROUPS:
725
718
# No value? then turn on.
728
currentSmoothGroup = '(null)'
721
currentSmoothGroup= '(null)'
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)'
726
currentSmoothGroup= '(null)'
736
currentSmoothGroup = '_'.join(l[1:])
729
currentSmoothGroup= '_'.join(l[1:])
738
currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
731
currentUsedVertListSmoothGroup= currentUsedVertList[currentSmoothGroup]
740
currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
733
currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]
744
737
elif l[0] == 'o' or l[0] == 'g':
746
739
# Forget about the current image
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.
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.)
755
currentObjectName = '_'.join(l[1:])
756
else: # No name given
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
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.
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()
774
currentUsedVertList = {}
777
########currentSmoothGroup = '(null)' # From examplesm changing the g/o shouldent change the smooth group.
778
currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
780
currentMaterialMeshMapping = {}
781
meshDict[currentObjectName] = (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
782
currentMesh.hasFaceUV(1)
783
contextMeshMatIdx = -1
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)
792
contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
796
# For new meshes switch smoothing groups to null
797
########currentSmoothGroup = '(null)' # From examplesm changing the g/o shouldent change the smooth group.
799
currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
801
currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
755
may_exist= False # we know the model is new.
756
else: # No name given
757
currentObjectName= '_'.join(l[1:])
761
currentObjectName_real= currentObjectName
762
currentObjectName += '_'+currentMat.name
764
obj_getmesh(may_exist)
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.
810
currentMat = materialDict['_'.join(l[1:])]
772
currentMat= getMat('_'.join(l[1:]), materialDict)
812
contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name]
774
contextMeshMatIdx= currentMaterialMeshMapping[currentMat.name]
814
contextMeshMatIdx = -1 #getMeshMaterialIndex(currentMesh, currentMat)
776
contextMeshMatIdx= -1 #getMeshMaterialIndex(currentMesh, currentMat)
778
# Check if we are splitting by material.
780
currentObjectName= '%s_%s' % (currentObjectName_real, currentMat.name)
817
785
elif l[0] == 'usemat' or l[0] == 'usemap':
818
786
if len(l) == 1 or l[1] == '(null)' or l[1] == 'off':
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.
825
793
# Assume its alredy set in the dict (may or maynot be loaded)
826
currentImg = imageDict[newImgName]
794
currentImg= imageDict[newImgName]
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.
837
elif l[0] == 'mtllib':
838
mtl_fileName.append(' '.join(l[1:]) ) # SHOULD SUPPORT MULTIPLE MTL?
803
elif l[0] == 'mtllib' and IMPORT_MTL and len(l)>1:
804
mtl_fileName.append(' '.join(l[1:]) ) # Support for multiple MTL's
841
807
# Applies material properties to materials alredy on the mesh as well as Textures.
843
809
for mtl in mtl_fileName:
844
load_mtl(DIR, mtl, meshDict, materialDict)
810
load_mtl(DIR, IMPORT_USE_EXISTING_MTL, mtl, meshDict, materialDict)
812
# Get a new scale factor if set as an option
814
if IMPORT_CONSTRAIN_BOUNDS != 0.0:
815
while (BOUNDS*SCALE) > IMPORT_CONSTRAIN_BOUNDS:
820
scn= Scene.GetCurrent()
823
for ob in scn.getChildren():
826
# Create objects for each mesh.
848
827
for mk, me in meshDict.iteritems():
851
830
# Ignore no vert meshes.
852
831
if not nme.verts: # == []
855
name = getUniqueName(mk)
856
ob = NMesh.PutRaw(nme, name)
834
ob= Object.New('Mesh', fileName)
837
ob.setSize(SCALE, SCALE, SCALE)
859
838
importedObjects.append(ob)
861
# Select all imported objects.
862
for ob in importedObjects:
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
847
grp= Group.New(fileName)
848
grp.objects= importedObjects
850
grp_ob= Object.New('Empty', fileName)
851
grp_ob.enableDupGroup= True
854
grp_ob.Layers= Layers
858
# Select all imported objects.
859
for ob in importedObjects:
864
865
if badObjUvs > 0:
865
866
print '\tERROR: found %d faces with badly formatted UV coords. everything else went okay.' % badObjUvs
876
877
def load_obj_ui(file):
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)
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)
885
893
# Get USER Options
887
('Material (*.mtl)', IMPORT_MTL, 'Imports material settings and images from the obj\'s .mtl file'),\
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.'),\
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.'),\
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)'),\
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
898
if not Draw.PupBlock('Import...', pup_block):
914
if not Draw.PupBlock('Import OBJ...', pup_block):
901
917
Window.WaitCursor(1)
902
918
Window.DrawProgressBar(0, '')
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
911
#orig_scene = Scene.GetCurrent()
913
obj_dir = stripFile(file)
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
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()
936
# Dont do material split if we dont import material
941
obj_dir= stripFile(file)
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')]
917
obj_files = [(obj_dir,stripPath(file))]
945
obj_files= [(obj_dir,stripPath(file))]
919
obj_len = len(obj_files)
947
obj_len= len(obj_files)
921
949
for d, f in obj_files:
923
951
if not sys.exists(d+f):
924
952
print 'Error: "%s%s" does not exist' % (d,f)
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()
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)
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)
940
print 'Total obj import "%s" dir: %.2f' % (obj_dir, sys.time() - time)
966
print 'Total obj import "%s" dir: %.2f' % (obj_dir, sys.time() - time)
944
Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ')
970
Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj')
948
972
if __name__ == '__main__':
b'\\ No newline at end of file'