added an option for python Draw.UIBlock(func, mouse_exit) so moving the mouse outside...
[blender.git] / release / scripts / export_fbx.py
1 #!BPY
2 """
3 Name: 'Autodesk FBX (.fbx)...'
4 Blender: 244
5 Group: 'Export'
6 Tooltip: 'Selection to an ASCII Autodesk FBX '
7 """
8 __author__ = "Campbell Barton"
9 __url__ = ['www.blender.org', 'blenderartists.org']
10 __version__ = "1.1"
11
12 __bpydoc__ = """\
13 This script is an exporter to the FBX file format.
14
15 http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx
16 """
17 # --------------------------------------------------------------------------
18 # FBX Export v0.1 by Campbell Barton (AKA Ideasman)
19 # --------------------------------------------------------------------------
20 # ***** BEGIN GPL LICENSE BLOCK *****
21 #
22 # This program is free software; you can redistribute it and/or
23 # modify it under the terms of the GNU General Public License
24 # as published by the Free Software Foundation; either version 2
25 # of the License, or (at your option) any later version.
26 #
27 # This program is distributed in the hope that it will be useful,
28 # but WITHOUT ANY WARRANTY; without even the implied warranty of
29 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30 # GNU General Public License for more details.
31 #
32 # You should have received a copy of the GNU General Public License
33 # along with this program; if not, write to the Free Software Foundation,
34 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
35 #
36 # ***** END GPL LICENCE BLOCK *****
37 # --------------------------------------------------------------------------
38
39 try:
40         import time
41         # import os # only needed for batch export, nbot used yet
42 except:
43         time = None # use this to check if they have python modules installed
44
45 # for python 2.3 support
46 try:
47         set()
48 except:
49         try:
50                 from sets import Set as set
51         except:
52                 set = None # so it complains you dont have a !
53
54 # os is only needed for batch 'own dir' option
55 try:
56         import os
57 except:
58         os = None
59
60 import Blender
61 import bpy
62 from Blender.Mathutils import Matrix, Vector, RotationMatrix
63
64 import BPyObject
65 import BPyMesh
66 import BPySys
67 import BPyMessages
68
69 import sys
70
71 ## This was used to make V, but faster not to do all that
72 ##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}'
73 ##v = range(255)
74 ##for c in valid: v.remove(ord(c))
75 v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,43,47,58,59,60,61,62,63,64,92,94,96,124,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254]
76 invalid = ''.join([chr(i) for i in v])
77 def cleanName(name):
78         for ch in invalid:      name = name.replace(ch, '_')
79         return name
80 del v, i
81
82
83 def copy_file(source, dest):
84         file = open(source, 'rb')
85         data = file.read()
86         file.close()
87         
88         file = open(dest, 'wb')
89         file.write(data)
90         file.close()
91
92
93 def copy_images(dest_dir, textures):
94         if not dest_dir.endswith(Blender.sys.sep):
95                 dest_dir += Blender.sys.sep
96         
97         image_paths = set()
98         for img in textures:
99                 image_paths.add(Blender.sys.expandpath(img.filename))
100         
101         # Now copy images
102         copyCount = 0
103         for image_path in image_paths:
104                 if Blender.sys.exists(image_path):
105                         # Make a name for the target path.
106                         dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
107                         if not Blender.sys.exists(dest_image_path): # Image isnt alredy there
108                                 print '\tCopying "%s" > "%s"' % (image_path, dest_image_path)
109                                 try:
110                                         copy_file(image_path, dest_image_path)
111                                         copyCount+=1
112                                 except:
113                                         print '\t\tWarning, file failed to copy, skipping.'
114         
115         print '\tCopied %d images' % copyCount
116
117 mtx4_identity = Matrix()
118
119 # testing
120 mtx_x90         = RotationMatrix( 90, 3, 'x') # used
121 #mtx_x90n       = RotationMatrix(-90, 3, 'x')
122 #mtx_y90        = RotationMatrix( 90, 3, 'y')
123 #mtx_y90n       = RotationMatrix(-90, 3, 'y')
124 #mtx_z90        = RotationMatrix( 90, 3, 'z')
125 #mtx_z90n       = RotationMatrix(-90, 3, 'z')
126
127 #mtx4_x90       = RotationMatrix( 90, 4, 'x')
128 mtx4_x90n       = RotationMatrix(-90, 4, 'x') # used
129 #mtx4_y90       = RotationMatrix( 90, 4, 'y')
130 mtx4_y90n       = RotationMatrix(-90, 4, 'y') # used
131 mtx4_z90        = RotationMatrix( 90, 4, 'z') # used
132 mtx4_z90n       = RotationMatrix(-90, 4, 'z') # used
133
134 def strip_path(p):
135         return p.split('\\')[-1].split('/')[-1]
136
137 # Used to add the scene name into the filename without using odd chars  
138 sane_name_mapping_ob = {}
139 sane_name_mapping_mat = {}
140 sane_name_mapping_tex = {}
141 sane_name_mapping_take = {}
142 sane_name_mapping_group = {}
143
144 # Make sure reserved names are not used
145 sane_name_mapping_ob['Scene'] = 'Scene_'
146 sane_name_mapping_ob['blend_root'] = 'blend_root_'
147
148 def increment_string(t):
149         name = t
150         num = ''
151         while name and name[-1].isdigit():
152                 num = name[-1] + num
153                 name = name[:-1]
154         if num: return '%s%d' % (name, int(num)+1)      
155         else:   return name + '_0'
156
157
158
159 # todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up.
160 def sane_name(data, dct):
161         #if not data: return None
162         name = data.name
163         
164         # dont cache, only ever call once for each data type now,
165         # so as to avoid namespace collision between types - like with objects <-> bones
166         #try:           return dct[name]
167         #except:                pass
168         
169         orig_name = name
170         if not name:
171                 name = 'unnamed' # blank string, ASKING FOR TROUBLE!
172         else:
173                 #name = BPySys.cleanName(name)
174                 name = cleanName(name) # use our own
175         
176         while name in dct.itervalues(): name = increment_string(name)
177         
178         dct[orig_name] = name
179         return name
180
181 def sane_obname(data):          return sane_name(data, sane_name_mapping_ob)
182 def sane_matname(data):         return sane_name(data, sane_name_mapping_mat)
183 def sane_texname(data):         return sane_name(data, sane_name_mapping_tex)
184 def sane_takename(data):        return sane_name(data, sane_name_mapping_take)
185 def sane_groupname(data):       return sane_name(data, sane_name_mapping_group)
186
187
188
189
190 def mat4x4str(mat):
191         return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ])
192
193 def meshNormalizedWeights(me):
194         try: # account for old bad BPyMesh
195                 groupNames, vWeightList = BPyMesh.meshWeight2List(me)
196         except:
197                 return [],[]
198         
199         if not groupNames:
200                 return [],[]
201         
202         for i, vWeights in enumerate(vWeightList):
203                 tot = 0.0
204                 for w in vWeights:
205                         tot+=w
206                 
207                 if tot:
208                         for j, w in enumerate(vWeights):
209                                 vWeights[j] = w/tot
210         
211         return groupNames, vWeightList
212
213 header_comment = \
214 '''; FBX 6.1.0 project file
215 ; Created by Blender FBX Exporter
216 ; for support mail: ideasman42@gmail.com
217 ; ----------------------------------------------------
218
219 '''
220
221 # This func can be called with just the filename
222 def write(filename, batch_objects = None, \
223                 EXP_OBS_SELECTED =                      True,
224                 EXP_MESH =                                      True,
225                 EXP_MESH_APPLY_MOD =            True,
226                 EXP_MESH_HQ_NORMALS =           False,
227                 EXP_ARMATURE =                          True,
228                 EXP_LAMP =                                      True,
229                 EXP_CAMERA =                            True,
230                 EXP_EMPTY =                                     True,
231                 EXP_IMAGE_COPY =                        False,
232                 GLOBAL_MATRIX =                         Matrix(),
233                 ANIM_ENABLE =                           True,
234                 ANIM_OPTIMIZE =                         True,
235                 ANIM_OPTIMIZE_PRECISSION =      6,
236                 ANIM_ACTION_ALL =                       False,
237                 BATCH_ENABLE =                          False,
238                 BATCH_GROUP =                           True,
239                 BATCH_SCENE =                           False,
240                 BATCH_FILE_PREFIX =                     '',
241                 BATCH_OWN_DIR =                         False
242         ):
243         
244         # ----------------- Batch support!
245         if BATCH_ENABLE:
246                 if os == None:  BATCH_OWN_DIR = False
247                 
248                 fbxpath = filename
249                 
250                 # get the path component of filename
251                 tmp_exists = Blender.sys.exists(fbxpath)
252                 
253                 if tmp_exists != 2: # a file, we want a path
254                         while fbxpath and fbxpath[-1] not in ('/', '\\'):
255                                 fbxpath = fbxpath[:-1]
256                         if not filename:
257                                 Draw.PupMenu('Error%t|Directory does not exist!')
258                                 return
259                         
260                         tmp_exists = Blender.sys.exists(fbxpath)
261                 
262                 if tmp_exists != 2:
263                         Draw.PupMenu('Error%t|Directory does not exist!')
264                         return
265                 
266                 if not fbxpath.endswith(Blender.sys.sep):
267                         fbxpath += Blender.sys.sep
268                 del tmp_exists
269                 
270                 
271                 if BATCH_GROUP:
272                         data_seq = bpy.data.groups
273                 else:
274                         data_seq = bpy.data.scenes
275                 
276                 # call this function within a loop with BATCH_ENABLE == False
277                 orig_sce = bpy.data.scenes.active
278                 
279                 
280                 new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
281                 for data in data_seq: # scene or group
282                         newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name)
283                         
284                         
285                         if BATCH_OWN_DIR:
286                                 new_fbxpath = fbxpath + newname + Blender.sys.sep
287                                 # path may alredy exist
288                                 # TODO - might exist but be a file. unlikely but should probably account for it.
289                                 
290                                 if Blender.sys.exists(new_fbxpath) == 0:
291                                         os.mkdir(new_fbxpath)
292                                 
293                         
294                         filename = new_fbxpath + newname + '.fbx'
295                         
296                         print '\nBatch exporting %s as...\n\t"%s"' % (data, filename)
297                         
298                         if BATCH_GROUP: #group
299                                 # group, so objects update properly, add a dummy scene.
300                                 sce = bpy.data.scenes.new()
301                                 sce.Layers = (1<<20) -1
302                                 bpy.data.scenes.active = sce
303                                 for ob_base in data.objects:
304                                         sce.objects.link(ob_base)
305                                 
306                                 sce.update(1)
307                                 
308                                 # TODO - BUMMER! Armatures not in the group wont animate the mesh
309                                 
310                         else:# scene
311                                 
312                                 
313                                 data_seq.active = data
314                         
315                         
316                         # Call self with modified args
317                         # Dont pass batch options since we alredy usedt them
318                         write(filename, data.objects,
319                                 False,
320                                 EXP_MESH,
321                                 EXP_MESH_APPLY_MOD,
322                                 EXP_MESH_HQ_NORMALS,
323                                 EXP_ARMATURE,
324                                 EXP_LAMP,
325                                 EXP_CAMERA,
326                                 EXP_EMPTY,
327                                 EXP_IMAGE_COPY,
328                                 GLOBAL_MATRIX,
329                                 ANIM_ENABLE,
330                                 ANIM_OPTIMIZE,
331                                 ANIM_OPTIMIZE_PRECISSION,
332                                 ANIM_ACTION_ALL
333                         )
334                         
335                         if BATCH_GROUP:
336                                 # remove temp group scene
337                                 bpy.data.scenes.unlink(sce)
338                 
339                 bpy.data.scenes.active = orig_sce
340                 
341                 return # so the script wont run after we have batch exported.
342         
343         # end batch support
344         
345         
346         # ----------------------------------------------
347         # storage classes
348         class my_bone_class:
349                 __slots__ =(\
350                   'blenName',\
351                   'blenBone',\
352                   'blenMeshes',\
353                   'restMatrix',\
354                   'parent',\
355                   'blenName',\
356                   'fbxName',\
357                   'fbxArm',\
358                   '__pose_bone',\
359                   '__anim_poselist')
360                 
361                 def __init__(self, blenBone, fbxArm):
362                         
363                         # This is so 2 armatures dont have naming conflicts since FBX bones use object namespace
364                         self.fbxName = sane_obname(blenBone)
365                         
366                         self.blenName =                 blenBone.name
367                         self.blenBone =                 blenBone
368                         self.blenMeshes =               {}                                      # fbxMeshObName : mesh
369                         self.fbxArm =                   fbxArm
370                         self.restMatrix =               blenBone.matrix['ARMATURESPACE']
371                         
372                         # not used yet
373                         # self.restMatrixInv =  self.restMatrix.copy().invert()
374                         # self.restMatrixLocal =        None # set later, need parent matrix
375                         
376                         self.parent =                   None
377                         
378                         # not public
379                         pose = fbxArm.blenObject.getPose()
380                         self.__pose_bone =              pose.bones[self.blenName]
381                         
382                         # store a list if matricies here, (poseMatrix, head, tail)
383                         # {frame:posematrix, frame:posematrix, ...}
384                         self.__anim_poselist = {}
385                 
386                 '''
387                 def calcRestMatrixLocal(self):
388                         if self.parent:
389                                 self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.copy().invert()
390                         else:
391                                 self.restMatrixLocal = self.restMatrix.copy()
392                 '''
393                 def setPoseFrame(self, f):
394                         # cache pose info here, frame must be set beforehand
395                         
396                         # Didnt end up needing head or tail, if we do - here it is.
397                         '''
398                         self.__anim_poselist[f] = (\
399                                 self.__pose_bone.poseMatrix.copy(),\
400                                 self.__pose_bone.head.copy(),\
401                                 self.__pose_bone.tail.copy() )
402                         '''
403                         
404                         self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy()
405                 
406                 # get pose from frame.
407                 def getPoseMatrix(self, f):# ----------------------------------------------
408                         return self.__anim_poselist[f]
409                 '''
410                 def getPoseHead(self, f):
411                         #return self.__pose_bone.head.copy()
412                         return self.__anim_poselist[f][1].copy()
413                 def getPoseTail(self, f):
414                         #return self.__pose_bone.tail.copy()
415                         return self.__anim_poselist[f][2].copy()
416                 '''
417                 # end
418                 
419                 def getAnimParRelMatrix(self, frame):
420                         #arm_mat = self.fbxArm.matrixWorld
421                         #arm_mat = self.fbxArm.parRelMatrix()
422                         if not self.parent:
423                                 #return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat) # dont apply arm matrix anymore
424                                 return mtx4_z90 * self.getPoseMatrix(frame)
425                         else:
426                                 #return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat)))  *  (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert()
427                                 return (mtx4_z90 * (self.getPoseMatrix(frame)))  *  (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert()
428                 
429                 # we need thes because cameras and lights modified rotations
430                 def getAnimParRelMatrixRot(self, frame):
431                         return self.getAnimParRelMatrix(frame)
432                 
433                 def flushAnimData(self):
434                         self.__anim_poselist.clear()
435
436
437         class my_object_generic:
438                 # Other settings can be applied for each type - mesh, armature etc.
439                 def __init__(self, ob, matrixWorld = None):
440                         self.fbxName = sane_obname(ob)
441                         self.blenObject = ob
442                         self.fbxGroupNames = []
443                         self.fbxParent = None # set later on IF the parent is in the selection.
444                         if matrixWorld:         self.matrixWorld = matrixWorld * GLOBAL_MATRIX
445                         else:                           self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX
446                         self.__anim_poselist = {} # we should only access this
447                 
448                 def parRelMatrix(self):
449                         if self.fbxParent:
450                                 return self.matrixWorld * self.fbxParent.matrixWorld.copy().invert()
451                         else:
452                                 return self.matrixWorld
453                 
454                 def setPoseFrame(self, f):
455                         self.__anim_poselist[f] =  self.blenObject.matrixWorld.copy()
456                 
457                 def getAnimParRelMatrix(self, frame):
458                         if self.fbxParent:
459                                 #return (self.__anim_poselist[frame] * self.fbxParent.__anim_poselist[frame].copy().invert() ) * GLOBAL_MATRIX
460                                 return (self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert()
461                         else:
462                                 return self.__anim_poselist[frame] * GLOBAL_MATRIX
463                 
464                 def getAnimParRelMatrixRot(self, frame):
465                         type = self.blenObject.type
466                         if self.fbxParent:
467                                 matrix_rot = (((self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert())).rotationPart()
468                         else:
469                                 matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart()
470                         
471                         # Lamps need to be rotated
472                         if type =='Lamp':
473                                 matrix_rot = mtx_x90 * matrix_rot
474                         elif ob and type =='Camera':
475                                 y = Vector(0,1,0) * matrix_rot
476                                 matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y)
477                         
478                         return matrix_rot
479                         
480         # ----------------------------------------------
481         
482         
483         
484         
485         
486         print '\nFBX export starting...', filename
487         start_time = Blender.sys.time()
488         try:
489                 file = open(filename, 'w')
490         except:
491                 return False
492         
493         sce = bpy.data.scenes.active
494         world = sce.world
495         
496         
497         # ---------------------------- Write the header first
498         file.write(header_comment)
499         if time:
500                 curtime = time.localtime()[0:6]
501         else:
502                 curtime = [0,0,0,0,0,0]
503         # 
504         file.write(\
505 '''FBXHeaderExtension:  {
506         FBXHeaderVersion: 1003
507         FBXVersion: 6100
508         CreationTimeStamp:  {
509                 Version: 1000
510                 Year: %.4i
511                 Month: %.2i
512                 Day: %.2i
513                 Hour: %.2i
514                 Minute: %.2i
515                 Second: %.2i
516                 Millisecond: 0
517         }
518         Creator: "FBX SDK/FBX Plugins build 20070228"
519         OtherFlags:  {
520                 FlagPLE: 0
521         }
522 }''' % (curtime))
523         
524         file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
525         file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version'))
526         
527         pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way
528         
529         # --------------- funcs for exporting
530         def object_tx(ob, loc, matrix, matrix_mod = None):
531                 '''
532                 Matrix mod is so armature objects can modify their bone matricies
533                 '''
534                 if isinstance(ob, Blender.Types.BoneType):
535                         
536                         # we know we have a matrix
537                         # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod)
538                         matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore
539                         
540                         parent = ob.parent
541                         if parent:
542                                 #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod)
543                                 par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore
544                                 matrix = matrix * par_matrix.copy().invert()
545                                 
546                         matrix_rot =    matrix.rotationPart()
547                         
548                         loc =                   tuple(matrix.translationPart())
549                         scale =                 tuple(matrix.scalePart())
550                         rot =                   tuple(matrix_rot.toEuler())
551                         
552                 else:
553                         # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore
554                         #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX
555                         if ob and not matrix: raise "error: this should never happen!"
556                         
557                         matrix_rot = matrix
558                         #if matrix:
559                         #       matrix = matrix_scale * matrix
560                         
561                         if matrix:
562                                 loc = tuple(matrix.translationPart())
563                                 scale = tuple(matrix.scalePart())
564                                 
565                                 matrix_rot = matrix.rotationPart()
566                                 # Lamps need to be rotated
567                                 if ob and ob.type =='Lamp':
568                                         matrix_rot = mtx_x90 * matrix_rot
569                                         rot = tuple(matrix_rot.toEuler())
570                                 elif ob and ob.type =='Camera':
571                                         y = Vector(0,1,0) * matrix_rot
572                                         matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y)
573                                         rot = tuple(matrix_rot.toEuler())
574                                 else:
575                                         rot = tuple(matrix_rot.toEuler())
576                         else:
577                                 if not loc:
578                                         loc = 0,0,0
579                                 scale = 1,1,1
580                                 rot = 0,0,0
581                 
582                 return loc, rot, scale, matrix, matrix_rot
583         
584         def write_object_tx(ob, loc, matrix, matrix_mod= None):
585                 '''
586                 We have loc to set the location if non blender objects that have a location
587                 
588                 matrix_mod is only used for bones at the moment
589                 '''
590                 loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod)
591                 
592                 file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc)
593                 file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot)
594                 file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale)
595                 return loc, rot, scale, matrix, matrix_rot
596         
597         def write_object_props(ob=None, loc=None, matrix=None, matrix_mod=None):
598                 # if the type is 0 its an empty otherwise its a mesh
599                 # only difference at the moment is one has a color
600                 file.write('''
601                 Properties60:  {
602                         Property: "QuaternionInterpolate", "bool", "",0
603                         Property: "Visibility", "Visibility", "A+",1''')
604                 
605                 loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix, matrix_mod)
606                 
607                 # Rotation order, note, for FBX files Iv loaded normal order is 1
608                 # setting to zero.
609                 # eEULER_XYZ = 0
610                 # eEULER_XZY
611                 # eEULER_YZX
612                 # eEULER_YXZ
613                 # eEULER_ZXY
614                 # eEULER_ZYX
615                 
616                 file.write('''
617                         Property: "RotationOffset", "Vector3D", "",0,0,0
618                         Property: "RotationPivot", "Vector3D", "",0,0,0
619                         Property: "ScalingOffset", "Vector3D", "",0,0,0
620                         Property: "ScalingPivot", "Vector3D", "",0,0,0
621                         Property: "TranslationActive", "bool", "",0
622                         Property: "TranslationMin", "Vector3D", "",0,0,0
623                         Property: "TranslationMax", "Vector3D", "",0,0,0
624                         Property: "TranslationMinX", "bool", "",0
625                         Property: "TranslationMinY", "bool", "",0
626                         Property: "TranslationMinZ", "bool", "",0
627                         Property: "TranslationMaxX", "bool", "",0
628                         Property: "TranslationMaxY", "bool", "",0
629                         Property: "TranslationMaxZ", "bool", "",0
630                         Property: "RotationOrder", "enum", "",0
631                         Property: "RotationSpaceForLimitOnly", "bool", "",0
632                         Property: "AxisLen", "double", "",10
633                         Property: "PreRotation", "Vector3D", "",0,0,0
634                         Property: "PostRotation", "Vector3D", "",0,0,0
635                         Property: "RotationActive", "bool", "",0
636                         Property: "RotationMin", "Vector3D", "",0,0,0
637                         Property: "RotationMax", "Vector3D", "",0,0,0
638                         Property: "RotationMinX", "bool", "",0
639                         Property: "RotationMinY", "bool", "",0
640                         Property: "RotationMinZ", "bool", "",0
641                         Property: "RotationMaxX", "bool", "",0
642                         Property: "RotationMaxY", "bool", "",0
643                         Property: "RotationMaxZ", "bool", "",0
644                         Property: "RotationStiffnessX", "double", "",0
645                         Property: "RotationStiffnessY", "double", "",0
646                         Property: "RotationStiffnessZ", "double", "",0
647                         Property: "MinDampRangeX", "double", "",0
648                         Property: "MinDampRangeY", "double", "",0
649                         Property: "MinDampRangeZ", "double", "",0
650                         Property: "MaxDampRangeX", "double", "",0
651                         Property: "MaxDampRangeY", "double", "",0
652                         Property: "MaxDampRangeZ", "double", "",0
653                         Property: "MinDampStrengthX", "double", "",0
654                         Property: "MinDampStrengthY", "double", "",0
655                         Property: "MinDampStrengthZ", "double", "",0
656                         Property: "MaxDampStrengthX", "double", "",0
657                         Property: "MaxDampStrengthY", "double", "",0
658                         Property: "MaxDampStrengthZ", "double", "",0
659                         Property: "PreferedAngleX", "double", "",0
660                         Property: "PreferedAngleY", "double", "",0
661                         Property: "PreferedAngleZ", "double", "",0
662                         Property: "InheritType", "enum", "",0
663                         Property: "ScalingActive", "bool", "",0
664                         Property: "ScalingMin", "Vector3D", "",1,1,1
665                         Property: "ScalingMax", "Vector3D", "",1,1,1
666                         Property: "ScalingMinX", "bool", "",0
667                         Property: "ScalingMinY", "bool", "",0
668                         Property: "ScalingMinZ", "bool", "",0
669                         Property: "ScalingMaxX", "bool", "",0
670                         Property: "ScalingMaxY", "bool", "",0
671                         Property: "ScalingMaxZ", "bool", "",0
672                         Property: "GeometricTranslation", "Vector3D", "",0,0,0
673                         Property: "GeometricRotation", "Vector3D", "",0,0,0
674                         Property: "GeometricScaling", "Vector3D", "",1,1,1
675                         Property: "LookAtProperty", "object", ""
676                         Property: "UpVectorProperty", "object", ""
677                         Property: "Show", "bool", "",1
678                         Property: "NegativePercentShapeSupport", "bool", "",1
679                         Property: "DefaultAttributeIndex", "int", "",0''')
680                 if ob and type(ob) != Blender.Types.BoneType:
681                         # Only mesh objects have color 
682                         file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
683                         file.write('\n\t\t\tProperty: "Size", "double", "",100')
684                         file.write('\n\t\t\tProperty: "Look", "enum", "",1')
685                 
686                 return loc, rot, scale, matrix, matrix_rot
687         
688         
689         # -------------------------------------------- Armatures
690         #def write_bone(bone, name, matrix_mod):
691         def write_bone(my_bone):
692                 file.write('\n\tModel: "Model::%s", "Limb" {' % my_bone.fbxName)
693                 file.write('\n\t\tVersion: 232')
694                 
695                 #poseMatrix = write_object_props(my_bone.blenBone, None, None, my_bone.fbxArm.parRelMatrix())[3]
696                 poseMatrix = write_object_props(my_bone.blenBone)[3] # dont apply bone matricies anymore
697                 pose_items.append( (my_bone.fbxName, poseMatrix) )
698                 
699                 
700                 # file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((my_bone.blenData.head['ARMATURESPACE'] - my_bone.blenData.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length)
701                 file.write('\n\t\t\tProperty: "Size", "double", "",1')
702                 
703                 #((my_bone.blenData.head['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld) - (my_bone.blenData.tail['ARMATURESPACE'] * my_bone.fbxArm.parRelMatrix())).length)
704                 
705                 """
706                 file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\
707                         ((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length)
708                 """
709                 
710                 file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\
711                         (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length)
712                 
713                 #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1')
714                 file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8')
715                 file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
716                 file.write('\n\t\t}')
717                 file.write('\n\t\tMultiLayer: 0')
718                 file.write('\n\t\tMultiTake: 1')
719                 file.write('\n\t\tShading: Y')
720                 file.write('\n\t\tCulling: "CullingOff"')
721                 file.write('\n\t\tTypeFlags: "Skeleton"')
722                 file.write('\n\t}')
723         
724         def write_camera_switch():
725                 file.write('''
726         Model: "Model::Camera Switcher", "CameraSwitcher" {
727                 Version: 232''')
728                 
729                 write_object_props()
730                 file.write('''
731                         Property: "Color", "Color", "A",0.8,0.8,0.8
732                         Property: "Camera Index", "Integer", "A+",100
733                 }
734                 MultiLayer: 0
735                 MultiTake: 1
736                 Hidden: "True"
737                 Shading: W
738                 Culling: "CullingOff"
739                 Version: 101
740                 Name: "Model::Camera Switcher"
741                 CameraId: 0
742                 CameraName: 100
743                 CameraIndexName: 
744         }''')
745         
746         def write_camera_dummy(name, loc, near, far, proj_type, up):
747                 file.write('\n\tModel: "Model::%s", "Camera" {' % name )
748                 file.write('\n\t\tVersion: 232')
749                 write_object_props(None, loc)
750                 
751                 file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
752                 file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0')
753                 file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",40')
754                 file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1')
755                 file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1')
756                 file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",0')
757                 file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",0')
758                 file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0.63,0.63,0.63')
759                 file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0')
760                 file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1')
761                 file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1')
762                 file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0')
763                 file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1')
764                 file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0')
765                 file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2')
766                 file.write('\n\t\t\tProperty: "GateFit", "enum", "",0')
767                 file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",21.3544940948486')
768                 file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0')
769                 file.write('\n\t\t\tProperty: "AspectW", "double", "",320')
770                 file.write('\n\t\t\tProperty: "AspectH", "double", "",200')
771                 file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",1')
772                 file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0')
773                 file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3')
774                 file.write('\n\t\t\tProperty: "ShowName", "bool", "",1')
775                 file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1')
776                 file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0')
777                 file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1')
778                 file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0')
779                 file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % near)
780                 file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % far)
781                 file.write('\n\t\t\tProperty: "FilmWidth", "double", "",0.816')
782                 file.write('\n\t\t\tProperty: "FilmHeight", "double", "",0.612')
783                 file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",1.33333333333333')
784                 file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1')
785                 file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",4')
786                 file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1')
787                 file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0')
788                 file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2')
789                 file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100')
790                 file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0')
791                 file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1')
792                 file.write('\n\t\t\tProperty: "LockMode", "bool", "",0')
793                 file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0')
794                 file.write('\n\t\t\tProperty: "FitImage", "bool", "",0')
795                 file.write('\n\t\t\tProperty: "Crop", "bool", "",0')
796                 file.write('\n\t\t\tProperty: "Center", "bool", "",1')
797                 file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1')
798                 file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0')
799                 file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5')
800                 file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1')
801                 file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0')
802                 file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1')
803                 file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",1.33333333333333')
804                 file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0')
805                 file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100')
806                 file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50')
807                 file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50')
808                 file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",%i' % proj_type)
809                 file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0')
810                 file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0')
811                 file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0')
812                 file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5')
813                 file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200')
814                 file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0')
815                 file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777')
816                 file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
817                 file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
818                 file.write('\n\t\t}')
819                 file.write('\n\t\tMultiLayer: 0')
820                 file.write('\n\t\tMultiTake: 0')
821                 file.write('\n\t\tHidden: "True"')
822                 file.write('\n\t\tShading: Y')
823                 file.write('\n\t\tCulling: "CullingOff"')
824                 file.write('\n\t\tTypeFlags: "Camera"')
825                 file.write('\n\t\tGeometryVersion: 124')
826                 file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
827                 file.write('\n\t\tUp: %i,%i,%i' % up)
828                 file.write('\n\t\tLookAt: 0,0,0')
829                 file.write('\n\t\tShowInfoOnMoving: 1')
830                 file.write('\n\t\tShowAudio: 0')
831                 file.write('\n\t\tAudioColor: 0,1,0')
832                 file.write('\n\t\tCameraOrthoZoom: 1')
833                 file.write('\n\t}')
834         
835         def write_camera_default():
836                 # This sucks but to match FBX converter its easier to
837                 # write the cameras though they are not needed.
838                 write_camera_dummy('Producer Perspective',      (0,71.3,287.5), 10, 4000, 0, (0,1,0))
839                 write_camera_dummy('Producer Top',                      (0,4000,0), 1, 30000, 1, (0,0,-1))
840                 write_camera_dummy('Producer Bottom',                   (0,-4000,0), 1, 30000, 1, (0,0,-1))
841                 write_camera_dummy('Producer Front',                    (0,0,4000), 1, 30000, 1, (0,1,0))
842                 write_camera_dummy('Producer Back',                     (0,0,-4000), 1, 30000, 1, (0,1,0))
843                 write_camera_dummy('Producer Right',                    (4000,0,0), 1, 30000, 1, (0,1,0))
844                 write_camera_dummy('Producer Left',                     (-4000,0,0), 1, 30000, 1, (0,1,0))
845         
846         def write_camera(my_cam):
847                 '''
848                 Write a blender camera
849                 '''
850                 render = sce.render
851                 width   = render.sizeX
852                 height  = render.sizeY
853                 aspect  = float(width)/height
854                 
855                 data = my_cam.blenObject.data
856                 
857                 file.write('\n\tModel: "Model::%s", "Camera" {' % my_cam.fbxName )
858                 file.write('\n\t\tVersion: 232')
859                 loc, rot, scale, matrix, matrix_rot = write_object_props(my_cam.blenObject, None, my_cam.parRelMatrix())
860                 
861                 file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0')
862                 file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",%.6f' % data.angle)
863                 file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1')
864                 file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1')
865                 file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",14.0323972702026')
866                 file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units?
867                 file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto 
868                 file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0')
869                 file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0')
870                 file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1')
871                 file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1')
872                 file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0')
873                 file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1')
874                 file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0')
875                 file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2')
876                 file.write('\n\t\t\tProperty: "GateFit", "enum", "",0')
877                 file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0')
878                 file.write('\n\t\t\tProperty: "AspectW", "double", "",%i' % width)
879                 file.write('\n\t\t\tProperty: "AspectH", "double", "",%i' % height)
880                 
881                 '''Camera aspect ratio modes.
882                         0 If the ratio mode is eWINDOW_SIZE, both width and height values aren't relevant.
883                         1 If the ratio mode is eFIXED_RATIO, the height value is set to 1.0 and the width value is relative to the height value.
884                         2 If the ratio mode is eFIXED_RESOLUTION, both width and height values are in pixels.
885                         3 If the ratio mode is eFIXED_WIDTH, the width value is in pixels and the height value is relative to the width value.
886                         4 If the ratio mode is eFIXED_HEIGHT, the height value is in pixels and the width value is relative to the height value. 
887                 
888                 Definition at line 234 of file kfbxcamera.h. '''
889                 
890                 file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",2')
891                 
892                 file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0')
893                 file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3')
894                 file.write('\n\t\t\tProperty: "ShowName", "bool", "",1')
895                 file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1')
896                 file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0')
897                 file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1')
898                 file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0')
899                 file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart)
900                 file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart)
901                 file.write('\n\t\t\tProperty: "FilmWidth", "double", "",1.0')
902                 file.write('\n\t\t\tProperty: "FilmHeight", "double", "",1.0')
903                 file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % aspect)
904                 file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1')
905                 file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",0')
906                 file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1')
907                 file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0')
908                 file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2')
909                 file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100')
910                 file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0')
911                 file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1')
912                 file.write('\n\t\t\tProperty: "LockMode", "bool", "",0')
913                 file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0')
914                 file.write('\n\t\t\tProperty: "FitImage", "bool", "",0')
915                 file.write('\n\t\t\tProperty: "Crop", "bool", "",0')
916                 file.write('\n\t\t\tProperty: "Center", "bool", "",1')
917                 file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1')
918                 file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0')
919                 file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5')
920                 file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1')
921                 file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0')
922                 file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1')
923                 file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",%.6f' % aspect)
924                 file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0')
925                 file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100')
926                 file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50')
927                 file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50')
928                 file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",0')
929                 file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0')
930                 file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0')
931                 file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0')
932                 file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5')
933                 file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200')
934                 file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0')
935                 file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777')
936                 file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
937                 file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
938                 
939                 file.write('\n\t\t}')
940                 file.write('\n\t\tMultiLayer: 0')
941                 file.write('\n\t\tMultiTake: 0')
942                 file.write('\n\t\tShading: Y')
943                 file.write('\n\t\tCulling: "CullingOff"')
944                 file.write('\n\t\tTypeFlags: "Camera"')
945                 file.write('\n\t\tGeometryVersion: 124')
946                 file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
947                 file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) )
948                 file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) )
949                 
950                 #file.write('\n\t\tUp: 0,0,0' )
951                 #file.write('\n\t\tLookAt: 0,0,0' )
952                 
953                 file.write('\n\t\tShowInfoOnMoving: 1')
954                 file.write('\n\t\tShowAudio: 0')
955                 file.write('\n\t\tAudioColor: 0,1,0')
956                 file.write('\n\t\tCameraOrthoZoom: 1')
957                 file.write('\n\t}')
958         
959         def write_light(my_light):
960                 light = my_light.blenObject.data
961                 file.write('\n\tModel: "Model::%s", "Light" {' % my_light.fbxName)
962                 file.write('\n\t\tVersion: 232')
963                 
964                 write_object_props(my_light.blenObject, None, my_light.parRelMatrix())
965                 
966                 # Why are these values here twice?????? - oh well, follow the holy sdk's output
967                 
968                 # Blender light types match FBX's, funny coincidence, we just need to
969                 # be sure that all unsupported types are made into a point light
970                 #ePOINT, 
971                 #eDIRECTIONAL
972                 #eSPOT
973                 light_type = light.type
974                 if light_type > 3: light_type = 0
975                 
976                 mode = light.mode
977                 if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows:
978                         do_shadow = 1
979                 else:
980                         do_shadow = 0
981                 
982                 if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular):
983                         do_light = 0
984                 else:
985                         do_light = 1
986                 
987                 scale = abs(GLOBAL_MATRIX.scalePart()[0]) # scale is always uniform in this case
988                 
989                 file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
990                 file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1')
991                 file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
992                 file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
993                 file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
994                 file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
995                 file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1')
996                 file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200
997                 file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale))
998                 file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
999                 file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col))
1000                 file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200
1001                 file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale))
1002                 file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
1003                 file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
1004                 file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light)
1005                 file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
1006                 file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
1007                 file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
1008                 file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
1009                 file.write('\n\t\t\tProperty: "DecayType", "enum", "",0')
1010                 file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist)
1011                 file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0')
1012                 file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0')
1013                 file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0')
1014                 file.write('\n\t\t\tProperty: "EnableFarAttenuation", "bool", "",0')
1015                 file.write('\n\t\t\tProperty: "FarAttenuationStart", "double", "",0')
1016                 file.write('\n\t\t\tProperty: "FarAttenuationEnd", "double", "",0')
1017                 file.write('\n\t\t\tProperty: "CastShadows", "bool", "",%i' % do_shadow)
1018                 file.write('\n\t\t\tProperty: "ShadowColor", "ColorRGBA", "",0,0,0,1')
1019                 file.write('\n\t\t}')
1020                 file.write('\n\t\tMultiLayer: 0')
1021                 file.write('\n\t\tMultiTake: 0')
1022                 file.write('\n\t\tShading: Y')
1023                 file.write('\n\t\tCulling: "CullingOff"')
1024                 file.write('\n\t\tTypeFlags: "Light"')
1025                 file.write('\n\t\tGeometryVersion: 124')
1026                 file.write('\n\t}')
1027         
1028         # matrixOnly is not used at the moment
1029         def write_null(my_null = None, fbxName = None, matrixOnly = None):
1030                 # ob can be null
1031                 if not fbxName: fbxName = my_null.fbxName
1032                 
1033                 file.write('\n\tModel: "Model::%s", "Null" {' % fbxName)
1034                 file.write('\n\t\tVersion: 232')
1035                 
1036                 # only use this for the root matrix at the moment
1037                 if matrixOnly:
1038                         poseMatrix = write_object_props(None, None, matrixOnly)[3]
1039                 
1040                 else: # all other Null's
1041                         if my_null:             poseMatrix = write_object_props(my_null.blenObject, None, my_null.parRelMatrix())[3]
1042                         else:                   poseMatrix = write_object_props()[3]
1043                 
1044                 pose_items.append((fbxName, poseMatrix))
1045                 
1046                 file.write('''
1047                 }
1048                 MultiLayer: 0
1049                 MultiTake: 1
1050                 Shading: Y
1051                 Culling: "CullingOff"
1052                 TypeFlags: "Null"
1053         }''')
1054         
1055         # Material Settings
1056         if world:       world_amb = world.getAmb()
1057         else:           world_amb = (0,0,0) # Default value
1058         
1059         def write_material(matname, mat):
1060                 file.write('\n\tMaterial: "Material::%s", "" {' % matname)
1061                 
1062                 # Todo, add more material Properties.
1063                 if mat:
1064                         mat_cold = tuple(mat.rgbCol)
1065                         mat_cols = tuple(mat.specCol)
1066                         #mat_colm = tuple(mat.mirCol) # we wont use the mirror color
1067                         mat_colamb = tuple([c for c in world_amb])
1068                         
1069                         mat_dif = mat.ref
1070                         mat_amb = mat.amb
1071                         mat_hard = (float(mat.hard)-1)/5.10
1072                         mat_spec = mat.spec/2.0
1073                         mat_alpha = mat.alpha
1074                         mat_emit = mat.emit
1075                         mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS
1076                         if mat_shadeless:
1077                                 mat_shader = 'Lambert'
1078                         else:
1079                                 if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT:
1080                                         mat_shader = 'Lambert'
1081                                 else:
1082                                         mat_shader = 'Phong'
1083                 else:
1084                         mat_cols = mat_cold = 0.8, 0.8, 0.8
1085                         mat_colamb = 0.0,0.0,0.0
1086                         # mat_colm 
1087                         mat_dif = 1.0
1088                         mat_amb = 0.5
1089                         mat_hard = 20.0
1090                         mat_spec = 0.2
1091                         mat_alpha = 1.0
1092                         mat_emit = 0.0
1093                         mat_shadeless = False
1094                         mat_shader = 'Phong'
1095                 
1096                 file.write('\n\t\tVersion: 102')
1097                 file.write('\n\t\tShadingModel: "%s"' % mat_shader.lower())
1098                 file.write('\n\t\tMultiLayer: 0')
1099                 
1100                 file.write('\n\t\tProperties60:  {')
1101                 file.write('\n\t\t\tProperty: "ShadingModel", "KString", "", "%s"' % mat_shader)
1102                 file.write('\n\t\t\tProperty: "MultiLayer", "bool", "",0')
1103                 file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold) # emit and diffuse color are he same in blender
1104                 file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",%.4f' % mat_emit)
1105                 
1106                 file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_colamb)
1107                 file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.4f' % mat_amb)
1108                 file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold)
1109                 file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.4f' % mat_dif)
1110                 file.write('\n\t\t\tProperty: "Bump", "Vector3D", "",0,0,0')
1111                 file.write('\n\t\t\tProperty: "TransparentColor", "ColorRGB", "",1,1,1')
1112                 file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",%.4f' % (1.0 - mat_alpha))
1113                 if not mat_shadeless:
1114                         file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cols)
1115                         file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.4f' % mat_spec)
1116                         file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0')
1117                         file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0')
1118                         file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1')
1119                 file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0')
1120                 file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb)
1121                 file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold)
1122                 if not mat_shadeless:
1123                         file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols)
1124                         file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard)
1125                 file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha)
1126                 if not mat_shadeless:
1127                         file.write('\n\t\t\tProperty: "Reflectivity", "double", "",0')
1128
1129                 file.write('\n\t\t}')
1130                 file.write('\n\t}')
1131         
1132         def write_video(texname, tex):
1133                 # Same as texture really!
1134                 file.write('\n\tVideo: "Video::%s", "Clip" {' % texname)
1135                 
1136                 file.write('''
1137                 Type: "Clip"
1138                 Properties60:  {
1139                         Property: "FrameRate", "double", "",0
1140                         Property: "LastFrame", "int", "",0
1141                         Property: "Width", "int", "",0
1142                         Property: "Height", "int", "",0''')
1143                 if tex:
1144                         fname = tex.filename
1145                         fname_strip = strip_path(fname)
1146                 else:
1147                         fname = fname_strip = ''
1148                 
1149                 file.write('\n\t\t\tProperty: "Path", "charptr", "", "%s"' % fname_strip)
1150                 
1151                 
1152                 file.write('''
1153                         Property: "StartFrame", "int", "",0
1154                         Property: "StopFrame", "int", "",0
1155                         Property: "PlaySpeed", "double", "",1
1156                         Property: "Offset", "KTime", "",0
1157                         Property: "InterlaceMode", "enum", "",0
1158                         Property: "FreeRunning", "bool", "",0
1159                         Property: "Loop", "bool", "",0
1160                         Property: "AccessMode", "enum", "",0
1161                 }
1162                 UseMipMap: 0''')
1163                 
1164                 file.write('\n\t\tFilename: "%s"' % fname_strip)
1165                 if fname_strip: fname_strip = '/' + fname_strip
1166                 file.write('\n\t\tRelativeFilename: "fbx%s"' % fname_strip) # make relative
1167                 file.write('\n\t}')
1168
1169         
1170         def write_texture(texname, tex, num):
1171                 # if tex == None then this is a dummy tex
1172                 file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {' % texname)
1173                 file.write('\n\t\tType: "TextureVideoClip"')
1174                 file.write('\n\t\tVersion: 202')
1175                 # TODO, rare case _empty_ exists as a name.
1176                 file.write('\n\t\tTextureName: "Texture::%s"' % texname)
1177                 
1178                 file.write('''
1179                 Properties60:  {
1180                         Property: "Translation", "Vector", "A+",0,0,0
1181                         Property: "Rotation", "Vector", "A+",0,0,0
1182                         Property: "Scaling", "Vector", "A+",1,1,1''')
1183                 file.write('\n\t\t\tProperty: "Texture alpha", "Number", "A+",%i' % num)
1184                 
1185                 
1186                 # WrapModeU/V 0==rep, 1==clamp, TODO add support
1187                 file.write('''
1188                         Property: "TextureTypeUse", "enum", "",0
1189                         Property: "CurrentTextureBlendMode", "enum", "",1
1190                         Property: "UseMaterial", "bool", "",0
1191                         Property: "UseMipMap", "bool", "",0
1192                         Property: "CurrentMappingType", "enum", "",0
1193                         Property: "UVSwap", "bool", "",0''')
1194                 
1195                 file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX)
1196                 file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY)
1197                 
1198                 file.write('''
1199                         Property: "TextureRotationPivot", "Vector3D", "",0,0,0
1200                         Property: "TextureScalingPivot", "Vector3D", "",0,0,0
1201                         Property: "VideoProperty", "object", ""
1202                 }''')
1203                 
1204                 file.write('\n\t\tMedia: "Video::%s"' % texname)
1205                 if tex:
1206                         fname = tex.filename
1207                         file.write('\n\t\tFileName: "%s"' % strip_path(fname))
1208                         file.write('\n\t\tRelativeFilename: "fbx/%s"' % strip_path(fname)) # need some make relative command
1209                 else:
1210                         file.write('\n\t\tFileName: ""')
1211                         file.write('\n\t\tRelativeFilename: "fbx"')
1212                 
1213                 file.write('''
1214                 ModelUVTranslation: 0,0
1215                 ModelUVScaling: 1,1
1216                 Texture_Alpha_Source: "None"
1217                 Cropping: 0,0,0,0
1218         }''')
1219
1220         def write_deformer_skin(obname):
1221                 '''
1222                 Each mesh has its own deformer
1223                 '''
1224                 file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname)
1225                 file.write('''
1226                 Version: 100
1227                 MultiLayer: 0
1228                 Type: "Skin"
1229                 Properties60:  {
1230                 }
1231                 Link_DeformAcuracy: 50
1232         }''')
1233         
1234         # in the example was 'Bip01 L Thigh_2'
1235         def write_sub_deformer_skin(my_mesh, my_bone, weights):
1236         
1237                 '''
1238                 Each subdeformer is spesific to a mesh, but the bone it links to can be used by many sub-deformers
1239                 So the SubDeformer needs the mesh-object name as a prefix to make it unique
1240                 
1241                 Its possible that there is no matching vgroup in this mesh, in that case no verts are in the subdeformer,
1242                 a but silly but dosnt really matter
1243                 '''
1244                 file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {' % (my_mesh.fbxName, my_bone.fbxName))
1245                 
1246                 file.write('''
1247                 Version: 100
1248                 MultiLayer: 0
1249                 Type: "Cluster"
1250                 Properties60:  {
1251                         Property: "SrcModel", "object", ""
1252                         Property: "SrcModelReference", "object", ""
1253                 }
1254                 UserData: "", ""''')
1255                 
1256                 # Support for bone parents
1257                 if my_mesh.fbxBoneParent:
1258                         if my_mesh.fbxBoneParent == my_bone:
1259                                 # TODO - this is a bit lazy, we could have a simple write loop
1260                                 # for this case because all weights are 1.0 but for now this is ok
1261                                 # Parent Bones arent used all that much anyway.
1262                                 vgroup_data = [(j, 1.0) for j in xrange(len(my_mesh.blenData.verts))]
1263                         else:
1264                                 # This bone is not a parent of this mesh object, no weights
1265                                 vgroup_data = []
1266                         
1267                 else:
1268                         # Normal weight painted mesh
1269                         if my_bone.blenName in weights[0]:
1270                                 # Before we used normalized wright list
1271                                 #vgroup_data = me.getVertsFromGroup(bone.name, 1)
1272                                 group_index = weights[0].index(my_bone.blenName)
1273                                 vgroup_data = [(j, weight[group_index]) for j, weight in enumerate(weights[1]) if weight[group_index]] 
1274                         else:
1275                                 vgroup_data = []
1276                 
1277                 file.write('\n\t\tIndexes: ')
1278                 
1279                 i = -1
1280                 for vg in vgroup_data:
1281                         if i == -1:
1282                                 file.write('%i'  % vg[0])
1283                                 i=0
1284                         else:
1285                                 if i==23:
1286                                         file.write('\n\t\t')
1287                                         i=0
1288                                 file.write(',%i' % vg[0])
1289                         i+=1
1290                 
1291                 file.write('\n\t\tWeights: ')
1292                 i = -1
1293                 for vg in vgroup_data:
1294                         if i == -1:
1295                                 file.write('%.8f'  % vg[1])
1296                                 i=0
1297                         else:
1298                                 if i==38:
1299                                         file.write('\n\t\t')
1300                                         i=0
1301                                 file.write(',%.8f' % vg[1])
1302                         i+=1
1303                 
1304                 if my_mesh.fbxParent:
1305                         # TODO FIXME, this case is broken in some cases. skinned meshes just shouldnt have parents where possible!
1306                         m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() )
1307                 else:
1308                         # Yes! this is it...  - but dosnt work when the mesh is a.
1309                         m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() )
1310                 
1311                 #m = mtx4_z90 * my_bone.restMatrix
1312                 matstr = mat4x4str(m)
1313                 matstr_i = mat4x4str(m.invert())
1314                 
1315                 file.write('\n\t\tTransform: %s' % matstr_i) # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/
1316                 file.write('\n\t\tTransformLink: %s' % matstr)
1317                 file.write('\n\t}')
1318         
1319         def write_mesh(my_mesh):
1320                 
1321                 me = my_mesh.blenData
1322                 
1323                 # if there are non NULL materials on this mesh
1324                 if [mat for mat in my_mesh.blenMaterials if mat]:       do_materials = True
1325                 else:                                                                                           do_materials = False
1326                 
1327                 if my_mesh.blenTextures:        do_textures = True
1328                 else:                                           do_textures = False                     
1329                 
1330                 
1331                 file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName)
1332                 file.write('\n\t\tVersion: 232') # newline is added in write_object_props
1333                 
1334                 write_object_props(my_mesh.blenObject, None, my_mesh.parRelMatrix())
1335                 
1336                 file.write('\n\t\t}')
1337                 file.write('\n\t\tMultiLayer: 0')
1338                 file.write('\n\t\tMultiTake: 1')
1339                 file.write('\n\t\tShading: Y')
1340                 file.write('\n\t\tCulling: "CullingOff"')
1341                 
1342                 
1343                 # Write the Real Mesh data here
1344                 file.write('\n\t\tVertices: ')
1345                 i=-1
1346                 
1347                 for v in me.verts:
1348                         if i==-1:
1349                                 file.write('%.6f,%.6f,%.6f' % tuple(v.co));     i=0
1350                         else:
1351                                 if i==7:
1352                                         file.write('\n\t\t');   i=0
1353                                 file.write(',%.6f,%.6f,%.6f'% tuple(v.co))
1354                         i+=1
1355                                 
1356                 file.write('\n\t\tPolygonVertexIndex: ')
1357                 i=-1
1358                 for f in me.faces:
1359                         fi = [v.index for v in f]
1360                         # flip the last index, odd but it looks like
1361                         # this is how fbx tells one face from another
1362                         fi[-1] = -(fi[-1]+1)
1363                         fi = tuple(fi)
1364                         if i==-1:
1365                                 if len(f) == 3:         file.write('%i,%i,%i' % fi )
1366                                 else:                           file.write('%i,%i,%i,%i' % fi )
1367                                 i=0
1368                         else:
1369                                 if i==13:
1370                                         file.write('\n\t\t')
1371                                         i=0
1372                                 if len(f) == 3:         file.write(',%i,%i,%i' % fi )
1373                                 else:                           file.write(',%i,%i,%i,%i' % fi )
1374                         i+=1
1375                 
1376                 ed_val = [None, None]
1377                 LOOSE = Blender.Mesh.EdgeFlags.LOOSE
1378                 for ed in me.edges:
1379                         if ed.flag & LOOSE:
1380                                 ed_val[0] = ed.v1.index
1381                                 ed_val[1] = -(ed.v2.index+1)
1382                                 if i==-1:
1383                                         file.write('%i,%i' % tuple(ed_val) )
1384                                         i=0
1385                                 else:
1386                                         if i==13:
1387                                                 file.write('\n\t\t')
1388                                                 i=0
1389                                         file.write(',%i,%i' % tuple(ed_val) )
1390                                 i+=1
1391                 del LOOSE
1392                 
1393                 file.write('\n\t\tGeometryVersion: 124')
1394                 
1395                 file.write('''
1396                 LayerElementNormal: 0 {
1397                         Version: 101
1398                         Name: ""
1399                         MappingInformationType: "ByVertice"
1400                         ReferenceInformationType: "Direct"
1401                         Normals: ''')
1402                 
1403                 i=-1
1404                 for v in me.verts:
1405                         if i==-1:
1406                                 file.write('%.15f,%.15f,%.15f' % tuple(v.no));  i=0
1407                         else:
1408                                 if i==2:
1409                                         file.write('\n                   ');    i=0
1410                                 file.write(',%.15f,%.15f,%.15f' % tuple(v.no))
1411                         i+=1
1412                 file.write('\n\t\t}')
1413                 
1414                 # Write VertexColor Layers
1415                 # note, no programs seem to use this info :/
1416                 collayers = []
1417                 if me.vertexColors:
1418                         collayers = me.getColorLayerNames()
1419                         collayer_orig = me.activeColorLayer
1420                         for colindex, collayer in enumerate(collayers):
1421                                 me.activeColorLayer = collayer
1422                                 file.write('\n\t\tLayerElementColor: %i {' % colindex)
1423                                 file.write('\n\t\t\tVersion: 101')
1424                                 file.write('\n\t\t\tName: "%s"' % collayer)
1425                                 
1426                                 file.write('''
1427                         MappingInformationType: "ByPolygonVertex"
1428                         ReferenceInformationType: "IndexToDirect"
1429                         Colors: ''')
1430                         
1431                                 i = -1
1432                                 ii = 0 # Count how many Colors we write
1433                                 
1434                                 for f in me.faces:
1435                                         for col in f.col:
1436                                                 if i==-1:
1437                                                         file.write('%i,%i,%i' % (col[0], col[1], col[2]))
1438                                                         i=0
1439                                                 else:
1440                                                         if i==7:
1441                                                                 file.write('\n\t\t\t\t')
1442                                                                 i=0
1443                                                         file.write(',%i,%i,%i' % (col[0], col[1], col[2]))
1444                                                 i+=1
1445                                                 ii+=1 # One more Color
1446                                 
1447                                 file.write('\n\t\t\tColorIndex: ')
1448                                 i = -1
1449                                 for j in xrange(ii):
1450                                         if i == -1:
1451                                                 file.write('%i' % j)
1452                                                 i=0
1453                                         else:
1454                                                 if i==55:
1455                                                         file.write('\n\t\t\t\t')
1456                                                         i=0
1457                                                 file.write(',%i' % j)
1458                                         i+=1
1459                                 
1460                                 file.write('\n\t\t}')
1461                 
1462                 
1463                 
1464                 # Write UV and texture layers.
1465                 uvlayers = []
1466                 if me.faceUV:
1467                         uvlayers = me.getUVLayerNames()
1468                         uvlayer_orig = me.activeUVLayer
1469                         for uvindex, uvlayer in enumerate(uvlayers):
1470                                 me.activeUVLayer = uvlayer
1471                                 file.write('\n\t\tLayerElementUV: %i {' % uvindex)
1472                                 file.write('\n\t\t\tVersion: 101')
1473                                 file.write('\n\t\t\tName: "%s"' % uvlayer)
1474                                 
1475                                 file.write('''
1476                         MappingInformationType: "ByPolygonVertex"
1477                         ReferenceInformationType: "IndexToDirect"
1478                         UV: ''')
1479                         
1480                                 i = -1
1481                                 ii = 0 # Count how many UVs we write
1482                                 
1483                                 for f in me.faces:
1484                                         for uv in f.uv:
1485                                                 if i==-1:
1486                                                         file.write('%.6f,%.6f' % tuple(uv))
1487                                                         i=0
1488                                                 else:
1489                                                         if i==7:
1490                                                                 file.write('\n                   ')
1491                                                                 i=0
1492                                                         file.write(',%.6f,%.6f' % tuple(uv))
1493                                                 i+=1
1494                                                 ii+=1 # One more UV
1495                                 
1496                                 file.write('\n\t\t\tUVIndex: ')
1497                                 i = -1
1498                                 for j in xrange(ii):
1499                                         if i == -1:
1500                                                 file.write('%i'  % j)
1501                                                 i=0
1502                                         else:
1503                                                 if i==55:
1504                                                         file.write('\n\t\t\t\t')
1505                                                         i=0
1506                                                 file.write(',%i' % j)
1507                                         i+=1
1508                                 
1509                                 file.write('\n\t\t}')
1510                                 
1511                                 if do_textures:
1512                                         file.write('\n\t\tLayerElementTexture: %i {' % uvindex)
1513                                         file.write('\n\t\t\tVersion: 101')
1514                                         file.write('\n\t\t\tName: "%s"' % uvlayer)
1515                                         
1516                                         if len(my_mesh.blenTextures) == 1:
1517                                                 file.write('\n\t\t\tMappingInformationType: "AllSame"')
1518                                         else:
1519                                                 file.write('\n\t\t\tMappingInformationType: "ByPolygon"')
1520                                         
1521                                         file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"')
1522                                         file.write('\n\t\t\tBlendMode: "Translucent"')
1523                                         file.write('\n\t\t\tTextureAlpha: 1')
1524                                         file.write('\n\t\t\tTextureId: ')
1525                                         
1526                                         if len(my_mesh.blenTextures) == 1:
1527                                                 file.write('0')
1528                                         else:
1529                                                 #texture_mapping_local = {None:0}
1530                                                 texture_mapping_local = {None:-1}
1531                                                 
1532                                                 i = 0 # 1 for dummy
1533                                                 for tex in my_mesh.blenTextures:
1534                                                         texture_mapping_local[tex] = i
1535                                                         i+=1
1536                                                 
1537                                                 i=-1
1538                                                 for f in me.faces:
1539                                                         img_key = f.image
1540                                                         
1541                                                         if i==-1:
1542                                                                 i=0
1543                                                                 file.write( '%s' % texture_mapping_local[img_key])
1544                                                         else:
1545                                                                 if i==55:
1546                                                                         file.write('\n                   ')
1547                                                                         i=0
1548                                                                 
1549                                                                 file.write(',%s' % texture_mapping_local[img_key])
1550                                                         i+=1
1551                                 
1552                                 else:
1553                                         file.write('''
1554                 LayerElementTexture: 0 {
1555                         Version: 101
1556                         Name: ""
1557                         MappingInformationType: "NoMappingInformation"
1558                         ReferenceInformationType: "IndexToDirect"
1559                         BlendMode: "Translucent"
1560                         TextureAlpha: 1
1561                         TextureId: ''')
1562                                 file.write('\n\t\t}')
1563                         
1564                         me.activeUVLayer = uvlayer_orig
1565                         
1566                 # Done with UV/textures.
1567                 
1568                 if do_materials:
1569                         file.write('\n\t\tLayerElementMaterial: 0 {')
1570                         file.write('\n\t\t\tVersion: 101')
1571                         file.write('\n\t\t\tName: ""')
1572                         
1573                         if len(my_mesh.blenMaterials) == 1:
1574                                 file.write('\n\t\t\tMappingInformationType: "AllSame"')
1575                         else:
1576                                 file.write('\n\t\t\tMappingInformationType: "ByPolygon"')
1577                         
1578                         file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"')
1579                         file.write('\n\t\t\tMaterials: ')
1580                         
1581                         if len(my_mesh.blenMaterials) == 1:
1582                                 file.write('0')
1583                         else:
1584                                 # Build a material mapping for this 
1585                                 #material_mapping_local = [0] * 16 # local-index : global index.
1586                                 material_mapping_local = [-1] * 16 # local-index : global index.
1587                                 i= 0 # 1
1588                                 for j, mat in enumerate(my_mesh.blenMaterials):
1589                                         if mat:
1590                                                 material_mapping_local[j] = i
1591                                                 i+=1
1592                                         # else leave as -1
1593                                 
1594                                 len_material_mapping_local = len(material_mapping_local)
1595                                 
1596                                 i=-1
1597                                 for f in me.faces:
1598                                         f_mat = f.mat
1599                                         if f_mat >= len_material_mapping_local:
1600                                                 f_mat = 0
1601                                         
1602                                         if i==-1:
1603                                                 i=0
1604                                                 file.write( '%s' % (material_mapping_local[f_mat]))
1605                                         else:
1606                                                 if i==55:
1607                                                         file.write('\n\t\t\t\t')
1608                                                         i=0
1609                                                 
1610                                                 file.write(',%s' % (material_mapping_local[f_mat]))
1611                                         i+=1
1612                         
1613                         file.write('\n\t\t}')
1614                 
1615                 file.write('''
1616                 Layer: 0 {
1617                         Version: 100
1618                         LayerElement:  {
1619                                 Type: "LayerElementNormal"
1620                                 TypedIndex: 0
1621                         }''')
1622                 
1623                 if do_materials:
1624                         file.write('''
1625                         LayerElement:  {
1626                                 Type: "LayerElementMaterial"
1627                                 TypedIndex: 0
1628                         }''')
1629                         
1630                 # Always write this
1631                 if do_textures:
1632                         file.write('''
1633                         LayerElement:  {
1634                                 Type: "LayerElementTexture"
1635                                 TypedIndex: 0
1636                         }''')
1637                 
1638                 if me.vertexColors:
1639                         file.write('''
1640                         LayerElement:  {
1641                                 Type: "LayerElementColor"
1642                                 TypedIndex: 0
1643                         }''')
1644                 
1645                 if me.faceUV:
1646                         file.write('''
1647                         LayerElement:  {
1648                                 Type: "LayerElementUV"
1649                                 TypedIndex: 0
1650                         }''')
1651                 
1652                 
1653                 file.write('\n\t\t}')
1654                 
1655                 if len(uvlayers) > 1:
1656                         for i in xrange(1, len(uvlayers)):
1657                                 
1658                                 file.write('\n\t\tLayer: %i {' % i)
1659                                 file.write('\n\t\t\tVersion: 100')
1660                                 
1661                                 file.write('''
1662                         LayerElement:  {
1663                                 Type: "LayerElementUV"''')
1664                                 
1665                                 file.write('\n\t\t\t\tTypedIndex: %i' % i)
1666                                 file.write('\n\t\t\t}')
1667                                 
1668                                 if do_textures:
1669                                         
1670                                         file.write('''
1671                         LayerElement:  {
1672                                 Type: "LayerElementTexture"''')
1673                                         
1674                                         file.write('\n\t\t\t\tTypedIndex: %i' % i)
1675                                         file.write('\n\t\t\t}')
1676                                 
1677                                 file.write('\n\t\t}')
1678                 
1679                 if len(collayers) > 1:
1680                         # Take into account any UV layers
1681                         layer_offset = 0
1682                         if uvlayers: layer_offset = len(uvlayers)-1
1683                         
1684                         for i in xrange(layer_offset, len(collayers)+layer_offset):
1685                                 file.write('\n\t\tLayer: %i {' % i)
1686                                 file.write('\n\t\t\tVersion: 100')
1687                                 
1688                                 file.write('''
1689                         LayerElement:  {
1690                                 Type: "LayerElementColor"''')
1691                                 
1692                                 file.write('\n\t\t\t\tTypedIndex: %i' % i)
1693                                 file.write('\n\t\t\t}')
1694                                 file.write('\n\t\t}')
1695                 file.write('\n\t}')
1696         
1697         def write_group(name):
1698                 file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {' % name)
1699                 
1700                 file.write('''
1701                 Properties60:  {
1702                         Property: "MultiLayer", "bool", "",0
1703                         Property: "Pickable", "bool", "",1
1704                         Property: "Transformable", "bool", "",1
1705                         Property: "Show", "bool", "",1
1706                 }
1707                 MultiLayer: 0
1708         }''')
1709         
1710         
1711         # add meshes here to clear because they are not used anywhere.
1712         meshes_to_clear = []
1713         
1714         ob_meshes = []  
1715         ob_lights = []
1716         ob_cameras = []
1717         # in fbx we export bones as children of the mesh
1718         # armatures not a part of a mesh, will be added to ob_arms
1719         ob_bones = [] 
1720         ob_arms = []
1721         ob_null = [] # emptys
1722         
1723         # List of types that have blender objects (not bones)
1724         ob_all_typegroups = [ob_meshes, ob_lights, ob_cameras, ob_arms, ob_null]
1725         
1726         groups = [] # blender groups, only add ones that have objects in the selections
1727         materials = {}
1728         textures = {}
1729         
1730         tmp_ob_type = ob_type = None # incase no objects are exported, so as not to raise an error
1731         
1732         # if EXP_OBS_SELECTED is false, use sceens objects
1733         if not batch_objects:
1734                 if EXP_OBS_SELECTED:    tmp_objects = sce.objects.context
1735                 else:                                   tmp_objects = sce.objects
1736         else:
1737                 tmp_objects = batch_objects
1738         
1739         if EXP_ARMATURE:
1740                 # This is needed so applying modifiers dosnt apply the armature deformation, its also needed
1741                 # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes.
1742                 # set every armature to its rest, backup the original values so we done mess up the scene
1743                 ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures]
1744                 
1745                 for arm in bpy.data.armatures:
1746                         arm.restPosition = True
1747                 
1748                 if ob_arms_orig_rest:
1749                         for ob_base in bpy.data.objects:
1750                                 #if ob_base.type == 'Armature':
1751                                 ob_base.makeDisplayList()
1752                                         
1753                         # This causes the makeDisplayList command to effect the mesh
1754                         Blender.Set('curframe', Blender.Get('curframe'))
1755                         
1756         
1757         for ob_base in tmp_objects:
1758                 for ob, mtx in BPyObject.getDerivedObjects(ob_base):
1759                         #for ob in [ob_base,]:
1760                         tmp_ob_type = ob.type
1761                         if tmp_ob_type == 'Camera':
1762                                 if EXP_CAMERA:
1763                                         ob_cameras.append(my_object_generic(ob, mtx))
1764                         elif tmp_ob_type == 'Lamp':
1765                                 if EXP_LAMP:
1766                                         ob_lights.append(my_object_generic(ob, mtx))
1767                         elif tmp_ob_type == 'Armature':
1768                                 if EXP_ARMATURE:
1769                                         # TODO - armatures dont work in dupligroups!
1770                                         if ob not in ob_arms: ob_arms.append(ob)
1771                                         # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)"
1772                         elif tmp_ob_type == 'Empty':
1773                                 if EXP_EMPTY:
1774                                         ob_null.append(my_object_generic(ob, mtx))
1775                         elif EXP_MESH:
1776                                 origData = True
1777                                 if tmp_ob_type != 'Mesh':
1778                                         me = bpy.data.meshes.new()
1779                                         try:    me.getFromObject(ob)
1780                                         except: me = None
1781                                         if me:
1782                                                 meshes_to_clear.append( me )
1783                                                 mats = me.materials
1784                                                 origData = False
1785                                 else:
1786                                         # Mesh Type!
1787                                         if EXP_MESH_APPLY_MOD:
1788                                                 me = bpy.data.meshes.new()
1789                                                 me.getFromObject(ob)
1790                                                 
1791                                                 # so we keep the vert groups
1792                                                 if EXP_ARMATURE:
1793                                                         orig_mesh = ob.getData(mesh=1)
1794                                                         if orig_mesh.getVertGroupNames():
1795                                                                 ob.copy().link(me)
1796                                                                 # If new mesh has no vgroups we can try add if verts are teh same
1797                                                                 if not me.getVertGroupNames(): # vgroups were not kept by the modifier
1798                                                                         if len(me.verts) == len(orig_mesh.verts):
1799                                                                                 groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh)
1800                                                                                 BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
1801                                                 
1802                                                 # print ob, me, me.getVertGroupNames()
1803                                                 meshes_to_clear.append( me )
1804                                                 origData = False
1805                                                 mats = me.materials
1806                                         else:
1807                                                 me = ob.getData(mesh=1)
1808                                                 mats = me.materials
1809                                                 
1810                                                 # Support object colors
1811                                                 tmp_colbits = ob.colbits
1812                                                 if tmp_colbits:
1813                                                         tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too.
1814                                                         for i in xrange(16):
1815                                                                 if tmp_colbits & (1<<i):
1816                                                                         mats[i] = tmp_ob_mats[i]
1817                                                         del tmp_ob_mats
1818                                                 del tmp_colbits
1819                                                         
1820                                         
1821                                 if me:
1822                                         # This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled.
1823                                         # so strictly this is bad. but only in rare cases would it have negative results
1824                                         # say with dupliverts the objects would rotate a bit differently
1825                                         if EXP_MESH_HQ_NORMALS:
1826                                                 BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
1827                                         
1828                                         for mat in mats:
1829                                                 # 2.44 use mat.lib too for uniqueness
1830                                                 if mat: materials[mat] = mat
1831                                         
1832                                         texture_mapping_local = {}
1833                                         if me.faceUV:
1834                                                 uvlayer_orig = me.activeUVLayer
1835                                                 for uvlayer in me.getUVLayerNames():
1836                                                         me.activeUVLayer = uvlayer
1837                                                         for f in me.faces:
1838                                                                 img = f.image
1839                                                                 textures[img] = texture_mapping_local[img] = img
1840                                                         
1841                                                         me.activeUVLayer = uvlayer_orig
1842                                         
1843                                         if EXP_ARMATURE:
1844                                                 armob = BPyObject.getObjectArmature(ob)
1845                                                 blenParentBoneName = None
1846                                                 
1847                                                 # Note - Fixed in BPyObject but for now just copy the function because testers wont have up to date modukes,
1848                                                 # TODO - remove this for 2.45 release since getObjectArmature has been fixed
1849                                                 if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.ARMATURE:
1850                                                         armob = ob.parent
1851                                                 
1852                                                 # parent bone - special case
1853                                                 if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE:
1854                                                         armob = ob.parent
1855                                                         blenParentBoneName = ob.parentbonename
1856                                                 
1857                                                         
1858                                                 if armob and armob not in ob_arms:
1859                                                         ob_arms.append(armob)
1860                                         
1861                                         else:
1862                                                 blenParentBoneName = armob = None
1863                                         
1864                                         my_mesh = my_object_generic(ob, mtx)
1865                                         my_mesh.blenData =              me
1866                                         my_mesh.origData =              origData
1867                                         my_mesh.blenMaterials = mats
1868                                         my_mesh.blenTextures =  texture_mapping_local.values()
1869                                         
1870                                         # if only 1 null texture then empty the list
1871                                         if len(my_mesh.blenTextures) == 1 and my_mesh.blenTextures[0] == None:
1872                                                 my_mesh.blenTextures = []
1873                                         
1874                                         my_mesh.fbxArm =        armob                                   # replace with my_object_generic armature instance later
1875                                         my_mesh.fbxBoneParent = blenParentBoneName      # replace with my_bone instance later
1876                                         
1877                                         ob_meshes.append( my_mesh )
1878         
1879         if EXP_ARMATURE:
1880                 # now we have the meshes, restore the rest arm position
1881                 for i, arm in enumerate(bpy.data.armatures):
1882                         arm.restPosition = ob_arms_orig_rest[i]
1883                         
1884                 if ob_arms_orig_rest:
1885                         for ob_base in bpy.data.objects:
1886                                 if ob_base.type == 'Armature':
1887                                         ob_base.makeDisplayList()
1888                         # This causes the makeDisplayList command to effect the mesh
1889                         Blender.Set('curframe', Blender.Get('curframe'))
1890         
1891         del tmp_ob_type, tmp_objects
1892         
1893         # now we have collected all armatures, add bones
1894         for i, ob in enumerate(ob_arms):
1895                 
1896                 ob_arms[i] = my_arm = my_object_generic(ob)
1897                 
1898                 my_arm.fbxBones =               []
1899                 my_arm.blenData =               ob.data
1900                 my_arm.blenAction =             ob.action
1901                 my_arm.blenActionList = []
1902                 
1903                 # fbxName, blenderObject, my_bones, blenderActions
1904                 #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, [])
1905                 
1906                 for bone in my_arm.blenData.bones.values():
1907                         my_bone = my_bone_class(bone, my_arm)
1908                         my_arm.fbxBones.append( my_bone )
1909                         ob_bones.append( my_bone )
1910         
1911         # add the meshes to the bones and replace the meshes armature with own armature class
1912         #for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
1913         for my_mesh in ob_meshes:
1914                 # Replace 
1915                 # ...this could be sped up with dictionary mapping but its unlikely for
1916                 # it ever to be a bottleneck - (would need 100+ meshes using armatures)
1917                 if my_mesh.fbxArm:
1918                         for my_arm in ob_arms:
1919                                 if my_arm.blenObject == my_mesh.fbxArm:
1920                                         my_mesh.fbxArm = my_arm
1921                                         break
1922                 
1923                 for my_bone in ob_bones:
1924                         
1925                         # The mesh uses this bones armature!
1926                         if my_bone.fbxArm == my_mesh.fbxArm:
1927                                 my_bone.blenMeshes[my_mesh.fbxName] = me
1928                                 
1929                                 
1930                                 # parent bone: replace bone names with our class instances
1931                                 # my_mesh.fbxBoneParent is None or a blender bone name initialy, replacing if the names match.
1932                                 if my_mesh.fbxBoneParent == my_bone.blenName:
1933                                         my_mesh.fbxBoneParent = my_bone
1934         
1935         bone_deformer_count = 0 # count how many bones deform a mesh
1936         my_bone_blenParent = None
1937         for my_bone in ob_bones:
1938                 my_bone_blenParent = my_bone.blenBone.parent
1939                 if my_bone_blenParent:
1940                         for my_bone_parent in ob_bones:
1941                                 # Note 2.45rc2 you can compare bones normally
1942                                 if my_bone_blenParent.name == my_bone_parent.blenName and my_bone.fbxArm == my_bone_parent.fbxArm:
1943                                         my_bone.parent = my_bone_parent
1944                                         break
1945                 
1946                 # Not used at the moment
1947                 # my_bone.calcRestMatrixLocal()
1948                 bone_deformer_count += len(my_bone.blenMeshes)
1949         
1950         del my_bone_blenParent 
1951         
1952         
1953         # Build blenObject -> fbxObject mapping
1954         # this is needed for groups as well as fbxParenting
1955         bpy.data.objects.tag = False
1956         tmp_obmapping = {}
1957         for ob_generic in ob_all_typegroups:
1958                 for ob_base in ob_generic:
1959                         ob_base.blenObject.tag = True
1960                         tmp_obmapping[ob_base.blenObject] = ob_base
1961         
1962         # Build Groups from objects we export
1963         for blenGroup in bpy.data.groups:
1964                 fbxGroupName = None
1965                 for ob in blenGroup.objects:
1966                         if ob.tag:
1967                                 if fbxGroupName == None:
1968                                         fbxGroupName = sane_groupname(blenGroup)
1969                                         groups.append((fbxGroupName, blenGroup))
1970                                 
1971                                 tmp_obmapping[ob].fbxGroupNames.append(fbxGroupName) # also adds to the objects fbxGroupNames
1972         
1973         groups.sort() # not really needed
1974         
1975         # Assign parents using this mapping
1976         for ob_generic in ob_all_typegroups:
1977                 for my_ob in ob_generic:
1978                         parent = my_ob.blenObject.parent
1979                         if parent and parent.tag: # does it exist and is it in the mapping
1980                                 my_ob.fbxParent = tmp_obmapping[parent]
1981         
1982         
1983         del tmp_obmapping
1984         # Finished finding groups we use
1985         
1986         
1987         materials =     [(sane_matname(mat), mat) for mat in materials.itervalues() if mat]
1988         textures =      [(sane_texname(img), img) for img in textures.itervalues()  if img]
1989         materials.sort() # sort by name
1990         textures.sort()
1991         
1992         camera_count = 8
1993         file.write('''
1994
1995 ; Object definitions
1996 ;------------------------------------------------------------------
1997
1998 Definitions:  {
1999         Version: 100
2000         Count: %i''' % (\
2001                 1+1+camera_count+\
2002                 len(ob_meshes)+\
2003                 len(ob_lights)+\
2004                 len(ob_cameras)+\
2005                 len(ob_arms)+\
2006                 len(ob_null)+\
2007                 len(ob_bones)+\
2008                 bone_deformer_count+\
2009                 len(materials)+\
2010                 (len(textures)*2))) # add 1 for the root model 1 for global settings
2011         
2012         del bone_deformer_count
2013         
2014         file.write('''
2015         ObjectType: "Model" {
2016                 Count: %i
2017         }''' % (\
2018                 1+camera_count+\
2019                 len(ob_meshes)+\
2020                 len(ob_lights)+\
2021                 len(ob_cameras)+\
2022                 len(ob_arms)+\
2023                 len(ob_null)+\
2024                 len(ob_bones))) # add 1 for the root model
2025         
2026         file.write('''
2027         ObjectType: "Geometry" {
2028                 Count: %i
2029         }''' % len(ob_meshes))
2030         
2031         if materials:
2032                 file.write('''
2033         ObjectType: "Material" {
2034                 Count: %i
2035         }''' % len(materials))
2036         
2037         if textures:
2038                 file.write('''
2039         ObjectType: "Texture" {
2040                 Count: %i
2041         }''' % len(textures)) # add 1 for an empty tex
2042                 file.write('''
2043         ObjectType: "Video" {
2044                 Count: %i
2045         }''' % len(textures)) # add 1 for an empty tex
2046         
2047         tmp = 0
2048         # Add deformer nodes
2049         for my_mesh in ob_meshes:
2050                 if my_mesh.fbxArm:
2051                         tmp+=1
2052         
2053         # Add subdeformers
2054         for my_bone in ob_bones:
2055                 tmp += len(my_bone.blenMeshes)
2056         
2057         if tmp:
2058                 file.write('''
2059         ObjectType: "Deformer" {
2060                 Count: %i
2061         }''' % tmp)
2062         del tmp
2063         
2064         # we could avoid writing this possibly but for now just write it
2065         
2066         file.write('''
2067         ObjectType: "Pose" {
2068                 Count: 1
2069         }''')
2070         
2071         if groups:
2072                 file.write('''
2073         ObjectType: "GroupSelection" {
2074                 Count: %i
2075         }''' % len(groups))
2076         
2077         file.write('''
2078         ObjectType: "GlobalSettings" {
2079                 Count: 1
2080         }
2081 }''')
2082
2083         file.write('''
2084
2085 ; Object properties
2086 ;------------------------------------------------------------------
2087
2088 Objects:  {''')
2089         
2090         # To comply with other FBX FILES
2091         write_camera_switch()
2092         
2093         # Write the null object
2094         write_null(None, 'blend_root')# , GLOBAL_MATRIX) 
2095         
2096         for my_null in ob_null:
2097                 write_null(my_null)
2098         
2099         for my_arm in ob_arms:
2100                 write_null(my_arm)
2101         
2102         for my_cam in ob_cameras:
2103                 write_camera(my_cam)
2104
2105         for my_light in ob_lights:
2106                 write_light(my_light)
2107         
2108         for my_mesh in ob_meshes:
2109                 write_mesh(my_mesh)
2110
2111         #for bonename, bone, obname, me, armob in ob_bones:
2112         for my_bone in ob_bones:
2113                 write_bone(my_bone)
2114         
2115         write_camera_default()
2116         
2117         for matname, mat in materials:
2118                 write_material(matname, mat)
2119         
2120         # each texture uses a video, odd
2121         for texname, tex in textures:
2122                 write_video(texname, tex)
2123         i = 0
2124         for texname, tex in textures:
2125                 write_texture(texname, tex, i)
2126                 i+=1
2127         
2128         for groupname, group in groups:
2129                 write_group(groupname)
2130         
2131         # NOTE - c4d and motionbuilder dont need normalized weights, but deep-exploration 5 does and (max?) do.
2132         
2133         # Write armature modifiers
2134         # TODO - add another MODEL? - because of this skin definition.
2135         for my_mesh in ob_meshes:
2136                 if my_mesh.fbxArm:
2137                         write_deformer_skin(my_mesh.fbxName)
2138                         
2139                         # Get normalized weights for temorary use
2140                         if my_mesh.fbxBoneParent:
2141                                 weights = None
2142                         else:
2143                                 weights = meshNormalizedWeights(my_mesh.blenData)
2144                         
2145                         #for bonename, bone, obname, bone_mesh, armob in ob_bones:
2146                         for my_bone in ob_bones:
2147                                 if me in my_bone.blenMeshes.itervalues():
2148                                         write_sub_deformer_skin(my_mesh, my_bone, weights)
2149         
2150         # Write pose's really weired, only needed when an armature and mesh are used together
2151         # each by themselves dont need pose data. for now only pose meshes and bones
2152         
2153         file.write('''
2154         Pose: "Pose::BIND_POSES", "BindPose" {
2155                 Type: "BindPose"
2156                 Version: 100
2157                 Properties60:  {
2158                 }
2159                 NbPoseNodes: ''')
2160         file.write(str(len(pose_items)))
2161         
2162
2163         for fbxName, matrix in pose_items:
2164                 file.write('\n\t\tPoseNode:  {')
2165                 file.write('\n\t\t\tNode: "Model::%s"' % fbxName )
2166                 if matrix:              file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix))
2167                 else:                   file.write('\n\t\t\tMatrix: %s' % mat4x4str(mtx4_identity))
2168                 file.write('\n\t\t}')
2169         
2170         file.write('\n\t}')
2171         
2172         
2173         # Finish Writing Objects
2174         # Write global settings
2175         file.write('''
2176         GlobalSettings:  {
2177                 Version: 1000
2178                 Properties60:  {
2179                         Property: "UpAxis", "int", "",1
2180                         Property: "UpAxisSign", "int", "",1
2181                         Property: "FrontAxis", "int", "",2
2182                         Property: "FrontAxisSign", "int", "",1
2183                         Property: "CoordAxis", "int", "",0
2184                         Property: "CoordAxisSign", "int", "",1
2185                         Property: "UnitScaleFactor", "double", "",100
2186                 }
2187         }
2188 ''')    
2189         file.write('}')
2190         
2191         file.write('''
2192
2193 ; Object relations
2194 ;------------------------------------------------------------------
2195
2196 Relations:  {''')
2197
2198         file.write('\n\tModel: "Model::blend_root", "Null" {\n\t}')
2199
2200         for my_null in ob_null:
2201                 file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_null.fbxName)
2202
2203         for my_arm in ob_arms:
2204                 file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_arm.fbxName)
2205
2206         for my_mesh in ob_meshes:
2207                 file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % my_mesh.fbxName)
2208
2209         # TODO - limbs can have the same name for multiple armatures, should prefix.
2210         #for bonename, bone, obname, me, armob in ob_bones:
2211         for my_bone in ob_bones:
2212                 file.write('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_bone.fbxName)
2213         
2214         for my_cam in ob_cameras:
2215                 file.write('\n\tModel: "Model::%s", "Camera" {\n\t}' % my_cam.fbxName)
2216         
2217         for my_light in ob_lights:
2218                 file.write('\n\tModel: "Model::%s", "Light" {\n\t}' % my_light.fbxName)
2219         
2220         file.write('''
2221         Model: "Model::Producer Perspective", "Camera" {
2222         }
2223         Model: "Model::Producer Top", "Camera" {
2224         }
2225         Model: "Model::Producer Bottom", "Camera" {
2226         }
2227         Model: "Model::Producer Front", "Camera" {
2228         }
2229         Model: "Model::Producer Back", "Camera" {
2230         }
2231         Model: "Model::Producer Right", "Camera" {
2232         }
2233         Model: "Model::Producer Left", "Camera" {
2234         }
2235         Model: "Model::Camera Switcher", "CameraSwitcher" {
2236         }''')
2237         
2238         for matname, mat in materials:
2239                 file.write('\n\tMaterial: "Material::%s", "" {\n\t}' % matname)
2240
2241         if textures:
2242                 for texname, tex in textures:
2243                         file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {\n\t}' % texname)
2244                 for texname, tex in textures:
2245                         file.write('\n\tVideo: "Video::%s", "Clip" {\n\t}' % texname)
2246
2247         # deformers - modifiers
2248         for my_mesh in ob_meshes:
2249                 if my_mesh.fbxArm:
2250                         file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {\n\t}' % my_mesh.fbxName)
2251         
2252         #for bonename, bone, obname, me, armob in ob_bones:
2253         for my_bone in ob_bones:
2254                 for fbxMeshObName in my_bone.blenMeshes: # .keys() - fbxMeshObName
2255                         # is this bone effecting a mesh?
2256                         file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {\n\t}' % (fbxMeshObName, my_bone.fbxName))
2257         
2258         # This should be at the end
2259         # file.write('\n\tPose: "Pose::BIND_POSES", "BindPose" {\n\t}')
2260         
2261         for groupname, group in groups:
2262                 file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {\n\t}' % groupname)
2263         
2264         file.write('\n}')
2265         file.write('''
2266
2267 ; Object connections
2268 ;------------------------------------------------------------------
2269
2270 Connections:  {''')
2271         
2272         # NOTE - The FBX SDK dosnt care about the order but some importers DO!
2273         # for instance, defining the material->mesh connection
2274         # before the mesh->blend_root crashes cinema4d
2275         
2276
2277         # write the fake root node
2278         file.write('\n\tConnect: "OO", "Model::blend_root", "Model::Scene"')
2279         
2280         for ob_generic in ob_all_typegroups: # all blender 'Object's we support
2281                 for my_ob in ob_generic:
2282                         if my_ob.fbxParent:
2283                                 file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName))
2284                         else:
2285                                 file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_ob.fbxName)
2286         
2287         if materials:
2288                 for my_mesh in ob_meshes:
2289                         # Connect all materials to all objects, not good form but ok for now.
2290                         for mat in my_mesh.blenMaterials:
2291                                 if mat:
2292                                         file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_name_mapping_mat[mat.name], my_mesh.fbxName))
2293         
2294         if textures:
2295                 for my_mesh in ob_meshes:
2296                         if my_mesh.blenTextures:
2297                                 # file.write('\n\tConnect: "OO", "Texture::_empty_", "Model::%s"' % my_mesh.fbxName)
2298                                 for tex in my_mesh.blenTextures:
2299                                         if tex:
2300                                                 file.write('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (sane_name_mapping_tex[tex.name], my_mesh.fbxName))
2301                 
2302                 for texname, tex in textures:
2303                         file.write('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname))
2304         
2305         for my_mesh in ob_meshes:
2306                 if my_mesh.fbxArm:
2307                         file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName))
2308         
2309         #for bonename, bone, obname, me, armob in ob_bones:
2310         for my_bone in ob_bones:
2311                 for fbxMeshObName in my_bone.blenMeshes: # .keys()
2312                         file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName))
2313         
2314         # limbs -> deformers
2315         # for bonename, bone, obname, me, armob in ob_bones:
2316         for my_bone in ob_bones:
2317                 for fbxMeshObName in my_bone.blenMeshes: # .keys()
2318                         file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName))
2319         
2320         
2321         #for bonename, bone, obname, me, armob in ob_bones:
2322         for my_bone in ob_bones:
2323                 # Always parent to armature now
2324                 if my_bone.parent:
2325                         file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.parent.fbxName) )
2326                 else:
2327                         # the armature object is written as an empty and all root level bones connect to it
2328                         file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.fbxArm.fbxName) )
2329         
2330         # groups
2331         if groups:
2332                 for ob_generic in ob_all_typegroups:
2333                         for ob_base in ob_generic:
2334                                 for fbxGroupName in ob_base.fbxGroupNames:
2335                                         file.write('\n\tConnect: "OO", "Model::%s", "GroupSelection::%s"' % (ob_base.fbxName, fbxGroupName))
2336         
2337         for my_arm in ob_arms:
2338                 file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_arm.fbxName)
2339         
2340         file.write('\n}')
2341         
2342         
2343         # Needed for scene footer as well as animation
2344         render = sce.render
2345         
2346         # from the FBX sdk
2347         #define KTIME_ONE_SECOND        KTime (K_LONGLONG(46186158000))
2348         def fbx_time(t):
2349                 # 0.5 + val is the same as rounding.
2350                 return int(0.5 + ((t/fps) * 46186158000))
2351         
2352         fps = float(render.fps) 
2353         start = render.sFrame
2354         end =   render.eFrame
2355         if end < start: start, end = end, start
2356         if start==end: ANIM_ENABLE = False
2357         
2358         # animations for these object types
2359         ob_anim_lists = ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms
2360         
2361         if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]:
2362                 
2363                 frame_orig = Blender.Get('curframe')
2364                 
2365                 if ANIM_OPTIMIZE:
2366                         ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION
2367                 
2368                 # default action, when no actions are avaioable
2369                 tmp_actions = [None] # None is the default action
2370                 blenActionDefault = None
2371                 action_lastcompat = None
2372                 
2373                 if ANIM_ACTION_ALL:
2374                         bpy.data.actions.tag = False
2375                         tmp_actions = list(bpy.data.actions)
2376                         
2377                         
2378                         # find which actions are compatible with the armatures
2379                         # blenActions is not yet initialized so do it now.
2380                         tmp_act_count = 0
2381                         for my_arm in ob_arms:
2382                                 
2383                                 # get the default name
2384                                 if not blenActionDefault:
2385                                         blenActionDefault = my_arm.blenAction
2386                                 
2387                                 arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones])
2388                                 
2389                                 for action in tmp_actions:
2390                                         
2391                                         action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) )
2392                                         
2393                                         if action_chan_names: # at least one channel matches.
2394                                                 my_arm.blenActionList.append(action)
2395                                                 action.tag = True
2396                                                 tmp_act_count += 1
2397                                                 
2398                                                 # incase there is no actions applied to armatures
2399                                                 action_lastcompat = action
2400                         
2401                         if tmp_act_count:
2402                                 # unlikely to ever happen but if no actions applied to armatures, just use the last compatible armature.
2403                                 if not blenActionDefault:
2404                                         blenActionDefault = action_lastcompat
2405                 
2406                 del action_lastcompat
2407                 
2408                 file.write('''
2409 ;Takes and animation section
2410 ;----------------------------------------------------
2411
2412 Takes:  {''')
2413                 
2414                 if blenActionDefault:
2415                         file.write('\n\tCurrent: "%s"' % sane_takename(blenActionDefault))
2416                 else:
2417                         file.write('\n\tCurrent: "Default Take"')
2418                 
2419                 for blenAction in tmp_actions:
2420                         # we have tagged all actious that are used be selected armatures
2421                         if blenAction:
2422                                 if blenAction.tag:
2423                                         print '\taction: "%s" exporting...' % blenAction.name
2424                                 else:
2425                                         print '\taction: "%s" has no armature using it, skipping' % blenAction.name
2426                                         continue
2427                         
2428                         if blenAction == None:
2429                                 # Warning, this only accounts for tmp_actions being [None]
2430                                 file.write('\n\tTake: "Default Take" {')
2431                                 act_start =     start
2432                                 act_end =       end
2433                         else:
2434                                 # use existing name
2435                                 if blenAction == blenActionDefault: # have we alredy got the name
2436                                         file.write('\n\tTake: "%s" {' % sane_name_mapping_take[blenAction.name])
2437                                 else:
2438                                         file.write('\n\tTake: "%s" {' % sane_takename(blenAction))
2439                                         
2440                                 tmp = blenAction.getFrameNumbers()
2441                                 if tmp:
2442                                         act_start =     min(tmp)
2443                                         act_end =       max(tmp)
2444                                         del tmp
2445                                 else:
2446                                         # Fallback on this, theres not much else we can do? :/
2447                                         # when an action has no length
2448                                         act_start =     start
2449                                         act_end =       end
2450                                 
2451                                 # Set the action active
2452                                 for my_bone in ob_arms:
2453                                         if blenAction in my_bone.blenActionList:
2454                                                 ob.action = blenAction
2455                                                 # print '\t\tSetting Action!', blenAction
2456                                 # sce.update(1)
2457                         
2458                         file.write('\n\t\tFileName: "Default_Take.tak"') # ??? - not sure why this is needed
2459                         file.write('\n\t\tLocalTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed
2460                         file.write('\n\t\tReferenceTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed
2461                         
2462                         file.write('''
2463
2464                 ;Models animation
2465                 ;----------------------------------------------------''')
2466                         
2467                         
2468                         # set pose data for all bones
2469                         # do this here incase the action changes
2470                         '''
2471                         for my_bone in ob_bones:
2472                                 my_bone.flushAnimData()
2473                         '''
2474                         i = act_start
2475                         while i <= act_end:
2476                                 Blender.Set('curframe', i)
2477                                 for ob_generic in ob_anim_lists:
2478                                         for my_ob in ob_generic:
2479                                                 #Blender.Window.RedrawAll()
2480                                                 if ob_generic == ob_meshes and my_ob.fbxArm:
2481                                                         # We cant animate armature meshes!
2482                                                         pass
2483                                                 else:
2484                                                         my_ob.setPoseFrame(i)
2485                                                 
2486                                 i+=1
2487                         
2488                         
2489                         #for bonename, bone, obname, me, armob in ob_bones:
2490                         for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms):
2491                                         
2492                                 for my_ob in ob_generic:
2493                                         
2494                                         if ob_generic == ob_meshes and my_ob.fbxArm:
2495                                                 # do nothing,
2496                                                 pass
2497                                         else:
2498                                                         
2499                                                 file.write('\n\t\tModel: "Model::%s" {' % my_ob.fbxName) # ??? - not sure why this is needed
2500                                                 file.write('\n\t\t\tVersion: 1.1')
2501                                                 file.write('\n\t\t\tChannel: "Transform" {')
2502                                                 
2503                                                 context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ]
2504                                                 
2505                                                 # ----------------
2506                                                 # ----------------
2507                                                 for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
2508                                                         
2509                                                         if              TX_CHAN=='T':   context_bone_anim_vecs = [mtx[0].translationPart()      for mtx in context_bone_anim_mats]
2510                                                         elif    TX_CHAN=='R':   context_bone_anim_vecs = [mtx[1].toEuler()                      for mtx in context_bone_anim_mats]
2511                                                         else:                                   context_bone_anim_vecs = [mtx[0].scalePart()            for mtx in context_bone_anim_mats]
2512                                                         
2513                                                         file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
2514                                                         
2515                                                         for i in xrange(3):
2516                                                                 # Loop on each axis of the bone
2517                                                                 file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
2518                                                                 file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] )
2519                                                                 file.write('\n\t\t\t\t\t\tKeyVer: 4005')
2520                                                                 
2521                                                                 if not ANIM_OPTIMIZE:
2522                                                                         # Just write all frames, simple but in-eficient
2523                                                                         file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start))
2524                                                                         file.write('\n\t\t\t\t\t\tKey: ')
2525                                                                         frame = act_start
2526                                                                         while frame <= act_end:
2527                                                                                 if frame!=act_start:
2528                                                                                         file.write(',')
2529                                                                                 
2530                                                                                 # Curve types are 
2531                                                                                 # C,n is for bezier? - linear is best for now so we can do simple keyframe removal
2532                                                                                 file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n'  % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] ))
2533                                                                                 #file.write('\n\t\t\t\t\t\t\t%i,%.15f,L'  % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] ))
2534                                                                                 frame+=1
2535                                                                 else:
2536                                                                         # remove unneeded keys, j is the frame, needed when some frames are removed.
2537                                                                         context_bone_anim_keys = [ (vec[i], j) for j, vec in enumerate(context_bone_anim_vecs) ]
2538                                                                         
2539                                                                         # last frame to fisrt frame, missing 1 frame on either side.
2540                                                                         # removeing in a backwards loop is faster
2541                                                                         for j in xrange( (act_end-act_start)-1, 0, -1 ):
2542                                                                                 # Is this key reduenant?
2543                                                                                 if      abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\
2544                                                                                         abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT:
2545                                                                                         del context_bone_anim_keys[j]
2546                                                                         
2547                                                                         if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]:
2548                                                                                 # This axis has no moton, its okay to skip KeyCount and Keys in this case
2549                                                                                 pass
2550                                                                         else:
2551                                                                                 # We only need to write these if there is at least one 
2552                                                                                 file.write('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys))
2553                                                                                 file.write('\n\t\t\t\t\t\tKey: ')
2554                                                                                 for val, frame in context_bone_anim_keys:
2555                                                                                         if frame != context_bone_anim_keys[0][1]: # not the first
2556                                                                                                 file.write(',')
2557                                                                                         # frame is alredy one less then blenders frame
2558                                                                                         file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n'  % (fbx_time(frame), val ))
2559                                                                                         #file.write('\n\t\t\t\t\t\t\t%i,%.15f,L'  % (fbx_time(frame), val ))
2560                                                                 
2561                                                                 if              i==0:   file.write('\n\t\t\t\t\t\tColor: 1,0,0')
2562                                                                 elif    i==1:   file.write('\n\t\t\t\t\t\tColor: 0,1,0')
2563                                                                 elif    i==2:   file.write('\n\t\t\t\t\t\tColor: 0,0,1')
2564                                                                 
2565                                                                 file.write('\n\t\t\t\t\t}')
2566                                                         file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) )
2567                                                         file.write('\n\t\t\t\t}')
2568                                                 
2569                                                 # --------------- 
2570                                                 
2571                                                 file.write('\n\t\t\t}')
2572                                                 file.write('\n\t\t}')
2573                         
2574                         # end the take
2575                         file.write('\n\t}')
2576                         
2577                         # end action loop. set original actions 
2578                         # do this after every loop incase actions effect eachother.
2579                         for my_bone in ob_arms:
2580                                 my_bone.blenObject.action = my_bone.blenAction
2581                 
2582                 file.write('\n}')
2583                 
2584                 Blender.Set('curframe', frame_orig)
2585                 
2586         else:
2587                 # no animation
2588                 file.write('\n;Takes and animation section')
2589                 file.write('\n;----------------------------------------------------')
2590                 file.write('\n')
2591                 file.write('\nTakes:  {')
2592                 file.write('\n\tCurrent: ""')
2593                 file.write('\n}')
2594         
2595         
2596         # write meshes animation
2597         #for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
2598                 
2599         
2600         # Clear mesh data Only when writing with modifiers applied
2601         for me in meshes_to_clear:
2602                 me.verts = None
2603         
2604         
2605         
2606         # --------------------------- Footer
2607         if world:
2608                 has_mist = world.mode & 1
2609                 mist_intense, mist_start, mist_end, mist_height = world.mist
2610                 world_hor = world.hor
2611         else:
2612                 has_mist = mist_intense = mist_start = mist_end = mist_height = 0
2613                 world_hor = 0,0,0
2614         
2615         file.write('\n;Version 5 settings')
2616         file.write('\n;------------------------------------------------------------------')
2617         file.write('\n')
2618         file.write('\nVersion5:  {')
2619         file.write('\n\tAmbientRenderSettings:  {')
2620         file.write('\n\t\tVersion: 101')
2621         file.write('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world_amb))
2622         file.write('\n\t}')
2623         file.write('\n\tFogOptions:  {')
2624         file.write('\n\t\tFlogEnable: %i' % has_mist)
2625         file.write('\n\t\tFogMode: 0')
2626         file.write('\n\t\tFogDensity: %.3f' % mist_intense)
2627         file.write('\n\t\tFogStart: %.3f' % mist_start)
2628         file.write('\n\t\tFogEnd: %.3f' % mist_end)
2629         file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world_hor))
2630         file.write('\n\t}')
2631         file.write('\n\tSettings:  {')
2632         file.write('\n\t\tFrameRate: "%i"' % int(fps))
2633         file.write('\n\t\tTimeFormat: 1')
2634         file.write('\n\t\tSnapOnFrames: 0')
2635         file.write('\n\t\tReferenceTimeIndex: -1')
2636         file.write('\n\t\tTimeLineStartTime: %i' % fbx_time(start-1))
2637         file.write('\n\t\tTimeLineStopTime: %i' % fbx_time(end-1))
2638         file.write('\n\t}')
2639         file.write('\n\tRendererSetting:  {')
2640         file.write('\n\t\tDefaultCamera: "Producer Perspective"')
2641         file.write('\n\t\tDefaultViewingMode: 0')
2642         file.write('\n\t}')
2643         file.write('\n}')
2644         file.write('\n')
2645         
2646         # Incase sombody imports this, clean up by clearing global dicts
2647         sane_name_mapping_ob.clear()
2648         sane_name_mapping_mat.clear()
2649         sane_name_mapping_tex.clear()
2650         
2651         ob_arms[:] =    []
2652         ob_bones[:] =   []
2653         ob_cameras[:] = []
2654         ob_lights[:] =  []
2655         ob_meshes[:] =  []
2656         ob_null[:] =    []
2657         
2658         
2659         # copy images if enabled
2660         if EXP_IMAGE_COPY:
2661                 copy_images( Blender.sys.dirname(filename),  [ tex[1] for tex in textures if tex[1] != None ])  
2662         
2663         print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time)
2664         return True
2665         
2666
2667 # --------------------------------------------
2668 # UI Function - not a part of the exporter.
2669 # this is to seperate the user interface from the rest of the exporter.
2670 from Blender import Draw, Window
2671 EVENT_NONE = 0
2672 EVENT_EXIT = 1
2673 EVENT_REDRAW = 2
2674 EVENT_FILESEL = 3
2675
2676 GLOBALS = {}
2677
2678 # export opts
2679
2680 def do_redraw(e,v):             GLOBALS['EVENT'] = e
2681
2682 # toggle between these 2, only allow one on at once
2683 def do_obs_sel(e,v):
2684         GLOBALS['EVENT'] = e
2685         GLOBALS['EXP_OBS_SCENE'].val = 0
2686         GLOBALS['EXP_OBS_SELECTED'].val = 1
2687
2688 def do_obs_sce(e,v):
2689         GLOBALS['EVENT'] = e
2690         GLOBALS['EXP_OBS_SCENE'].val = 1
2691         GLOBALS['EXP_OBS_SELECTED'].val = 0
2692
2693 def do_obs_sce(e,v):
2694         GLOBALS['EVENT'] = e
2695         GLOBALS['EXP_OBS_SCENE'].val = 1
2696         GLOBALS['EXP_OBS_SELECTED'].val = 0
2697
2698 def do_batch_type_grp(e,v):
2699         GLOBALS['EVENT'] = e
2700         GLOBALS['BATCH_GROUP'].val = 1
2701         GLOBALS['BATCH_SCENE'].val = 0
2702
2703 def do_batch_type_sce(e,v):
2704         GLOBALS['EVENT'] = e
2705         GLOBALS['BATCH_GROUP'].val = 0
2706         GLOBALS['BATCH_SCENE'].val = 1
2707
2708 def do_anim_act_all(e,v):
2709         GLOBALS['EVENT'] = e
2710         GLOBALS['ANIM_ACTION_ALL'][0].val = 1
2711         GLOBALS['ANIM_ACTION_ALL'][1].val = 0
2712
2713 def do_anim_act_cur(e,v):
2714         if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val:
2715                 Draw.PupMenu('Warning%t|Cant use this with batch export group option')
2716         else:
2717                 GLOBALS['EVENT'] = e
2718                 GLOBALS['ANIM_ACTION_ALL'][0].val = 0
2719                 GLOBALS['ANIM_ACTION_ALL'][1].val = 1
2720
2721 def fbx_ui_exit(e,v):
2722         GLOBALS['EVENT'] = e
2723
2724 def do_help(e,v):
2725         url = 'http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx'
2726         print 'Trying to open web browser with documentation at this address...'