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