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