Advertisement
Guest User

Untitled

a guest
Jul 10th, 2010
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.52 KB | None | 0 0
  1. #!BPY
  2.  
  3. """
  4. Name: '0bj (.obj)...'
  5. Blender: 249
  6. Group: 'Export'
  7. Tooltip: 'Save a Wavefront OBJ File'
  8. """
  9.  
  10. __author__ = "Campbell Barton, Jiri Hnidek, Paolo Ciccone"
  11. __url__ = ['http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj', 'www.blender.org', 'blenderartists.org']
  12. __version__ = "1.22"
  13.  
  14. __bpydoc__ = """\
  15. This script is an exporter to OBJ file format.
  16.  
  17. Usage:
  18.  
  19. Select the objects you wish to export and run this script from "File->Export" menu.
  20. Selecting the default options from the popup box will be good in most cases.
  21. All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d)
  22. will be exported as mesh data.
  23. """
  24.  
  25.  
  26. # ***** BEGIN GPL LICENSE BLOCK *****
  27. #
  28. # Script copyright (C) Campbell J Barton 2007-2009
  29. # - V1.22- bspline import/export added (funded by PolyDimensions GmbH)
  30. #
  31. # This program is free software; you can redistribute it and/or
  32. # modify it under the terms of the GNU General Public License
  33. # as published by the Free Software Foundation; either version 2
  34. # of the License, or (at your option) any later version.
  35. #
  36. # This program is distributed in the hope that it will be useful,
  37. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  38. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  39. # GNU General Public License for more details.
  40. #
  41. # You should have received a copy of the GNU General Public License
  42. # along with this program; if not, write to the Free Software Foundation,
  43. # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  44. #
  45. # ***** END GPL LICENCE BLOCK *****
  46. # --------------------------------------------------------------------------
  47.  
  48.  
  49. import Blender
  50. from Blender import Mesh, Scene, Window, sys, Image, Draw
  51. import BPyMesh
  52. import BPyObject
  53. import BPySys
  54. import BPyMessages
  55.  
  56. # Returns a tuple - path,extension.
  57. # 'hello.obj' > ('hello', '.obj')
  58. def splitExt(path):
  59. dotidx = path.rfind('.')
  60. if dotidx == -1:
  61. return path, ''
  62. else:
  63. return path[:dotidx], path[dotidx:]
  64.  
  65. def fixName(name):
  66. if name == None:
  67. return 'None'
  68. else:
  69. return name.replace(' ', '_')
  70.  
  71. # A Dict of Materials
  72. # (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
  73. MTL_DICT = {}
  74.  
  75. def write_mtl(filename):
  76.  
  77. world = Blender.World.GetCurrent()
  78. if world:
  79. worldAmb = world.getAmb()
  80. else:
  81. worldAmb = (0,0,0) # Default value
  82.  
  83. file = open(filename, "w")
  84. file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1])
  85. file.write('# Material Count: %i\n' % len(MTL_DICT))
  86. # Write material/image combinations we have used.
  87. for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems():
  88.  
  89. # Get the Blender data for the material and the image.
  90. # Having an image named None will make a bug, dont do it :)
  91.  
  92. file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname
  93.  
  94. if mat:
  95. file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's
  96. file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour,
  97. file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse
  98. file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular
  99. file.write('Ni %.6f\n' % mat.IOR) # Refraction index
  100. file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve)
  101.  
  102. # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting.
  103. if mat.getMode() & Blender.Material.Modes['SHADELESS']:
  104. file.write('illum 0\n') # ignore lighting
  105. elif mat.getSpec() == 0:
  106. file.write('illum 1\n') # no specular.
  107. else:
  108. file.write('illum 2\n') # light normaly
  109.  
  110. else:
  111. #write a dummy material here?
  112. file.write('Ns 0\n')
  113. file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour,
  114. file.write('Kd 0.8 0.8 0.8\n')
  115. file.write('Ks 0.8 0.8 0.8\n')
  116. file.write('d 1\n') # No alpha
  117. file.write('illum 2\n') # light normaly
  118.  
  119. # Write images!
  120. if img: # We have an image on the face!
  121. file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image
  122.  
  123. elif mat: # No face image. if we havea material search for MTex image.
  124. for mtex in mat.getTextures():
  125. if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
  126. try:
  127. filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1]
  128. file.write('map_Kd %s\n' % filename) # Diffuse mapping image
  129. break
  130. except:
  131. # Texture has no image though its an image type, best ignore.
  132. pass
  133.  
  134. file.write('\n\n')
  135.  
  136. file.close()
  137.  
  138. def copy_file(source, dest):
  139. file = open(source, 'rb')
  140. data = file.read()
  141. file.close()
  142.  
  143. file = open(dest, 'wb')
  144. file.write(data)
  145. file.close()
  146.  
  147.  
  148. def copy_images(dest_dir):
  149. if dest_dir[-1] != sys.sep:
  150. dest_dir += sys.sep
  151.  
  152. # Get unique image names
  153. uniqueImages = {}
  154. for matname, mat, image in MTL_DICT.itervalues(): # Only use image name
  155. # Get Texface images
  156. if image:
  157. uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default.
  158.  
  159. # Get MTex images
  160. if mat:
  161. for mtex in mat.getTextures():
  162. if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
  163. image_tex = mtex.tex.image
  164. if image_tex:
  165. try:
  166. uniqueImages[image_tex] = image_tex
  167. except:
  168. pass
  169.  
  170. # Now copy images
  171. copyCount = 0
  172.  
  173. for bImage in uniqueImages.itervalues():
  174. image_path = sys.expandpath(bImage.filename)
  175. if sys.exists(image_path):
  176. # Make a name for the target path.
  177. dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
  178. if not sys.exists(dest_image_path): # Image isnt alredy there
  179. print '\tCopying "%s" > "%s"' % (image_path, dest_image_path)
  180. copy_file(image_path, dest_image_path)
  181. copyCount+=1
  182. print '\tCopied %d images' % copyCount
  183.  
  184.  
  185. def test_nurbs_compat(ob):
  186. if ob.type != 'Curve':
  187. return False
  188.  
  189. for nu in ob.data:
  190. if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier
  191. return True
  192.  
  193. return False
  194.  
  195. def write_nurb(file, ob, ob_mat):
  196. tot_verts = 0
  197. cu = ob.data
  198.  
  199. # use negative indices
  200. Vector = Blender.Mathutils.Vector
  201. for nu in cu:
  202.  
  203. if nu.type==0: DEG_ORDER_U = 1
  204. else: DEG_ORDER_U = nu.orderU-1 # Tested to be correct
  205.  
  206. if nu.type==1:
  207. print "\tWarning, bezier curve:", ob.name, "only poly and nurbs curves supported"
  208. continue
  209.  
  210. if nu.knotsV:
  211. print "\tWarning, surface:", ob.name, "only poly and nurbs curves supported"
  212. continue
  213.  
  214. if len(nu) <= DEG_ORDER_U:
  215. print "\tWarning, orderU is lower then vert count, skipping:", ob.name
  216. continue
  217.  
  218. pt_num = 0
  219. do_closed = (nu.flagU & 1)
  220. do_endpoints = (do_closed==0) and (nu.flagU & 2)
  221.  
  222. for pt in nu:
  223. pt = Vector(pt[0], pt[1], pt[2]) * ob_mat
  224. file.write('v %.6f %.6f %.6f\n' % (pt[0], pt[1], pt[2]))
  225. pt_num += 1
  226. tot_verts += pt_num
  227.  
  228. file.write('g %s\n' % (fixName(ob.name))) # fixName(ob.getData(1)) could use the data name too
  229. file.write('cstype bspline\n') # not ideal, hard coded
  230. file.write('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still
  231.  
  232. curve_ls = [-(i+1) for i in xrange(pt_num)]
  233.  
  234. # 'curv' keyword
  235. if do_closed:
  236. if DEG_ORDER_U == 1:
  237. pt_num += 1
  238. curve_ls.append(-1)
  239. else:
  240. pt_num += DEG_ORDER_U
  241. curve_ls = curve_ls + curve_ls[0:DEG_ORDER_U]
  242.  
  243. file.write('curv 0.0 1.0 %s\n' % (' '.join( [str(i) for i in curve_ls] ))) # Blender has no U and V values for the curve
  244.  
  245. # 'parm' keyword
  246. tot_parm = (DEG_ORDER_U + 1) + pt_num
  247. tot_parm_div = float(tot_parm-1)
  248. parm_ls = [(i/tot_parm_div) for i in xrange(tot_parm)]
  249.  
  250. if do_endpoints: # end points, force param
  251. for i in xrange(DEG_ORDER_U+1):
  252. parm_ls[i] = 0.0
  253. parm_ls[-(1+i)] = 1.0
  254.  
  255. file.write('parm u %s\n' % ' '.join( [str(i) for i in parm_ls] ))
  256.  
  257. file.write('end\n')
  258.  
  259. return tot_verts
  260.  
  261. def write(filename, objects,\
  262. EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\
  263. EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\
  264. EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\
  265. EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False,\
  266. EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True):
  267. '''
  268. Basic write function. The context and options must be alredy set
  269. This can be accessed externaly
  270. eg.
  271. write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options.
  272. '''
  273.  
  274. def veckey3d(v):
  275. return round(v.x, 6), round(v.y, 6), round(v.z, 6)
  276.  
  277. def veckey2d(v):
  278. return round(v.x, 6), round(v.y, 6)
  279.  
  280. def findVertexGroupName(face, vWeightMap):
  281. """
  282. Searches the vertexDict to see what groups is assigned to a given face.
  283. We use a frequency system in order to sort out the name because a given vetex can
  284. belong to two or more groups at the same time. To find the right name for the face
  285. we list all the possible vertex group names with their frequency and then sort by
  286. frequency in descend order. The top element is the one shared by the highest number
  287. of vertices is the face's group
  288. """
  289. weightDict = {}
  290. for vert in face:
  291. vWeights = vWeightMap[vert.index]
  292. for vGroupName, weight in vWeights:
  293. weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight
  294.  
  295. if weightDict:
  296. alist = [(weight,vGroupName) for vGroupName, weight in weightDict.iteritems()] # sort least to greatest amount of weight
  297. alist.sort()
  298. return(alist[-1][1]) # highest value last
  299. else:
  300. return '(null)'
  301.  
  302.  
  303. print 'OBJ Export path: "%s"' % filename
  304. temp_mesh_name = '~tmp-mesh'
  305.  
  306. time1 = sys.time()
  307. scn = Scene.GetCurrent()
  308.  
  309. file = open(filename, "w")
  310.  
  311. # Write Header
  312. file.write('# Blender3D v%s OBJ File: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] ))
  313. file.write('# www.blender3d.org\n')
  314.  
  315. # Tell the obj file what material file to use.
  316. if EXPORT_MTL:
  317. mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1])
  318. file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] ))
  319.  
  320. # Get the container mesh. - used for applying modifiers and non mesh objects.
  321. containerMesh = meshName = tempMesh = None
  322. for meshName in Blender.NMesh.GetNames():
  323. if meshName.startswith(temp_mesh_name):
  324. tempMesh = Mesh.Get(meshName)
  325. if not tempMesh.users:
  326. containerMesh = tempMesh
  327. if not containerMesh:
  328. containerMesh = Mesh.New(temp_mesh_name)
  329.  
  330. if EXPORT_ROTX90:
  331. mat_xrot90= Blender.Mathutils.RotationMatrix(-90, 4, 'x')
  332.  
  333. del meshName
  334. del tempMesh
  335.  
  336. # Initialize totals, these are updated each object
  337. totverts = totuvco = totno = 1
  338.  
  339. face_vert_index = 1
  340.  
  341. globalNormals = {}
  342.  
  343. # Get all meshes
  344. for ob_main in objects:
  345. for ob, ob_mat in BPyObject.getDerivedObjects(ob_main):
  346.  
  347. # Nurbs curve support
  348. if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob):
  349. if EXPORT_ROTX90:
  350. ob_mat = ob_mat * mat_xrot90
  351.  
  352. totverts += write_nurb(file, ob, ob_mat)
  353.  
  354. continue
  355. # end nurbs
  356.  
  357. # Will work for non meshes now! :)
  358. # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None)
  359. me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn)
  360. if not me:
  361. continue
  362.  
  363. if EXPORT_UV:
  364. faceuv= me.faceUV
  365. else:
  366. faceuv = False
  367.  
  368. # We have a valid mesh
  369. if EXPORT_TRI and me.faces:
  370. # Add a dummy object to it.
  371. has_quads = False
  372. for f in me.faces:
  373. if len(f) == 4:
  374. has_quads = True
  375. break
  376.  
  377. if has_quads:
  378. oldmode = Mesh.Mode()
  379. Mesh.Mode(Mesh.SelectModes['FACE'])
  380.  
  381. me.sel = True
  382. tempob = scn.objects.new(me)
  383. me.quadToTriangle(0) # more=0 shortest length
  384. oldmode = Mesh.Mode(oldmode)
  385. scn.objects.unlink(tempob)
  386.  
  387. Mesh.Mode(oldmode)
  388.  
  389. # Make our own list so it can be sorted to reduce context switching
  390. faces = [ f for f in me.faces ]
  391.  
  392. if EXPORT_EDGES:
  393. edges = me.edges
  394. else:
  395. edges = []
  396.  
  397. if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write
  398. continue # dont bother with this mesh.
  399.  
  400. if EXPORT_ROTX90:
  401. me.transform(ob_mat*mat_xrot90)
  402. else:
  403. me.transform(ob_mat)
  404.  
  405. # High Quality Normals
  406. if EXPORT_NORMALS and faces:
  407. if EXPORT_NORMALS_HQ:
  408. BPyMesh.meshCalcNormals(me)
  409. else:
  410. # transforming normals is incorrect
  411. # when the matrix is scaled,
  412. # better to recalculate them
  413. me.calcNormals()
  414.  
  415. # # Crash Blender
  416. #materials = me.getMaterials(1) # 1 == will return None in the list.
  417. materials = me.materials
  418.  
  419. materialNames = []
  420. materialItems = materials[:]
  421. if materials:
  422. for mat in materials:
  423. if mat: # !=None
  424. materialNames.append(mat.name)
  425. else:
  426. materialNames.append(None)
  427. # Cant use LC because some materials are None.
  428. # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken.
  429.  
  430. # Possible there null materials, will mess up indicies
  431. # but at least it will export, wait until Blender gets fixed.
  432. materialNames.extend((16-len(materialNames)) * [None])
  433. materialItems.extend((16-len(materialItems)) * [None])
  434.  
  435. # Sort by Material, then images
  436. # so we dont over context switch in the obj file.
  437. if EXPORT_KEEP_VERT_ORDER:
  438. pass
  439. elif faceuv:
  440. try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth))
  441. except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth)))
  442. elif len(materials) > 1:
  443. try: faces.sort(key = lambda a: (a.mat, a.smooth))
  444. except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth)))
  445. else:
  446. # no materials
  447. try: faces.sort(key = lambda a: a.smooth)
  448. except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth))
  449.  
  450. # Set the default mat to no material and no image.
  451. contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get.
  452. contextSmooth = None # Will either be true or false, set bad to force initialization switch.
  453.  
  454. if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB:
  455. name1 = ob.name
  456. name2 = ob.getData(1)
  457. if name1 == name2:
  458. obnamestring = fixName(name1)
  459. else:
  460. obnamestring = '%s_%s' % (fixName(name1), fixName(name2))
  461.  
  462. if EXPORT_BLEN_OBS:
  463. file.write('o %s\n' % obnamestring) # Write Object name
  464. else: # if EXPORT_GROUP_BY_OB:
  465. file.write('g %s\n' % obnamestring)
  466.  
  467.  
  468. # Vert
  469. for v in me.verts:
  470. file.write('v %i %i %i\n' % tuple(v.co * 1000))
  471.  
  472. # UV
  473. if faceuv:
  474. uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/
  475.  
  476. uv_dict = {} # could use a set() here
  477. for f_index, f in enumerate(faces):
  478.  
  479. for uv_index, uv in enumerate(f.uv):
  480. uvkey = veckey2d(uv)
  481. try:
  482. uv_face_mapping[f_index][uv_index] = uv_dict[uvkey]
  483. except:
  484. uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict)
  485. file.write('vt %.6f %.6f\n' % tuple(uv))
  486.  
  487. uv_unique_count = len(uv_dict)
  488. del uv, uvkey, uv_dict, f_index, uv_index
  489. # Only need uv_unique_count and uv_face_mapping
  490.  
  491. # NORMAL, Smooth/Non smoothed.
  492. if EXPORT_NORMALS:
  493. for f in faces:
  494. if f.smooth:
  495. for v in f:
  496. noKey = veckey3d(v.no)
  497. if not globalNormals.has_key( noKey ):
  498. globalNormals[noKey] = totno
  499. totno +=1
  500. file.write('vn %.6f %.6f %.6f\n' % noKey)
  501. else:
  502. # Hard, 1 normal from the face.
  503. noKey = veckey3d(f.no)
  504. if not globalNormals.has_key( noKey ):
  505. globalNormals[noKey] = totno
  506. totno +=1
  507. file.write('vn %.6f %.6f %.6f\n' % noKey)
  508.  
  509. if not faceuv:
  510. f_image = None
  511.  
  512. if EXPORT_POLYGROUPS:
  513. # Retrieve the list of vertex groups
  514. vertGroupNames = me.getVertGroupNames()
  515.  
  516. currentVGroup = ''
  517. # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to
  518. vgroupsMap = [[] for _i in xrange(len(me.verts))]
  519. for vertexGroupName in vertGroupNames:
  520. for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1):
  521. vgroupsMap[vIdx].append((vertexGroupName, vWeight))
  522.  
  523. for f_index, f in enumerate(faces):
  524. f_v= f.v
  525. f_smooth= f.smooth
  526. f_mat = min(f.mat, len(materialNames)-1)
  527. if faceuv:
  528. f_image = f.image
  529. f_uv= f.uv
  530.  
  531. # MAKE KEY
  532. if faceuv and f_image: # Object is always true.
  533. key = materialNames[f_mat], f_image.name
  534. else:
  535. key = materialNames[f_mat], None # No image, use None instead.
  536.  
  537. # Write the vertex group
  538. if EXPORT_POLYGROUPS:
  539. if vertGroupNames:
  540. # find what vertext group the face belongs to
  541. theVGroup = findVertexGroupName(f,vgroupsMap)
  542. if theVGroup != currentVGroup:
  543. currentVGroup = theVGroup
  544. file.write('g %s\n' % theVGroup)
  545.  
  546. # CHECK FOR CONTEXT SWITCH
  547. if key == contextMat:
  548. pass # Context alredy switched, dont do anything
  549. else:
  550. if key[0] == None and key[1] == None:
  551. # Write a null material, since we know the context has changed.
  552. if EXPORT_GROUP_BY_MAT:
  553. file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null)
  554. file.write('usemtl (null)\n') # mat, image
  555.  
  556. else:
  557. mat_data= MTL_DICT.get(key)
  558. if not mat_data:
  559. # First add to global dict so we can export to mtl
  560. # Then write mtl
  561.  
  562. # Make a new names from the mat and image name,
  563. # converting any spaces to underscores with fixName.
  564.  
  565. # If none image dont bother adding it to the name
  566. if key[1] == None:
  567. mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image
  568. else:
  569. mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image
  570.  
  571. if EXPORT_GROUP_BY_MAT:
  572. file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null)
  573.  
  574. file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null)
  575.  
  576. contextMat = key
  577. if f_smooth != contextSmooth:
  578. if f_smooth: # on now off
  579. file.write('s 1\n')
  580. contextSmooth = f_smooth
  581. else: # was off now on
  582. file.write('s off\n')
  583. contextSmooth = f_smooth
  584.  
  585. file.write('f')
  586. if faceuv:
  587. if EXPORT_NORMALS:
  588. if f_smooth: # Smoothed, use vertex normals
  589. for vi, v in enumerate(f_v):
  590. file.write( ' %d/%d/%d' % (\
  591. v.index+totverts,\
  592. totuvco + uv_face_mapping[f_index][vi],\
  593. globalNormals[ veckey3d(v.no) ])) # vert, uv, normal
  594.  
  595. else: # No smoothing, face normals
  596. no = globalNormals[ veckey3d(f.no) ]
  597. for vi, v in enumerate(f_v):
  598. file.write( ' %d/%d/%d' % (\
  599. v.index+totverts,\
  600. totuvco + uv_face_mapping[f_index][vi],\
  601. no)) # vert, uv, normal
  602.  
  603. else: # No Normals
  604. for vi, v in enumerate(f_v):
  605. file.write( ' %d/%d' % (\
  606. v.index+totverts,\
  607. totuvco + uv_face_mapping[f_index][vi])) # vert, uv
  608.  
  609. face_vert_index += len(f_v)
  610.  
  611. else: # No UV's
  612. if EXPORT_NORMALS:
  613. if f_smooth: # Smoothed, use vertex normals
  614. for v in f_v:
  615. file.write( ' %d//%d' % (\
  616. v.index+totverts,\
  617. globalNormals[ veckey3d(v.no) ]))
  618. else: # No smoothing, face normals
  619. no = globalNormals[ veckey3d(f.no) ]
  620. for v in f_v:
  621. file.write( ' %d//%d' % (\
  622. v.index+totverts,\
  623. no))
  624. else: # No Normals
  625. for v in f_v:
  626. file.write( ' %d' % (\
  627. v.index+totverts))
  628.  
  629. file.write('\n')
  630.  
  631. # Write edges.
  632. if EXPORT_EDGES:
  633. LOOSE= Mesh.EdgeFlags.LOOSE
  634. for ed in edges:
  635. if ed.flag & LOOSE:
  636. file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts))
  637.  
  638. # Make the indicies global rather then per mesh
  639. totverts += len(me.verts)
  640. if faceuv:
  641. totuvco += uv_unique_count
  642. me.verts= None
  643. file.close()
  644.  
  645.  
  646. # Now we have all our materials, save them
  647. if EXPORT_MTL:
  648. write_mtl(mtlfilename)
  649. if EXPORT_COPY_IMAGES:
  650. dest_dir = filename
  651. # Remove chars until we are just the path.
  652. while dest_dir and dest_dir[-1] not in '\\/':
  653. dest_dir = dest_dir[:-1]
  654. if dest_dir:
  655. copy_images(dest_dir)
  656. else:
  657. print '\tError: "%s" could not be used as a base for an image path.' % filename
  658.  
  659. print "OBJ Export time: %.2f" % (sys.time() - time1)
  660.  
  661.  
  662.  
  663. def write_ui(filename):
  664.  
  665. if not filename.lower().endswith('.obj'):
  666. filename += '.obj'
  667.  
  668. if not BPyMessages.Warning_SaveOver(filename):
  669. return
  670.  
  671. global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\
  672. EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\
  673. EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\
  674. EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\
  675. EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\
  676. EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS
  677.  
  678. EXPORT_APPLY_MODIFIERS = Draw.Create(0)
  679. EXPORT_ROTX90 = Draw.Create(1)
  680. EXPORT_TRI = Draw.Create(0)
  681. EXPORT_EDGES = Draw.Create(1)
  682. EXPORT_NORMALS = Draw.Create(0)
  683. EXPORT_NORMALS_HQ = Draw.Create(0)
  684. EXPORT_UV = Draw.Create(1)
  685. EXPORT_MTL = Draw.Create(1)
  686. EXPORT_SEL_ONLY = Draw.Create(1)
  687. EXPORT_ALL_SCENES = Draw.Create(0)
  688. EXPORT_ANIMATION = Draw.Create(0)
  689. EXPORT_COPY_IMAGES = Draw.Create(0)
  690. EXPORT_BLEN_OBS = Draw.Create(0)
  691. EXPORT_GROUP_BY_OB = Draw.Create(0)
  692. EXPORT_GROUP_BY_MAT = Draw.Create(0)
  693. EXPORT_KEEP_VERT_ORDER = Draw.Create(1)
  694. EXPORT_POLYGROUPS = Draw.Create(0)
  695. EXPORT_CURVE_AS_NURBS = Draw.Create(1)
  696.  
  697.  
  698. # Old UI
  699. '''
  700. # removed too many options are bad!
  701.  
  702. # Get USER Options
  703. pup_block = [\
  704. ('Context...'),\
  705. ('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection. Else export whole scene.'),\
  706. ('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a separate OBJ file.'),\
  707. ('Animation', EXPORT_ANIMATION, 'Each frame as a numbered OBJ file.'),\
  708. ('Object Prefs...'),\
  709. ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\
  710. ('Rotate X90', EXPORT_ROTX90 , 'Rotate on export so Blenders UP is translated into OBJs UP'),\
  711. ('Keep Vert Order', EXPORT_KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\
  712. ('Extra Data...'),\
  713. ('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\
  714. ('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\
  715. ('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\
  716. ('UVs', EXPORT_UV, 'Export texface UV coords.'),\
  717. ('Materials', EXPORT_MTL, 'Write a separate MTL file with the OBJ.'),\
  718. ('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never overwrite.'),\
  719. ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\
  720. ('Grouping...'),\
  721. ('Objects', EXPORT_BLEN_OBS, 'Export blender objects as "OBJ objects".'),\
  722. ('Object Groups', EXPORT_GROUP_BY_OB, 'Export blender objects as "OBJ Groups".'),\
  723. ('Material Groups', EXPORT_GROUP_BY_MAT, 'Group by materials.'),\
  724. ]
  725.  
  726. if not Draw.PupBlock('Export...', pup_block):
  727. return
  728. '''
  729.  
  730. # BEGIN ALTERNATIVE UI *******************
  731. if True:
  732.  
  733. EVENT_NONE = 0
  734. EVENT_EXIT = 1
  735. EVENT_REDRAW = 2
  736. EVENT_EXPORT = 3
  737.  
  738. GLOBALS = {}
  739. GLOBALS['EVENT'] = EVENT_REDRAW
  740. #GLOBALS['MOUSE'] = Window.GetMouseCoords()
  741. GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()]
  742.  
  743. def obj_ui_set_event(e,v):
  744. GLOBALS['EVENT'] = e
  745.  
  746. def do_split(e,v):
  747. global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER, EXPORT_POLYGROUPS
  748. if EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val:
  749. EXPORT_KEEP_VERT_ORDER.val = 0
  750. else:
  751. EXPORT_KEEP_VERT_ORDER.val = 1
  752.  
  753. def do_vertorder(e,v):
  754. global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER
  755. if EXPORT_KEEP_VERT_ORDER.val:
  756. EXPORT_BLEN_OBS.val = EXPORT_GROUP_BY_OB.val = EXPORT_GROUP_BY_MAT.val = EXPORT_APPLY_MODIFIERS.val = 0
  757. else:
  758. if not (EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val):
  759. EXPORT_KEEP_VERT_ORDER.val = 1
  760.  
  761.  
  762. def do_help(e,v):
  763. url = __url__[0]
  764. print 'Trying to open web browser with documentation at this address...'
  765. print '\t' + url
  766.  
  767. try:
  768. import webbrowser
  769. webbrowser.open(url)
  770. except:
  771. print '...could not open a browser window.'
  772.  
  773. def obj_ui():
  774. ui_x, ui_y = GLOBALS['MOUSE']
  775.  
  776. # Center based on overall pup size
  777. ui_x -= 165
  778. ui_y -= 140
  779.  
  780. global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\
  781. EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\
  782. EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\
  783. EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\
  784. EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\
  785. EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS
  786.  
  787. Draw.Label('Context...', ui_x+9, ui_y+239, 220, 20)
  788. Draw.BeginAlign()
  789. EXPORT_SEL_ONLY = Draw.Toggle('Selection Only', EVENT_NONE, ui_x+9, ui_y+219, 110, 20, EXPORT_SEL_ONLY.val, 'Only export objects in visible selection. Else export whole scene.')
  790. EXPORT_ALL_SCENES = Draw.Toggle('All Scenes', EVENT_NONE, ui_x+119, ui_y+219, 110, 20, EXPORT_ALL_SCENES.val, 'Each scene as a separate OBJ file.')
  791. EXPORT_ANIMATION = Draw.Toggle('Animation', EVENT_NONE, ui_x+229, ui_y+219, 110, 20, EXPORT_ANIMATION.val, 'Each frame as a numbered OBJ file.')
  792. Draw.EndAlign()
  793.  
  794.  
  795. Draw.Label('Output Options...', ui_x+9, ui_y+189, 220, 20)
  796. Draw.BeginAlign()
  797. EXPORT_APPLY_MODIFIERS = Draw.Toggle('Apply Modifiers', EVENT_REDRAW, ui_x+9, ui_y+170, 110, 20, EXPORT_APPLY_MODIFIERS.val, 'Use transformed mesh data from each object. May break vert order for morph targets.', do_split)
  798. EXPORT_ROTX90 = Draw.Toggle('Rotate X90', EVENT_NONE, ui_x+119, ui_y+170, 110, 20, EXPORT_ROTX90.val, 'Rotate on export so Blenders UP is translated into OBJs UP')
  799. EXPORT_COPY_IMAGES = Draw.Toggle('Copy Images', EVENT_NONE, ui_x+229, ui_y+170, 110, 20, EXPORT_COPY_IMAGES.val, 'Copy image files to the export directory, never overwrite.')
  800. Draw.EndAlign()
  801.  
  802.  
  803. Draw.Label('Export...', ui_x+9, ui_y+139, 220, 20)
  804. Draw.BeginAlign()
  805. EXPORT_EDGES = Draw.Toggle('Edges', EVENT_NONE, ui_x+9, ui_y+120, 50, 20, EXPORT_EDGES.val, 'Edges not connected to faces.')
  806. EXPORT_TRI = Draw.Toggle('Triangulate', EVENT_NONE, ui_x+59, ui_y+120, 70, 20, EXPORT_TRI.val, 'Triangulate quads.')
  807. Draw.EndAlign()
  808. Draw.BeginAlign()
  809. EXPORT_MTL = Draw.Toggle('Materials', EVENT_NONE, ui_x+139, ui_y+120, 70, 20, EXPORT_MTL.val, 'Write a separate MTL file with the OBJ.')
  810. EXPORT_UV = Draw.Toggle('UVs', EVENT_NONE, ui_x+209, ui_y+120, 31, 20, EXPORT_UV.val, 'Export texface UV coords.')
  811. Draw.EndAlign()
  812. Draw.BeginAlign()
  813. EXPORT_NORMALS = Draw.Toggle('Normals', EVENT_NONE, ui_x+250, ui_y+120, 59, 20, EXPORT_NORMALS.val, 'Export vertex normal data (Ignored on import).')
  814. EXPORT_NORMALS_HQ = Draw.Toggle('HQ', EVENT_NONE, ui_x+309, ui_y+120, 31, 20, EXPORT_NORMALS_HQ.val, 'Calculate high quality normals for rendering.')
  815. Draw.EndAlign()
  816. EXPORT_POLYGROUPS = Draw.Toggle('Polygroups', EVENT_REDRAW, ui_x+9, ui_y+95, 120, 20, EXPORT_POLYGROUPS.val, 'Export vertex groups as OBJ groups (one group per face approximation).')
  817.  
  818. EXPORT_CURVE_AS_NURBS = Draw.Toggle('Nurbs', EVENT_NONE, ui_x+139, ui_y+95, 100, 20, EXPORT_CURVE_AS_NURBS.val, 'Export 3D nurbs curves and polylines as OBJ curves, (bezier not supported).')
  819.  
  820.  
  821. Draw.Label('Blender Objects as OBJ:', ui_x+9, ui_y+59, 220, 20)
  822. Draw.BeginAlign()
  823. EXPORT_BLEN_OBS = Draw.Toggle('Objects', EVENT_REDRAW, ui_x+9, ui_y+39, 60, 20, EXPORT_BLEN_OBS.val, 'Export blender objects as "OBJ objects".', do_split)
  824. EXPORT_GROUP_BY_OB = Draw.Toggle('Groups', EVENT_REDRAW, ui_x+69, ui_y+39, 60, 20, EXPORT_GROUP_BY_OB.val, 'Export blender objects as "OBJ Groups".', do_split)
  825. EXPORT_GROUP_BY_MAT = Draw.Toggle('Material Groups', EVENT_REDRAW, ui_x+129, ui_y+39, 100, 20, EXPORT_GROUP_BY_MAT.val, 'Group by materials.', do_split)
  826. Draw.EndAlign()
  827.  
  828. EXPORT_KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+239, ui_y+39, 100, 20, EXPORT_KEEP_VERT_ORDER.val, 'Keep vert and face order, disables some other options. Use for morph targets.', do_vertorder)
  829.  
  830. Draw.BeginAlign()
  831. Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 20, 'Load the wiki page for this script', do_help)
  832. Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 20, '', obj_ui_set_event)
  833. Draw.PushButton('Export', EVENT_EXPORT, ui_x+229, ui_y+9, 110, 20, 'Export with these settings', obj_ui_set_event)
  834. Draw.EndAlign()
  835.  
  836.  
  837. # hack so the toggle buttons redraw. this is not nice at all
  838. while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_EXPORT):
  839. Draw.UIBlock(obj_ui, 0)
  840.  
  841. if GLOBALS['EVENT'] != EVENT_EXPORT:
  842. return
  843.  
  844. # END ALTERNATIVE UI *********************
  845.  
  846.  
  847. if EXPORT_KEEP_VERT_ORDER.val:
  848. EXPORT_BLEN_OBS.val = False
  849. EXPORT_GROUP_BY_OB.val = False
  850. EXPORT_GROUP_BY_MAT.val = False
  851. EXPORT_APPLY_MODIFIERS.val = False
  852.  
  853. Window.EditMode(0)
  854. Window.WaitCursor(1)
  855.  
  856. EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val
  857. EXPORT_ROTX90 = EXPORT_ROTX90.val
  858. EXPORT_TRI = EXPORT_TRI.val
  859. EXPORT_EDGES = EXPORT_EDGES.val
  860. EXPORT_NORMALS = EXPORT_NORMALS.val
  861. EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val
  862. EXPORT_UV = EXPORT_UV.val
  863. EXPORT_MTL = EXPORT_MTL.val
  864. EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val
  865. EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val
  866. EXPORT_ANIMATION = EXPORT_ANIMATION.val
  867. EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES.val
  868. EXPORT_BLEN_OBS = EXPORT_BLEN_OBS.val
  869. EXPORT_GROUP_BY_OB = EXPORT_GROUP_BY_OB.val
  870. EXPORT_GROUP_BY_MAT = EXPORT_GROUP_BY_MAT.val
  871. EXPORT_KEEP_VERT_ORDER = EXPORT_KEEP_VERT_ORDER.val
  872. EXPORT_POLYGROUPS = EXPORT_POLYGROUPS.val
  873. EXPORT_CURVE_AS_NURBS = EXPORT_CURVE_AS_NURBS.val
  874.  
  875.  
  876. base_name, ext = splitExt(filename)
  877. context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension
  878.  
  879. # Use the options to export the data using write()
  880. # def write(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True):
  881. orig_scene = Scene.GetCurrent()
  882. if EXPORT_ALL_SCENES:
  883. export_scenes = Scene.Get()
  884. else:
  885. export_scenes = [orig_scene]
  886.  
  887. # Export all scenes.
  888. for scn in export_scenes:
  889. scn.makeCurrent() # If alredy current, this is not slow.
  890. context = scn.getRenderingContext()
  891. orig_frame = Blender.Get('curframe')
  892.  
  893. if EXPORT_ALL_SCENES: # Add scene name into the context_name
  894. context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
  895.  
  896. # Export an animation?
  897. if EXPORT_ANIMATION:
  898. scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame.
  899. else:
  900. scene_frames = [orig_frame] # Dont export an animation.
  901.  
  902. # Loop through all frames in the scene and export.
  903. for frame in scene_frames:
  904. if EXPORT_ANIMATION: # Add frame to the filename.
  905. context_name[2] = '_%.6d' % frame
  906.  
  907. Blender.Set('curframe', frame)
  908. if EXPORT_SEL_ONLY:
  909. export_objects = scn.objects.context
  910. else:
  911. export_objects = scn.objects
  912.  
  913. full_path= ''.join(context_name)
  914.  
  915. # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad.
  916. # EXPORT THE FILE.
  917. write(full_path, export_objects,\
  918. EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\
  919. EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\
  920. EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\
  921. EXPORT_ROTX90, EXPORT_BLEN_OBS,\
  922. EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\
  923. EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS)
  924.  
  925. Blender.Set('curframe', orig_frame)
  926.  
  927. # Restore old active scene.
  928. orig_scene.makeCurrent()
  929. Window.WaitCursor(0)
  930.  
  931.  
  932. if __name__ == '__main__':
  933. Window.FileSelector(write_ui, 'Export Wavefront OBJ', sys.makename(ext='.obj'))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement