addons-contrib: more view_layer syntax updates
[blender-addons-contrib.git] / io_directx_bel / import_x.py
1 # Blender directX importer
2 # version baby
3
4 # litterature explaining the parser directions :
5
6 # I don't want to load the whole file as it can be huge : go chunks
7 # also I want random access to 3d datas to import pieces, not always everything
8 # so step1 is a whole file fast parsing, retrieving tokens name and building en empty internal dict
9 # with only pointers and no 3d datas.
10 # step 2 is to call any token by their names and retrieve the 3d datas thanks to pointers stored in dicts
11 # between step 1 and step 2 a script ui should be provided to select, transform etc before import.
12 # > I need to know the pointer position of tokens but data.tell() is slow
13 # a += pointer computed from line length is way faster. so I need eol -> rb mode
14 # and readline() is ok in binary mode 'rb' with \r\n (win) \n (unix) but not \r mac..
15 # 2chrs for windows, 1 for mac and lunix > win eol \r\n becomes \n\n (add a line)
16 # mac eol \r becomes \n so win lines info are wrong
17 # this also allows support for wrong files format (mixed \r and \r\n)
18 # for now it only works for text format, but the used methods will be independant of the container type.
19
20 # TEST FILES
21 # http://assimp.svn.sourceforge.net/viewvc/assimp/trunk/test/models/X/
22
23
24 import os
25 import re
26 import struct, binascii
27 import time
28
29 import bpy
30 import mathutils as bmat
31 from mathutils import Vector, Matrix
32
33 try :
34         import bel
35         import bel.mesh
36         import bel.image
37         import bel.uv
38         import bel.material
39         import bel.ob
40         import bel.fs
41 except :
42         import io_directx_bel.bel as bel
43         from .bel import mesh,image,uv,material,ob,fs
44
45 from .templates_x import *
46
47 '''
48 # just a temp hack to reload bel everytime
49 import imp
50 imp.reload(bel)
51 imp.reload(bel.fs)
52 imp.reload(bel.image)
53 imp.reload(bel.material)
54 imp.reload(bel.mesh)
55 imp.reload(bel.ob)
56 imp.reload(bel.uv)
57 '''
58
59 ###################################################
60
61 def load(operator, context, filepath,
62          global_clamp_size=0.0,
63          show_tree=False,
64          show_templates=False,
65          show_geninfo=False,
66          quickmode=False,
67          parented=False,
68          bone_maxlength=1.0,
69          chunksize=False,
70          naming_method=0,
71          use_ngons=True,
72          use_edges=True,
73          use_smooth_groups=True,
74          use_split_objects=True,
75          use_split_groups=True,
76          use_groups_as_vgroups=False,
77          use_image_search=True,
78          global_matrix=None,
79          ):
80
81
82     if quickmode :
83         parented = False
84
85     bone_minlength = bone_maxlength / 100.0
86
87     #global templates, tokens
88     rootTokens = []
89     namelookup = {}
90     imgnamelookup = {}
91     chunksize = int(chunksize)
92     reserved_type = (
93         'dword',
94         'float',
95         'string'
96     )
97
98     '''
99         'array',
100         'Matrix4x4',
101         'Vector',
102     '''
103     '''
104     with * : defined in dXdata
105
106     WORD     16 bits
107     * DWORD     32 bits
108     * FLOAT     IEEE float
109     DOUBLE     64 bits
110     CHAR     8 bits
111     UCHAR     8 bits
112     BYTE     8 bits
113     * STRING     NULL-terminated string
114     CSTRING     Formatted C-string (currently unsupported)
115     UNICODE     UNICODE string (currently unsupported)
116
117 BINARY FORMAT
118 # TOKENS in little-endian WORDs
119 #define TOKEN_NAME         1
120 #define TOKEN_STRING       2
121 #define TOKEN_INTEGER      3
122 #define TOKEN_GUID         5
123 #define TOKEN_INTEGER_LIST 6
124 #define TOKEN_FLOAT_LIST   7
125 #define TOKEN_OBRACE      10
126 #define TOKEN_CBRACE      11
127 #define TOKEN_OPAREN      12
128 #define TOKEN_CPAREN      13
129 #define TOKEN_OBRACKET    14
130 #define TOKEN_CBRACKET    15
131 #define TOKEN_OANGLE      16
132 #define TOKEN_CANGLE      17
133 #define TOKEN_DOT         18
134 #define TOKEN_COMMA       19
135 #define TOKEN_SEMICOLON   20
136 #define TOKEN_TEMPLATE    31
137 #define TOKEN_WORD        40
138 #define TOKEN_DWORD       41
139 #define TOKEN_FLOAT       42
140 #define TOKEN_DOUBLE      43
141 #define TOKEN_CHAR        44
142 #define TOKEN_UCHAR       45
143 #define TOKEN_SWORD       46
144 #define TOKEN_SDWORD      47
145 #define TOKEN_VOID        48
146 #define TOKEN_LPSTR       49
147 #define TOKEN_UNICODE     50
148 #define TOKEN_CSTRING     51
149 #define TOKEN_ARRAY       52
150
151     '''
152
153     # COMMON REGEX
154     space = '[\ \t]{1,}' # at least one space / tab
155     space0 = '[\ \t]{0,}' # zero or more space / tab
156
157     # DIRECTX REGEX TOKENS
158     r_template = r'template' + space + '[\w]*' + space0 + '\{'
159     if quickmode :
160         r_sectionname = r'Mesh' + space + '[\W-]*'
161     else :
162         r_sectionname = r'[\w]*' + space + '[\w-]*' + space0 + '\{'
163     r_refsectionname = r'\{' + space0 + '[\w-]*' + space0 + '\}'
164     r_endsection = r'\{|\}'
165
166     # dX comments
167     r_ignore = r'#|//'
168
169     #r_frame = r'Frame' + space + '[\w]*'
170     #r_matrix = r'FrameTransformMatrix' + space + '\{[\s\d.,-]*'
171     #r_mesh = r'Mesh' + space + '[\W]*'
172
173     ###################
174     ## STEP 1 FUNCTIONS
175     ###################
176
177     ## HEADER
178     # returns header values or False if directx reco tag is missing
179     # assuming there's never comment header and that xof if the 1st
180     # string of the file
181     '''
182      they look like xof 0303txt 0032
183      4       Magic Number (required) "xof "
184      2       Minor Version 03
185      2       Major Version 02
186      4       Format Type (required)
187         "txt " Text File
188         "bin " Binary File
189         "tzip" MSZip Compressed Text File
190         "bzip" MSZip Compressed Binary File
191      4       Float Accuracy "0032" 32 bit or "0064" 64 bit
192     '''
193     def dXheader(data) :
194         l = data.read(4)
195         if l != b'xof ' :
196             print ('no header found !')
197             data.seek(0)
198             return False
199         minor = data.read(2).decode()
200         major = data.read(2).decode()
201         format = data.read(4).decode().strip()
202         accuracy = int(data.read(4).decode())
203         data.seek(0)
204         return ( minor, major, format, accuracy )
205
206
207     ##
208     def dXtree(data,quickmode = False) :
209         tokens = {}
210         templates = {}
211         tokentypes = {}
212         c = 0
213         lvl = 0
214         tree = ['']
215         ptr = 0
216         eol = 0
217         trunkated = False
218         previouslvl = False
219         while True :
220         #for l in data.readlines() :
221             lines, trunkated = nextFileChunk(data,trunkated)
222             if lines == None : break
223             for l in lines :
224
225                 # compute pointer position
226                 ptr += eol
227                 c += 1
228                 eol = len(l) + 1
229                 #print(c,data.tell(),ptr+eol)
230                 #if l != '' : print('***',l)
231                 #if l == ''  : break
232                 l = l.strip()
233
234                 # remove blank and comment lines
235                 if l == '' or re.match(r_ignore,l) :
236                     continue
237
238                 # one line token cases level switch
239                 if previouslvl :
240                     lvl -= 1
241                     previouslvl = False
242
243                 #print('%s lines in %.2f\''%(c,time.clock()-t),end='\r')
244                 #print(c,len(l)+1,ptr,data.tell())
245                 if '{' in l :
246                     lvl += 1
247                     if '}' in l : previouslvl = True #; print('got one line token : \n%s'%l)
248                 elif '}' in l :
249                     lvl -= 1
250                 #print(c,lvl,tree)
251
252                 if quickmode == False :
253                     ## look for templates
254                     if re.match(r_template,l) :
255                         tname = l.split(' ')[1]
256                         templates[tname] = {'pointer' : ptr, 'line' : c}
257                         continue
258
259                     ## look for {references}
260                     if re.match(r_refsectionname,l) :
261                         refname = namelookup[ l[1:-1].strip() ]
262                         #print('FOUND reference to %s in %s at line %s (level %s)'%(refname,tree[lvl-1],c,lvl))
263                         #tree = tree[0:lvl]
264                         parent = tree[lvl-1]
265                         # tag it as a reference, since it's not exactly a child.
266                         # put it in childs since order can matter in sub tokens declaration
267                         tokens[parent]['childs'].append('*'+refname)
268                         if refname not in tokens :
269                             print('reference to %s done before its declaration (line %s)\ncreated dummy'%(refname,c))
270                             tokens[refname] = {}
271                         if 'user' not in tokens[refname] : tokens[refname]['users'] = [parent]
272                         else : tokens[refname]['users'].append(parent)
273                         continue
274
275                 ## look for any token or only Mesh token in quickmode
276                 if re.match(r_sectionname,l) :
277                     tokenname = getName(l,tokens)
278                     #print('FOUND %s %s %s %s'%(tokenname,c,lvl,tree))
279                     #print('pointer %s %s'%(data.tell(),ptr))
280                     if lvl == 1 : rootTokens.append(tokenname)
281                     typ = l.split(' ')[0].strip().lower()
282                     tree = tree[0:lvl]
283                     if typ not in tokentypes : tokentypes[typ] = [tokenname]
284                     else : tokentypes[typ].append(tokenname)
285                     parent = tree[-1]
286                     if tokenname in tokens :
287                         tokens[tokenname]['pointer'] = ptr
288                         tokens[tokenname]['line'] = c
289                         tokens[tokenname]['parent'] = parent
290                         tokens[tokenname]['childs'] = []
291                         tokens[tokenname]['type'] = typ
292
293                     else : tokens[tokenname] = {'pointer': ptr,
294                                                 'line'   : c,
295                                                 'parent' : parent,
296                                                 'childs' : [],
297                                                 'users'  : [],
298                                                 'type'   : typ
299                                                 }
300                     tree.append(tokenname)
301                     if lvl > 1 and quickmode == False :
302                         tokens[parent]['childs'].append(tokenname)
303
304         return tokens, templates, tokentypes
305
306     ## returns file binary chunks
307     def nextFileChunk(data,trunkated=False,chunksize=1024) :
308         if chunksize == 0 : chunk = data.read()
309         else : chunk = data.read(chunksize)
310         if format == 'txt' :
311             lines = chunk.decode('utf-8', errors='ignore')
312             #if stream : return lines.replace('\r','').replace('\n','')
313             lines = lines.replace('\r','\n').split('\n')
314             if trunkated : lines[0] = trunkated + lines[0]
315             if len(lines) == 1 :
316                 if lines[0] == '' : return None, None
317                 return lines, False
318             return lines, lines.pop()
319         # wip, todo for binaries
320         else :
321             print(chunk)
322             for word in range(0,len(chunk)) :
323                 w = chunk[word:word+4]
324                 print(word,w,struct.unpack("<l", w),binascii.unhexlify(w))
325
326
327     # name unnamed tokens, watchout for x duplicate
328     # for blender, referenced token in x should be named and unique..
329     def getName(l,tokens) :
330         xnam = l.split(' ')[1].strip()
331
332         #if xnam[0] == '{' : xnam = ''
333         if xnam and xnam[-1] == '{' : xnam = xnam[:-1]
334
335         name = xnam
336         if len(name) == 0 : name = l.split(' ')[0].strip()
337
338         namelookup[xnam] = bel.bpyname(name,tokens,4)
339
340         return namelookup[xnam]
341
342
343     ###################
344     ## STEP 2 FUNCTIONS
345     ###################
346     # once the internal dict is populated the functions below can be used
347
348     ## from a list of tokens, displays every child, users and references
349     '''
350       walk_dxtree( [ 'Mesh01', 'Mesh02' ] ) # for particular pieces
351       walk_dxtree(tokens.keys()) for the whole tree
352     '''
353     def walk_dXtree(field,lvl=0,tab='') :
354         for fi, tokenname in enumerate(field) :
355             if lvl > 0 or tokens[tokenname]['parent'] == '' :
356                 if tokenname not in tokens :
357                     tokenname = tokenname[1:]
358                     ref = 'ref: '
359                 else : ref = False
360
361                 frame_type = tokens[tokenname]['type']
362                 line = ('{:7}'.format(tokens[tokenname]['line']))
363                 log = ' %s%s (%s)'%( ref if ref else '', tokenname, frame_type )
364                 print('%s.%s%s'%(line, tab, log))
365                 if fi == len(field) - 1 : tab = tab[:-3] + '   '
366
367                 if ref == False :
368                     for user in tokens[tokenname]['users'] :
369                          print('%s.%s |__ user: %s'%(line, tab.replace('_',' '), user))
370                     walk_dXtree(tokens[tokenname]['childs'],lvl+1,tab.replace('_',' ')+' |__')
371
372                 if fi == len(field) - 1 and len(tokens[tokenname]['childs']) == 0 :
373                     print('%s.%s'%(line,tab))
374
375     ## remove eol, comments, spaces from a raw block of datas
376     def cleanBlock(block) :
377         while '//' in block :
378             s = block.index('//')
379             e = block.index('\n',s+1)
380             block = block[0:s] + block[e:]
381         while '#' in block :
382             s = block.index('#')
383             e = block.index('\n',s+1)
384             block = block[0:s] + block[e:]
385         block = block.replace('\n','').replace(' ','').replace('\t ','')
386         return block
387
388     def readToken(tokenname) :
389         token = tokens[tokenname]
390         datatype = token['type'].lower()
391         if datatype in templates : tpl = templates[datatype]
392         elif datatype in defaultTemplates : tpl = defaultTemplates[datatype]
393         else :
394             print("can't find any template to read %s (type : %s)"%(tokenname,datatype))
395             return False
396         #print('> use template %s'%datatype)
397         block = readBlock(data,token)
398         ptr = 0
399         #return dXtemplateData(tpl,block)
400         fields, ptr = dXtemplateData(tpl,block)
401         if datatype in templatesConvert :
402             fields = eval( templatesConvert[datatype] )
403         return fields
404
405     def dXtemplateData(tpl,block,ptr=0) :
406         #print('dxTPL',block[ptr])
407         pack = []
408         for member in tpl['members'] :
409             #print(member)
410             dataname = member[-1]
411             datatype = member[0].lower()
412             if datatype ==  'array' :
413                 datatype = member[1].lower()
414                 s = dataname.index('[') + 1
415                 e = dataname.index(']')
416                 #print(dataname[s:e])
417                 length = eval(dataname[s:e])
418                 #print("array %s type %s length defined by '%s' : %s"%(dataname[:s-1],datatype,dataname[s:e],length))
419                 dataname = dataname[:s-1]
420                 datavalue, ptr = dXarray(block, datatype, length, ptr)
421                 #print('back to %s'%(dataname))
422             else :
423                 length = 1
424                 datavalue, ptr = dXdata(block, datatype, length, ptr)
425
426             #if len(str(datavalue)) > 50 : dispvalue = str(datavalue[0:25]) + ' [...] ' + str(datavalue[-25:])
427             #else : dispvalue = str(datavalue)
428             #print('%s :  %s %s'%(dataname,dispvalue,type(datavalue)))
429             exec('%s = datavalue'%(dataname))
430             pack.append( datavalue )
431         return pack, ptr + 1
432
433     def dXdata(block,datatype,length,s=0,eof=';') :
434         #print('dxDTA',block[s])
435         # at last, the data we need
436         # should be a ';' but one meet ',' often, like in meshface
437         if datatype == 'dword' :
438             e = block.index(';',s+1)
439             try : field = int(block[s:e])
440             except :
441                 e = block.index(',',s+1)
442                 field = int(block[s:e])
443             return field, e+1
444         elif datatype == 'float' :
445             e = block.index(eof,s+1)
446             return float(block[s:e]), e+1
447         elif datatype == 'string' :
448             e = block.index(eof,s+1)
449             return str(block[s+1:e-1]) , e+1
450         else :
451             if datatype in templates : tpl = templates[datatype]
452             elif datatype in defaultTemplates : tpl = defaultTemplates[datatype]
453             else :
454                 print("can't find any template for type : %s"%(datatype))
455                 return False
456             #print('> use template %s'%datatype)
457             fields, ptr = dXtemplateData(tpl,block,s)
458             if datatype in templatesConvert :
459                 fields = eval( templatesConvert[datatype] )
460             return fields, ptr
461
462     def dXarray(block, datatype, length, s=0) :
463         #print('dxARR',block[s])
464         lst = []
465         if datatype in reserved_type :
466             eoi=','
467             for i in range(length) :
468                 if i+1 == length : eoi = ';'
469                 datavalue, s = dXdata(block,datatype,1,s,eoi)
470                 lst.append( datavalue )
471
472         else :
473             eoi = ';,'
474             for i in range(length) :
475                 if i+1 == length : eoi = ';;'
476                 #print(eoi)
477                 e = block.index(eoi,s)
478                 #except : print(block,s) ; popo()
479                 datavalue, na = dXdata(block[s:e+1],datatype,1)
480                 lst.append( datavalue )
481                 s = e + 2
482         return lst, s
483
484     ###################################################
485
486     ## populate a template with its datas
487     # this make them available in the internal dict. should be used in step 2 for unknown data type at least
488     def readTemplate(data,tpl_name,display=False) :
489         ptr = templates[tpl_name]['pointer']
490         line = templates[tpl_name]['line']
491         #print('> %s at line %s (chr %s)'%(tpl_name,line,ptr))
492         data.seek(ptr)
493         block = ''
494         trunkated = False
495         go = True
496         while go :
497             lines, trunkated = nextFileChunk(data,trunkated,chunksize) # stream ?
498             if lines == None :
499                 break
500             for l in lines :
501                 #l = data.readline().decode().strip()
502                 block += l.strip()
503                 if '}' in l :
504                     go = False
505                     break
506
507         uuid = re.search(r'<.+>',block).group()
508         templates[tpl_name]['uuid'] = uuid.lower()
509         templates[tpl_name]['members'] = []
510         templates[tpl_name]['restriction'] = 'closed'
511
512         members = re.search(r'>.+',block).group()[1:-1].split(';')
513         for member in members :
514             if member == '' : continue
515             if member[0] == '[' :
516                 templates[tpl_name]['restriction'] = member
517                 continue
518             templates[tpl_name]['members'].append( member.split(' ') )
519
520         if display :
521             print('\ntemplate %s :'%tpl_name)
522             for k,v in templates[tpl_name].items() :
523                 if k != 'members' :
524                     print('  %s : %s'%(k,v))
525                 else :
526                     for member in v :
527                         print('  %s'%str(member)[1:-1].replace(',',' ').replace("'",''))
528
529             if tpl_name in defaultTemplates :
530                 defaultTemplates[tpl_name]['line'] = templates[tpl_name]['line']
531                 defaultTemplates[tpl_name]['pointer'] = templates[tpl_name]['pointer']
532                 if defaultTemplates[tpl_name] != templates[tpl_name] :
533                     print('! DIFFERS FROM BUILTIN TEMPLATE :')
534                     print('raw template %s :'%tpl_name)
535                     print(templates[tpl_name])
536                     print('raw default template %s :'%tpl_name)
537                     print(defaultTemplates[tpl_name])
538                     #for k,v in defaultTemplates[tpl_name].items() :
539                     #    if k != 'members' :
540                     #        print('  %s : %s'%(k,v))
541                     #    else :
542                     #        for member in v :
543                     #            print('  %s'%str(member)[1:-1].replace(',',' ').replace("'",''))
544                 else :
545                     print('MATCHES BUILTIN TEMPLATE')
546
547
548     ##  read any kind of token data block
549     # by default the block is cleaned from inline comment space etc to allow data parsing
550     # useclean = False (retrieve all bytes) if you need to compute a file byte pointer
551     # to mimic the file.tell() function and use it with file.seek()
552     def readBlock(data,token, clean=True) :
553         ptr = token['pointer']
554         data.seek(ptr)
555         block = ''
556         #lvl = 0
557         trunkated = False
558         go = True
559         while go :
560             lines, trunkated = nextFileChunk(data,trunkated,chunksize)
561             if lines == None : break
562             for l in lines :
563                 #eol = len(l) + 1
564                 l = l.strip()
565                 #c += 1
566                 block += l+'\n'
567                 if re.match(r_endsection,l) :
568                     go = False
569                     break
570         s = block.index('{') + 1
571         e = block.index('}')
572         block = block[s:e]
573         if clean : block = cleanBlock(block)
574         return block
575
576     def getChilds(tokenname) :
577         childs = []
578         # '*' in childname means it's a reference. always perform this test
579         # when using the childs field
580         for childname in tokens[tokenname]['childs'] :
581             if childname[0] == '*' : childname = childname[1:]
582             childs.append( childname )
583         return childs
584
585     # the input nested list of [bonename, matrix, [child0,child1..]] is given by import_dXtree()
586     def buildArm(armdata, child,lvl=0,parent_matrix=False) :
587
588         bonename, bonemat, bonechilds = child
589
590         if lvl == 0 :
591             armname = armdata
592             armdata = bpy.data.armatures.new(name=armname)
593             arm = bpy.data.objects.new(armname,armdata)
594             bpy.context.scene.objects.link(arm)
595             arm.select_set(True)
596             bpy.context.view_layer.objects.active = arm
597             bpy.ops.object.mode_set(mode='EDIT')
598             parent_matrix = Matrix()
599
600         bone = armdata.edit_bones.new(name=bonename)
601         bonematW = parent_matrix * bonemat
602         bone.head = bonematW.to_translation()
603         #bone.roll.. ?
604         bone_length = bone_maxlength
605         for bonechild in bonechilds :
606             bonechild = buildArm(armdata,bonechild,lvl+1,bonematW)
607             bonechild.parent = bone
608             bone_length = min((bonechild.head - bone.head).length, bone_length)
609         bone.tail = bonematW * Vector((0,bone_length,0))
610         if lvl == 0 :
611             bpy.ops.object.mode_set(mode='OBJECT')
612             return arm
613         return bone
614
615     def import_dXtree(field,lvl=0) :
616         tab = ' '*lvl*2
617         if field == [] :
618             if show_geninfo : print('%s>> no childs, return False'%(tab))
619             return False
620         ob = False
621         mat = False
622         is_root = False
623         frames = []
624         obs = []
625
626         parentname = tokens[field[0]]['parent']
627         if show_geninfo : print('%s>>childs in frame %s :'%(tab,parentname))
628
629         for tokenname in field :
630
631             tokentype = tokens[tokenname]['type']
632
633             # frames can contain more than one mesh
634             if tokentype  == 'mesh' :
635                 # object and mesh naming :
636                 # if parent frame has several meshes : obname = meshname = mesh token name,
637                 # if parent frame has only one mesh  : obname = parent frame name, meshname =  mesh token name.
638                 if parentname :
639                     meshcount = 0
640                     for child in getChilds(parentname) :
641                         if tokens[child]['type'] == 'mesh' :
642                             meshcount += 1
643                             if meshcount == 2 :
644                                 parentname = tokenname
645                                 break
646                 else : parentname = tokenname
647
648                 ob = getMesh(parentname,tokenname)
649                 obs.append(ob)
650
651                 if show_geninfo : print('%smesh : %s'%(tab,tokenname))
652
653             # frames contain one matrix (empty or bone)
654             elif tokentype  == 'frametransformmatrix' :
655                 [mat] = readToken(tokenname)
656                 if show_geninfo : print('%smatrix : %s'%(tab,tokenname))
657
658             # frames can contain 0 or more frames
659             elif tokentype  == 'frame' :
660                 frames.append(tokenname)
661                 if show_geninfo : print('%sframe : %s'%(tab,tokenname))
662
663         # matrix is used for mesh transform if some mesh(es) exist(s)
664         if ob :
665             is_root = True
666             if mat == False :
667                 mat = Matrix()
668                 if show_geninfo : print('%smesh token without matrix, set it to default\n%splease report in bug tracker if you read this !'%(tab,tab))
669             if parentname == '' :
670                 mat = mat * global_matrix
671             if len(obs) == 1 :
672                 ob.matrix_world = mat
673             else :
674                 ob = bel.ob.new(parentname, None, naming_method)
675                 ob.matrix_world = mat
676                 for child in obs :
677                     child.parent = ob
678
679         # matrix only, store it as a list as we don't know if
680         # it's a bone or an empty yet
681         elif mat :
682             ob = [parentname, mat,[]]
683
684         # nothing case ?
685         else :
686             ob = [parentname, Matrix() * global_matrix,[]]
687             if show_geninfo : print('%snothing here'%(tab))
688
689         childs = []
690
691         for tokenname in frames :
692             if show_geninfo : print('%s<Begin %s :'%(tab,tokenname))
693
694             # child is either False, empty, object, or a list or undefined name matrices hierarchy
695             child = import_dXtree(getChilds(tokenname),lvl+1)
696             if child and type(child) != list :
697                 is_root = True
698             childs.append( [tokenname, child] )
699             if show_geninfo : print('%sEnd %s>'%(tab,tokenname))
700
701         if is_root and parentname != '' :
702
703             if show_geninfo : print('%send of tree a this point'%(tab))
704             if type(ob) == list :
705                 mat = ob[1]
706                 ob = bel.ob.new(parentname, None, naming_method)
707             ob.matrix_world = mat
708
709         for tokenname, child in childs :
710             if show_geninfo : print('%sbegin2 %s>'%(tab,tokenname))
711             # returned a list of object(s) or matrice(s)
712             if child :
713
714                 # current frame is an object or an empty, we parent this frame to it
715                 #if eot or (ob and ( type(ob.data) == type(None) or type(ob.data) == bpy.types.Mesh ) ) :
716                 if is_root :
717                     # this branch is an armature, convert it
718                     if type(child) == list :
719                         if show_geninfo : print('%sconvert to armature %s'%(tab,tokenname))
720                         child = buildArm(tokenname, child)
721
722                     # parent the obj/empty/arm to current
723                     # or apply the global user defined matrix to the object root
724                     if parentname != '' :
725                         child.parent = ob
726                     else :
727                         child.matrix_world = global_matrix
728
729                 # returned a list of parented matrices. append it in childs list
730                 elif type(child[0]) == str :
731                     ob[2].append(child)
732
733                 # child is an empty or a mesh, so current frame is an empty, not an armature
734                 elif ob and ( type(child.data) == type(None) or type(child.data) == bpy.types.Mesh ) :
735                     #print('  child data type: %s'%type(child.data))
736                     child.parent = ob
737                     #print('%s parented to %s'%(child.name,ob.name))
738
739             # returned False
740             else :
741                  if show_geninfo : print('%sreturned %s, nothing'%(tab,child))
742
743         #print('>> %s return %s'%(field,ob))
744         return ob# if ob else False
745
746     # build from mesh token type
747     def getMesh(obname,tokenname,debug = False):
748
749         if debug : print('\nmesh name : %s'%tokenname)
750
751         verts = []
752         edges = []
753         faces = []
754         matslots = []
755         facemats = []
756         uvs = []
757         groupnames = []
758         groupindices = []
759         groupweights = []
760
761         nVerts, verts, nFaces, faces = readToken(tokenname)
762
763         if debug :
764             print('verts    : %s %s\nfaces    : %s %s'%(nVerts, len(verts),nFaces, len(faces)))
765
766         #for childname in token['childs'] :
767         for childname in getChilds(tokenname) :
768
769             tokentype = tokens[childname]['type']
770
771             # UV
772             if tokentype == 'meshtexturecoords' :
773                 uv = readToken(childname)
774                 #uv = bel.uv.asVertsLocation(uv, faces)
775                 uv = bel.uv.asFlatList(uv, faces)
776                 uvs.append(uv)
777
778                 if debug : print('uv       : %s'%(len(uv)))
779
780             # MATERIALS
781             elif tokentype == 'meshmateriallist' :
782                 nbslots, facemats = readToken(childname)
783
784                 if debug : print('facemats : %s'%(len(facemats)))
785
786                 # mat can exist but with no datas so we prepare the mat slot
787                 # with dummy ones
788                 for slot in range(nbslots) :
789                     matslots.append('dXnoname%s'%slot )
790
791                 # length does not match (could be tuned more, need more cases)
792                 if len(facemats) != len(faces) :
793                     facemats = [ facemats[0] for i in faces ]
794
795                 # seek for materials then textures if any mapped in this mesh.
796                 # no type test, only one option type in token meshmateriallist : 'Material'
797                 for slotid, matname in enumerate(getChilds(childname)) :
798
799                     # rename dummy mats with the right name
800                     matslots[slotid] = matname
801
802                     # blender material creation (need tuning)
803                     mat = bel.material.new(matname,naming_method)
804                     matslots[slotid] = mat.name
805
806                     if naming_method != 1 :
807                         #print('matname : %s'%matname)
808                         (diffuse_color,alpha), power, specCol, emitCol = readToken(matname)
809                         #if debug : print(diffuse_color,alpha, power, specCol, emitCol)
810                         mat.diffuse_color = diffuse_color
811                         mat.diffuse_intensity = power
812                         mat.specular_color = specCol
813                         # dX emit don't use diffuse color but is a color itself
814                         # convert it to a kind of intensity
815                         mat.emit = (emitCol[0] + emitCol[1] + emitCol[2] ) / 3
816
817                         if alpha != 1.0 :
818                             mat.use_transparency = True
819                             mat.transparency_method = 'Z_TRANSPARENCY'
820                             mat.alpha = alpha
821                             mat.specular_alpha = 0
822                             transp = True
823                         else : transp = False
824
825                         # texture
826                         # only 'TextureFilename' can be here, no type test
827                         # textures have no name in .x so we build
828                         # image and texture names from the image file name
829                         # bdata texture slot name = bdata image name
830                         btexnames = []
831                         for texname in getChilds(matname) :
832
833                             # create/rename/reuse etc corresponding data image
834                             # (returns False if not found)
835                             [filename] = readToken(texname)
836                             img = bel.image.new(path+'/'+filename)
837
838                             if img == False :
839                                 imgname = 'not_found'
840                             else :
841                                 imgname = img.name
842
843                             #print('texname : %s'%texname)
844                             #print('filename : %s'%filename)
845                             #print('btex/img name : %s'%imgname)
846
847                             # associated texture (no naming check.. maybe tune more)
848                             # tex and texslot are created even if img not found
849                             if imgname in bpy.data.textures and ( img == False or bpy.data.textures[imgname].image == img ) :
850                                 tex = bpy.data.textures[imgname]
851                             else :
852                                 tex = bpy.data.textures.new(name=imgname,type='IMAGE')
853                                 if img : tex.image = img
854
855                             tex.use_alpha = transp
856                             tex.use_preview_alpha = transp
857
858                             # then create texture slot
859                             texslot = mat.texture_slots.create(index=0)
860                             texslot.texture = tex
861                             texslot.texture_coords = 'UV'
862                             texslot.uv_layer = 'UV0'
863                             texslot.use_map_alpha = transp
864                             texslot.alpha_factor = alpha
865
866                 # create remaining dummy mat
867                 for slotid, matname in enumerate(matslots) :
868                     if matname not in bpy.data.materials :
869                         mat = bel.material.new(matname,naming_method)
870                         matslots[slotid] = mat.name
871
872                 if debug : print('matslots : %s'%matslots)
873
874             # VERTICES GROUPS/WEIGHTS
875             elif tokentype == 'skinweights' :
876                 groupname, nverts, vindices, vweights, mat = readToken(childname)
877                 groupname = namelookup[groupname]
878                 if debug :
879                     print('vgroup    : %s (%s/%s verts) %s'%(groupname,len(vindices),len(vweights),'bone' if groupname in tokens else ''))
880
881                 #if debug : print('matrix : %s\n%s'%(type(mat),mat))
882
883                 groupnames.append(groupname)
884                 groupindices.append(vindices)
885                 groupweights.append(vweights)
886
887         ob = bel.mesh.write(obname,tokenname,
888                             verts, edges, faces,
889                             matslots, facemats, uvs,
890                             groupnames, groupindices, groupweights,
891                             use_smooth_groups,
892                             naming_method)
893
894         return ob
895
896     ## here we go
897
898     file = os.path.basename(filepath)
899
900     print('\nimporting %s...'%file)
901     start = time.clock()
902     path = os.path.dirname(filepath)
903     filepath = os.fsencode(filepath)
904     data = open(filepath,'rb')
905     header = dXheader(data)
906
907     if global_matrix is None:
908         global_matrix = mathutils.Matrix()
909
910     if header :
911         minor, major, format, accuracy = header
912
913         if show_geninfo :
914             print('\n%s directX header'%file)
915             print('  minor  : %s'%(minor))
916             print('  major  : %s'%(major))
917             print('  format : %s'%(format))
918             print('  floats are %s bits'%(accuracy))
919
920         if format in [ 'txt' ] : #, 'bin' ] :
921
922             ## FILE READ : STEP 1 : STRUCTURE
923             if show_geninfo : print('\nBuilding internal .x tree')
924             t = time.clock()
925             tokens, templates, tokentypes = dXtree(data,quickmode)
926             readstruct_time = time.clock()-t
927             if show_geninfo : print('builded tree in %.2f\''%(readstruct_time)) # ,end='\r')
928
929             ## populate templates with datas
930             for tplname in templates :
931                 readTemplate(data,tplname,show_templates)
932
933             ## DATA TREE CHECK
934             if show_tree :
935                 print('\nDirectX Data Tree :\n')
936                 walk_dXtree(tokens.keys())
937
938             ## DATA IMPORTATION
939             if show_geninfo :
940                 #print(tokens)
941                 print('Root frames :\n %s'%rootTokens)
942             if parented :
943                 import_dXtree(rootTokens)
944             else :
945                 for tokenname in tokentypes['mesh'] :
946                     obname = tokens[tokenname]['parent']
947                     # object and mesh naming :
948                     # if parent frame has several meshes : obname = meshname = mesh token name,
949                     # if parent frame has only one mesh  : obname = parent frame name, meshname =  mesh token name.
950                     if obname :
951                         meshcount = 0
952                         for child in getChilds(obname) :
953                             if tokens[child]['type'] == 'mesh' :
954                                 meshcount += 1
955                                 if meshcount == 2 :
956                                     obname = tokenname
957                                     break
958                     else : obname = tokenname
959
960                     ob = getMesh(obname,tokenname,show_geninfo)
961                     ob.matrix_world = global_matrix
962
963             print('done in %.2f\''%(time.clock()-start)) # ,end='\r')
964
965         else :
966             print('only .x files in text format are currently supported')
967             print('please share your file to make the importer evolve')
968
969
970         return {'FINISHED'}