use f.area where possible over python function and use len(mface) over len(mface.v)
[blender.git] / release / scripts / lightwave_import.py
1 #!BPY
2 """
3 Name: 'LightWave + Materials (.lwo)...'
4 Blender: 239
5 Group: 'Import'
6 Tooltip: 'Import LightWave Object File Format (.lwo)'
7 """
8
9 __author__ = "Alessandro Pirovano, Anthony D'Agostino (Scorpius)"
10 __url__ = ("blender", "elysiun",
11 "Anthony's homepage, http://www.redrival.com/scorpius", "Alessandro's homepage, http://uaraus.altervista.org")
12
13 importername = "lwo_import 0.2.2b"
14
15 # $Id$
16 #
17 # +---------------------------------------------------------+
18 # | Save your work before and after use.                    |
19 # | Please report any useful comment to:                    |
20 # | uaraus-dem@yahoo.it                                     |
21 # | Thanks                                                  |
22 # +---------------------------------------------------------+
23 # +---------------------------------------------------------+
24 # | Copyright (c) 2002 Anthony D'Agostino                   |
25 # | http://www.redrival.com/scorpius                        |
26 # | scorpius@netzero.com                                    |
27 # | April 21, 2002                                          |
28 # | Import Export Suite v0.5                                |
29 # +---------------------------------------------------------+
30 # | Read and write LightWave Object File Format (*.lwo)     |
31 # +---------------------------------------------------------+
32 # +---------------------------------------------------------+
33 # | Alessandro Pirovano tweaked starting on March 2005      |
34 # | http://uaraus.altervista.org                            |
35 # +---------------------------------------------------------+
36 # +----------------------------------------------------------
37 # | GPL license block
38 # |
39 # | This program is free software; you can redistribute it and/or modify
40 # | it under the terms of the GNU General Public License as published by
41 # | the Free Software Foundation; either version 2 of the License, or
42 # | (at your option) any later version.
43 # |
44 # | This program is distributed in the hope that it will be useful,
45 # | but WITHOUT ANY WARRANTY; without even the implied warranty of
46 # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
47 # | GNU General Public License for more details.
48 # |
49 # | You should have received a copy of the GNU General Public License
50 # | along with this program; if not, write to the Free Software
51 # | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
52 # +----------------------------------------------------------
53 # +---------------------------------------------------------+
54 # | Release log:                                            |
55 # | 0.2.1 : This code works with Blender 2.40 RC1           |
56 # |         modified material mode assignment to deal with  |
57 # |         Python API modification                         |
58 # |         Changed script license to GNU GPL               |
59 # | 0.2.0:  This code works with Blender 2.40a2 or up       |
60 # |         Major rewrite to deal with large meshes         |
61 # |         - 2 pass file parsing                           |
62 # |         - lower memory footprint                        |
63 # |           (as long as python gc allows)                 |
64 # |         2.40a2 - Removed subsurf settings patches=poly  |
65 # |         2.40a2 - Edge generation instead of 2vert faces |
66 # | 0.1.16: fixed (try 2) texture offset calculations       |
67 # |         added hint on axis mapping                      |
68 # |         added hint on texture blending mode             |
69 # |         added hint on texture transparency setting      |
70 # |         search images in original directory first       |
71 # |         fixed texture order application                 |
72 # | 0.1.15: added release log                               |
73 # |         fixed texture offset calculations (non-UV)      |
74 # |         fixed reverting vertex order in face generation |
75 # |         associate texture on game-engine settings       |
76 # |         vector math definitely based on mathutils       |
77 # |         search images in "Images" and "../Images" dir   |
78 # |         revised logging facility                        |
79 # |         fixed subsurf texture and material mappings     |
80 # | 0.1.14: patched missing mod_vector (not definitive)     |
81 # | 0.1.13: first public release                            |
82 # +---------------------------------------------------------+
83
84 #blender related import
85 import Blender
86
87 #iosuite related import
88 try: #new naming
89     import meshtools as my_meshtools
90 except ImportError: #fallback to the old one
91     print "using old mod_meshtools"
92     import mod_meshtools as my_meshtools
93
94 #python specific modules import
95 import struct, chunk, os, cStringIO, time, operator, copy
96
97 # ===========================================================
98 # === Utility Preamble ======================================
99 # ===========================================================
100
101 textname = "lwo_log"
102 type_list = type(list())
103 type_dict = type(dict())
104 #uncomment the following line to disable logging facility
105 #textname = None
106
107 # ===========================================================
108
109
110 # ===========================================================
111 # === Make sure it is a string ... deal with strange chars ==
112 # ===========================================================
113 def safestring(st):
114     myst = ""
115     for ll in xrange(len(st)):
116         if st[ll] < " ":
117             myst += "#"
118         else:
119             myst += st[ll]
120     return myst
121
122 class dotext:
123
124     _NO = 0    #use internal to class only
125     LOG = 1    #write only to LOG
126     CON = 2    #write to both LOG and CONSOLE
127
128     def __init__(self, tname, where=LOG):
129         self.dwhere = where #defaults on console only
130         if (tname==None):
131             print "*** not using text object to log script"
132             self.txtobj = None
133             return
134         tlist = Blender.Text.get()
135         for i in xrange(len(tlist)):
136             if (tlist[i].getName()==tname):
137                 tlist[i].clear()
138                 #print tname, " text object found and cleared!"
139                 self.txtobj = tlist[i]
140                 return
141         #print tname, " text object not found and created!"
142         self.txtobj = Blender.Text.New(tname)
143     # end def __init__
144
145     def write(self, wstring, maxlen=100):
146         if (self.txtobj==None): return
147         while (1):
148             ll = len(wstring)
149             if (ll>maxlen):
150                 self.txtobj.write((wstring[:maxlen]))
151                 self.txtobj.write("\n")
152                 wstring = (wstring[maxlen:])
153             else:
154                 self.txtobj.write(wstring)
155                 break
156     # end def write
157
158     def pstring(self, ppstring, where = _NO):
159         if where == dotext._NO: where = self.dwhere
160         if where == dotext.CON:
161             print ppstring
162         self.write(ppstring)
163         self.write("\n")
164     # end def pstring
165
166     def plist(self, pplist, where = _NO):
167         self.pprint ("list:[")
168         for pp in xrange(len(pplist)):
169             self.pprint ("[%d] -> %s" % (pp, pplist[pp]), where)
170         self.pprint ("]")
171     # end def plist
172
173     def pdict(self, pdict, where = _NO):
174         self.pprint ("dict:{", where)
175         for pp in pdict.iterkeys():
176             self.pprint ("[%s] -> %s" % (pp, pdict[pp]), where)
177         self.pprint ("}")
178     # end def pdict
179
180     def pprint(self, parg, where = _NO):
181         if parg == None:
182             self.pstring("_None_", where)
183         elif type(parg) == type_list:
184             self.plist(parg, where)
185         elif type(parg) == type_dict:
186             self.pdict(parg, where)
187         else:
188             self.pstring(safestring(str(parg)), where)
189     # end def pprint
190
191     def logcon(self, parg):
192         self.pprint(parg, dotext.CON)
193     # end def logcon
194 # endclass dotext
195
196 tobj=dotext(textname)
197 #uncomment the following line to log all messages on both console and logfile
198 #tobj=dotext(textname,dotext.CON)
199
200 def rlcopy(ll):
201     if type(ll) != type_list:
202         return ll
203     if ll == []:
204         return []
205     cpy = [rlcopy(ii) for ii in ll]
206     return cpy
207
208 # ===========================================================
209 # === Main read functions ===================================
210 # ===========================================================
211
212 # =============================
213 # === Read LightWave Format ===
214 # =============================
215 def read(filename):
216     global tobj
217
218     tobj.logcon ("#####################################################################")
219     tobj.logcon ("This is: %s" % importername)
220     tobj.logcon ("Importing file:")
221     tobj.logcon (filename)
222     tobj.pprint ("#####################################################################")
223
224     start = time.clock()
225     file = open(filename, "rb")
226
227     editmode = Blender.Window.EditMode()    # are we in edit mode?  If so ...
228     if editmode: Blender.Window.EditMode(0) # leave edit mode before getting the mesh    # === LWO header ===
229
230     form_id, form_size, form_type = struct.unpack(">4s1L4s",  file.read(12))
231     if (form_type == "LWOB"):
232         read_lwob(file, filename)
233     elif (form_type == "LWO2"):
234         read_lwo2(file, filename)
235     else:
236         tobj.logcon ("Can't read a file with the form_type: %s" %form_type)
237         return
238
239     Blender.Window.DrawProgressBar(1.0, "")    # clear progressbar
240     file.close()
241     end = time.clock()
242     seconds = " in %.2f %s" % (end-start, "seconds")
243     if form_type == "LWO2": fmt = " (v6.0 Format)"
244     if form_type == "LWOB": fmt = " (v5.5 Format)"
245     message = "Successfully imported " + os.path.basename(filename) + fmt + seconds
246     #my_meshtools.print_boxed(message)
247     tobj.pprint ("#####################################################################")
248     tobj.logcon (message)
249     tobj.logcon ("#####################################################################")
250     if editmode: Blender.Window.EditMode(1)  # optional, just being nice
251
252
253 # enddef read
254
255
256 # =================================
257 # === Read LightWave 5.5 format ===
258 # =================================
259 def read_lwob(file, filename):
260     global tobj
261
262     tobj.logcon("LightWave 5.5 format")
263     objname = os.path.splitext(os.path.basename(filename))[0]
264
265     while 1:
266         try:
267             lwochunk = chunk.Chunk(file)
268         except EOFError:
269             break
270         if lwochunk.chunkname == "LAYR":
271             objname = read_layr(lwochunk)
272         elif lwochunk.chunkname == "PNTS":                         # Verts
273             verts = read_verts(lwochunk)
274         elif lwochunk.chunkname == "POLS": # Faces v5.5
275             faces = read_faces_5(lwochunk)
276             my_meshtools.create_mesh(verts, faces, objname)
277         else:                                                       # Misc Chunks
278             lwochunk.skip()
279     return
280 # enddef read_lwob
281
282
283 # =============================
284 # === Read LightWave Format ===
285 # =============================
286 def read_lwo2(file, filename, typ="LWO2"):
287     global tobj
288
289     tobj.logcon("LightWave 6 (and above) format")
290
291     dir_part = Blender.sys.dirname(filename)
292     fname_part = Blender.sys.basename(filename)
293     ask_weird = 1
294
295     #first initialization of data structures
296     defaultname = os.path.splitext(fname_part)[0]
297     tag_list = []              #tag list: global for the whole file?
298     surf_list = []             #surf list: global for the whole file?
299     clip_list = []             #clip list: global for the whole file?
300     object_index = 0
301     object_list = None
302     objspec_list = None
303     # init value is: object_list = [[None, {}, [], [], {}, {}, 0, {}, {}]]
304     #0 - objname                    #original name
305     #1 - obj_dict = {TAG}           #objects created
306     #2 - verts = []                 #object vertexes
307     #3 - faces = []                 #object faces (associations poly -> vertexes)
308     #4 - obj_dim_dict = {TAG}       #tuples size and pos in local object coords - used for NON-UV mappings
309     #5 - polytag_dict = {TAG}       #tag to polygons mapping
310     #6 - patch_flag                 #0 = surf; 1 = patch (subdivision surface) - it was the image list
311     #7 - uvcoords_dict = {name}     #uvmap coordinates (mixed mode per vertex/per face)
312     #8 - facesuv_dict = {name}      #vmad only coordinates associations poly & vertex -> uv tuples
313
314     #pass 1: look in advance for materials
315     tobj.logcon ("#####################################################################")
316     tobj.logcon ("Starting Pass 1: hold on tight")
317     tobj.logcon ("#####################################################################")
318     while 1:
319         try:
320             lwochunk = chunk.Chunk(file)
321         except EOFError:
322             break
323         tobj.pprint(" ")
324         if lwochunk.chunkname == "TAGS":                         # Tags
325             tobj.pprint("---- TAGS")
326             tag_list.extend(read_tags(lwochunk))
327         elif lwochunk.chunkname == "SURF":                         # surfaces
328             tobj.pprint("---- SURF")
329             surf_list.append(read_surfs(lwochunk, surf_list, tag_list))
330         elif lwochunk.chunkname == "CLIP":                         # texture images
331             tobj.pprint("---- CLIP")
332             clip_list.append(read_clip(lwochunk, dir_part))
333             tobj.pprint("read total %s clips up to now" % len(clip_list))
334         else:                                                       # Misc Chunks
335             if ask_weird:
336                 ckname = safestring(lwochunk.chunkname)
337                 if "#" in ckname:
338                     choice = Blender.Draw.PupMenu("WARNING: file could be corrupted.%t|Import anyway|Give up")
339                     if choice != 1:
340                         tobj.logcon("---- %s: Maybe file corrupted. Terminated by user" % lwochunk.chunkname)
341                         return
342                     ask_weird = 0
343             tobj.pprint("---- %s: skipping (maybe later)" % lwochunk.chunkname)
344             lwochunk.skip()
345
346     #add default material for orphaned faces, if any
347     surf_list.append({'NAME': "_Orphans", 'g_MAT': Blender.Material.New("_Orphans")})
348
349     #pass 2: effectively generate objects
350     tobj.logcon ("#####################################################################")
351     tobj.logcon ("Pass 2: now for the hard part")
352     tobj.logcon ("#####################################################################")
353     file.seek(0)
354     # === LWO header ===
355     form_id, form_size, form_type = struct.unpack(">4s1L4s",  file.read(12))
356     if (form_type != "LWO2"):
357         tobj.logcon ("??? Inconsistent file type: %s" %form_type)
358         return
359     while 1:
360         try:
361             lwochunk = chunk.Chunk(file)
362         except EOFError:
363             break
364         tobj.pprint(" ")
365         if lwochunk.chunkname == "LAYR":
366             tobj.pprint("---- LAYR")
367             objname = read_layr(lwochunk)
368             tobj.pprint(objname)
369             if objspec_list != None: #create the object
370                 create_objects(clip_list, objspec_list, surf_list)
371                 update_material(clip_list, objspec_list, surf_list) #give it all the object
372             objspec_list = [objname, {}, [], [], {}, {}, 0, {}, {}]
373             object_index += 1
374         elif lwochunk.chunkname == "PNTS":                         # Verts
375             tobj.pprint("---- PNTS")
376             verts = read_verts(lwochunk)
377             objspec_list[2] = verts
378         elif lwochunk.chunkname == "VMAP":                         # MAPS (UV)
379             tobj.pprint("---- VMAP")
380             #objspec_list[7] = read_vmap(objspec_list[7], len(objspec_list[2]), lwochunk)
381             read_vmap(objspec_list[7], len(objspec_list[2]), lwochunk)
382         elif lwochunk.chunkname == "VMAD":                         # MAPS (UV) per-face
383             tobj.pprint("---- VMAD")
384             #objspec_list[7], objspec_list[8] = read_vmad(objspec_list[7], objspec_list[8], len(objspec_list[3]), len(objspec_list[2]), lwochunk)
385             read_vmad(objspec_list[7], objspec_list[8], len(objspec_list[3]), len(objspec_list[2]), lwochunk)
386         elif lwochunk.chunkname == "POLS": # Faces v6.0
387             tobj.pprint("-------- POLS(6)")
388             faces, flag = read_faces_6(lwochunk)
389             #flag is 0 for regular polygon, 1 for patches (= subsurf), 2 for anything else to be ignored
390             if flag<2:
391                 if objspec_list[3] != []:
392                     #create immediately the object
393                     create_objects(clip_list, objspec_list, surf_list)
394                     update_material(clip_list, objspec_list, surf_list) #give it all the object
395                     #update with new data
396                     objspec_list = [objspec_list[0],                  #update name
397                                     {},                               #init
398                                     objspec_list[2],                  #same vertexes
399                                     faces,                            #give it the new faces
400                                     {},                               #no need to copy - filled at runtime
401                                     {},                               #polygon tagging will follow
402                                     flag,                             #patch flag
403                                     objspec_list[7],                  #same uvcoords
404                                     {}]                               #no vmad mapping
405                     object_index += 1
406                 #end if already has a face list
407                 objspec_list[3] = faces
408                 objname = objspec_list[0]
409                 if objname == None:
410                     objname = defaultname
411             #end if processing a valid poly type
412         elif lwochunk.chunkname == "PTAG":                         # PTags
413             tobj.pprint("---- PTAG")
414             polytag_dict = read_ptags(lwochunk, tag_list)
415             for kk, ii in polytag_dict.iteritems(): objspec_list[5][kk] = ii
416         else:                                                       # Misc Chunks
417             tobj.pprint("---- %s: skipping (definitely!)" % lwochunk.chunkname)
418             lwochunk.skip()
419         #uncomment here to log data structure as it is built
420         #tobj.pprint(object_list)
421     #last object read
422     create_objects(clip_list, objspec_list, surf_list)
423     update_material(clip_list, objspec_list, surf_list) #give it all the object
424     objspec_list = None
425     surf_list = None
426     clip_list = None
427
428
429     tobj.pprint ("\n#####################################################################")
430     tobj.pprint("Found %d objects:" % object_index)
431     tobj.pprint ("#####################################################################")
432 # enddef read_lwo2
433
434
435
436
437
438
439 # ===========================================================
440 # === File reading routines =================================
441 # ===========================================================
442 # ==================
443 # === Read Verts ===
444 # ==================
445 def read_verts(lwochunk):
446     global tobj
447
448     data = cStringIO.StringIO(lwochunk.read())
449     numverts = lwochunk.chunksize/12
450     #$verts = []
451     verts = [None] * numverts
452     for i in xrange(numverts):
453         if not i%1000 and my_meshtools.show_progress:
454             Blender.Window.DrawProgressBar(float(i)/numverts, "Reading Verts")
455         x, y, z = struct.unpack(">fff", data.read(12))
456         verts[i] = (x, z, y)
457     tobj.pprint("read %d vertexes" % (i+1))
458     return verts
459 # enddef read_verts
460
461
462 # =================
463 # === Read Name ===
464 # =================
465 # modified to deal with odd lenght strings
466 def read_name(file):
467     name = ""
468     while 1:
469         char = file.read(1)
470         if char == "\0": break
471         else: name += char
472     len_name = len(name) + 1 #count the trailing zero
473     if len_name%2==1:
474         char = file.read(1) #remove zero padding to even lenght
475         len_name += 1
476     return name, len_name
477
478
479 # ==================
480 # === Read Layer ===
481 # ==================
482 def read_layr(lwochunk):
483     data = cStringIO.StringIO(lwochunk.read())
484     idx, flags = struct.unpack(">hh", data.read(4))
485     pivot = struct.unpack(">fff", data.read(12))
486     layer_name, discard = read_name(data)
487     if not layer_name: layer_name = "NoName"
488     return layer_name
489 # enddef read_layr
490
491
492 # ======================
493 # === Read Faces 5.5 ===
494 # ======================
495 def read_faces_5(lwochunk):
496     data = cStringIO.StringIO(lwochunk.read())
497     faces = []
498     i = 0
499     while i < lwochunk.chunksize:
500         if not i%1000 and my_meshtools.show_progress:
501            Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
502
503         '''
504         facev = []
505         numfaceverts, = struct.unpack(">H", data.read(2))
506         for j in xrange(numfaceverts):
507             index, = struct.unpack(">H", data.read(2))
508             facev.append(index)
509         '''
510         numfaceverts, = struct.unpack(">H", data.read(2))
511         facev = [struct.unpack(">H", data.read(2))[0] for j in xrange(numfaceverts)]
512         facev.reverse()
513         faces.append(facev)
514         surfaceindex, = struct.unpack(">H", data.read(2))
515         if surfaceindex < 0:
516             tobj.logcon ("***Error. Referencing uncorrect surface index")
517             return
518         i += (4+numfaceverts*2)
519     return faces
520
521
522 # ==================================
523 # === Read Variable-Length Index ===
524 # ==================================
525 def read_vx(data):
526     byte1, = struct.unpack(">B", data.read(1))
527     if byte1 != 0xFF:    # 2-byte index
528         byte2, = struct.unpack(">B", data.read(1))
529         index = byte1*256 + byte2
530         index_size = 2
531     else:                # 4-byte index
532         byte2, byte3, byte4 = struct.unpack(">3B", data.read(3))
533         index = byte2*65536 + byte3*256 + byte4
534         index_size = 4
535     return index, index_size
536
537
538 # ======================
539 # === Read uvmapping ===
540 # ======================
541 def read_vmap(uvcoords_dict, maxvertnum, lwochunk):
542     if maxvertnum == 0:
543         tobj.pprint ("Found VMAP but no vertexes to map!")
544         return uvcoords_dict
545     data = cStringIO.StringIO(lwochunk.read())
546     map_type = data.read(4)
547     if map_type != "TXUV":
548         tobj.pprint ("Reading VMAP: No Texture UV map Were Found. Map Type: %s" % map_type)
549         return uvcoords_dict
550     dimension, = struct.unpack(">H", data.read(2))
551     name, i = read_name(data) #i initialized with string lenght + zeros
552     tobj.pprint ("TXUV %d %s" % (dimension, name))
553     #note if there is already a VMAD it will be lost
554     #it is assumed that VMAD will follow the corresponding VMAP
555     try: #if uvcoords_dict.has_key(name):
556         my_uv_dict = uvcoords_dict[name]          #update existing
557     except: #else:
558         my_uv_dict = {}    #start a brand new: this could be made more smart
559     while (i < lwochunk.chunksize - 6):      #4+2 header bytes already read
560         vertnum, vnum_size = read_vx(data)
561         u, v = struct.unpack(">ff", data.read(8))
562         if vertnum >= maxvertnum:
563             tobj.pprint ("Hem: more uvmap than vertexes? ignoring uv data for vertex %d" % vertnum)
564         else:
565             my_uv_dict[vertnum] = (u, v)
566         i += 8 + vnum_size
567     #end loop on uv pairs
568     uvcoords_dict[name] = my_uv_dict
569     #this is a per-vertex mapping AND the uv tuple is vertex-ordered, so faces_uv is the same as faces
570     #return uvcoords_dict
571     return
572
573 # ========================
574 # === Read uvmapping 2 ===
575 # ========================
576 def read_vmad(uvcoords_dict, facesuv_dict, maxfacenum, maxvertnum, lwochunk):
577     if maxvertnum == 0 or maxfacenum == 0:
578         tobj.pprint ("Found VMAD but no vertexes to map!")
579         return uvcoords_dict, facesuv_dict
580     data = cStringIO.StringIO(lwochunk.read())
581     map_type = data.read(4)
582     if map_type != "TXUV":
583         tobj.pprint ("Reading VMAD: No Texture UV map Were Found. Map Type: %s" % map_type)
584         return uvcoords_dict, facesuv_dict
585     dimension, = struct.unpack(">H", data.read(2))
586     name, i = read_name(data) #i initialized with string lenght + zeros
587     tobj.pprint ("TXUV %d %s" % (dimension, name))
588     try: #if uvcoords_dict.has_key(name):
589         my_uv_dict = uvcoords_dict[name]          #update existing
590     except: #else:
591         my_uv_dict = {}    #start a brand new: this could be made more smart
592     my_facesuv_list = []
593     newindex = maxvertnum + 10 #why +10? Why not?
594     #end variable initialization
595     while (i < lwochunk.chunksize - 6):  #4+2 header bytes already read
596         vertnum, vnum_size = read_vx(data)
597         i += vnum_size
598         polynum, vnum_size = read_vx(data)
599         i += vnum_size
600         u, v = struct.unpack(">ff", data.read(8))
601         if polynum >= maxfacenum or vertnum >= maxvertnum:
602             tobj.pprint ("Hem: more uvmap than vertexes? ignorig uv data for vertex %d" % vertnum)
603         else:
604             my_uv_dict[newindex] = (u, v)
605             my_facesuv_list.append([polynum, vertnum, newindex])
606             newindex += 1
607         i += 8
608     #end loop on uv pairs
609     uvcoords_dict[name] = my_uv_dict
610     facesuv_dict[name] = my_facesuv_list
611     tobj.pprint ("updated %d vertexes data" % (newindex-maxvertnum-10))
612     return
613
614
615 # =================
616 # === Read tags ===
617 # =================
618 def read_tags(lwochunk):
619     data = cStringIO.StringIO(lwochunk.read())
620     tag_list = []
621     current_tag = ""
622     i = 0
623     while i < lwochunk.chunksize:
624         char = data.read(1)
625         if char == "\0":
626             tag_list.append(current_tag)
627             if (len(current_tag) % 2 == 0): char = data.read(1)
628             current_tag = ""
629         else:
630             current_tag += char
631         i += 1
632     tobj.pprint("read %d tags, list follows:" % len(tag_list))
633     tobj.pprint( tag_list)
634     return tag_list
635
636
637 # ==================
638 # === Read Ptags ===
639 # ==================
640 def read_ptags(lwochunk, tag_list):
641     data = cStringIO.StringIO(lwochunk.read())
642     polygon_type = data.read(4)
643     if polygon_type != "SURF":
644         tobj.pprint ("No Surf Were Found. Polygon Type: %s" % polygon_type)
645         return {}
646     ptag_dict = {}
647     i = 0
648     while(i < lwochunk.chunksize-4): #4 bytes polygon type already read
649         if not i%1000 and my_meshtools.show_progress:
650            Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading PTAGS")
651         poln, poln_size = read_vx(data)
652         i += poln_size
653         tag_index, = struct.unpack(">H", data.read(2))
654         if tag_index > (len(tag_list)):
655             tobj.pprint ("Reading PTAG: Surf belonging to undefined TAG: %d. Skipping" % tag_index)
656             return {}
657         i += 2
658         tag_key = tag_list[tag_index]
659         try: #if ptag_dict.has_key(tag_key):
660             ptag_dict[tag_list[tag_index]].append(poln)
661         except: #else:
662             ptag_dict[tag_list[tag_index]] = [poln]
663             
664     for i in ptag_dict.iterkeys():
665         tobj.pprint ("read %d polygons belonging to TAG %s" % (len(ptag_dict[i]), i))
666     return ptag_dict
667
668
669
670 # ==================
671 # === Read Clips ===
672 # ==================
673 def read_clip(lwochunk, dir_part):
674 # img, IMG, g_IMG refers to blender image objects
675 # ima, IMAG, g_IMAG refers to clip dictionary 'ID' entries: refer to blok and surf
676     clip_dict = {}
677     data = cStringIO.StringIO(lwochunk.read())
678     image_index, = struct.unpack(">L", data.read(4))
679     clip_dict['ID'] = image_index
680     i = 4
681     while(i < lwochunk.chunksize):
682         subchunkname, = struct.unpack("4s", data.read(4))
683         subchunklen, = struct.unpack(">H", data.read(2))
684         if subchunkname == "STIL":
685             tobj.pprint("-------- STIL")
686             clip_name, k = read_name(data)
687             #now split text independently from platform
688             #depend on the system where image was saved. NOT the one where the script is run
689             no_sep = "\\"
690             if Blender.sys.sep == no_sep: no_sep ="/"
691             if (no_sep in clip_name):
692                 clip_name = clip_name.replace(no_sep, Blender.sys.sep)
693             short_name = Blender.sys.basename(clip_name)
694             if (clip_name == "") or (short_name == ""):
695                 tobj.pprint ("Reading CLIP: Empty clip name not allowed. Skipping")
696                 discard = data.read(subchunklen-k)
697             clip_dict['NAME'] = clip_name
698             clip_dict['BASENAME'] = short_name
699         elif subchunkname == "XREF":                           #cross reference another image
700             tobj.pprint("-------- XREF")
701             image_index, = struct.unpack(">L", data.read(4))
702             clip_name, k = read_name(data)
703             clip_dict['NAME'] = clip_name
704             clip_dict['XREF'] = image_index
705         elif subchunkname == "NEGA":                           #negate texture effect
706             tobj.pprint("-------- NEGA")
707             n, = struct.unpack(">H", data.read(2))
708             clip_dict['NEGA'] = n
709         else:                                                       # Misc Chunks
710             tobj.pprint("-------- CLIP:%s: skipping" % subchunkname)
711             discard = data.read(subchunklen)
712         i = i + 6 + subchunklen
713     #end loop on surf chunks
714     tobj.pprint("read image:%s" % clip_dict)
715     if clip_dict.has_key('XREF'):
716         tobj.pprint("Cross-reference: no image pre-allocated.")
717         return clip_dict
718     #look for images
719     img = load_image("",clip_dict['NAME'])
720     if img == None:
721         tobj.pprint (  "***No image %s found: trying LWO file subdir" % clip_dict['NAME'])
722         img = load_image(dir_part,clip_dict['BASENAME'])
723     if img == None:
724         tobj.pprint (  "***No image %s found in directory %s: trying Images subdir" % (clip_dict['BASENAME'], dir_part))
725         img = load_image(dir_part+Blender.sys.sep+"Images",clip_dict['BASENAME'])
726     if img == None:
727         tobj.pprint (  "***No image %s found: trying alternate Images subdir" % clip_dict['BASENAME'])
728         img = load_image(dir_part+Blender.sys.sep+".."+Blender.sys.sep+"Images",clip_dict['BASENAME'])
729     if img == None:
730         tobj.pprint (  "***No image %s found: giving up" % clip_dict['BASENAME'])
731     #lucky we are: we have an image
732     tobj.pprint ("Image pre-allocated.")
733     clip_dict['g_IMG'] = img
734     return clip_dict
735
736
737 # ===========================
738 # === Read Surfaces Block ===
739 # ===========================
740 def read_surfblok(subchunkdata):
741     lenght = len(subchunkdata)
742     my_dict = {}
743     my_uvname = ""
744     data = cStringIO.StringIO(subchunkdata)
745     ##############################################################
746     # blok header sub-chunk
747     ##############################################################
748     subchunkname, = struct.unpack("4s", data.read(4))
749     subchunklen, = struct.unpack(">h", data.read(2))
750     accumulate_i = subchunklen + 6
751     if subchunkname != 'IMAP':
752         tobj.pprint("---------- SURF: BLOK: %s: block aborting" % subchunkname)
753         return {}, ""
754     tobj.pprint ("---------- IMAP")
755     ordinal, i = read_name(data)
756     my_dict['ORD'] = ordinal
757     #my_dict['g_ORD'] = -1
758     my_dict['ENAB'] = True
759     while(i < subchunklen): # ---------left 6------------------------- loop on header parameters
760         sub2chunkname, = struct.unpack("4s", data.read(4))
761         sub2chunklen, = struct.unpack(">h", data.read(2))
762         i = i + 6 + sub2chunklen
763         if sub2chunkname == "CHAN":
764             tobj.pprint("------------ CHAN")
765             sub2chunkname, = struct.unpack("4s", data.read(4))
766             my_dict['CHAN'] = sub2chunkname
767             sub2chunklen -= 4
768         elif sub2chunkname == "ENAB":                             #only present if is to be disabled
769             tobj.pprint("------------ ENAB")
770             ena, = struct.unpack(">h", data.read(2))
771             my_dict['ENAB'] = ena
772             sub2chunklen -= 2
773         elif sub2chunkname == "NEGA":                             #only present if is to be enabled
774             tobj.pprint("------------ NEGA")
775             ena, = struct.unpack(">h", data.read(2))
776             if ena == 1:
777                 my_dict['NEGA'] = ena
778             sub2chunklen -= 2
779         elif sub2chunkname == "OPAC":                             #only present if is to be disabled
780             tobj.pprint("------------ OPAC")
781             opa, = struct.unpack(">h", data.read(2))
782             s, = struct.unpack(">f", data.read(4))
783             envelope, env_size = read_vx(data)
784             my_dict['OPAC'] = opa
785             my_dict['OPACVAL'] = s
786             sub2chunklen -= 6
787         elif sub2chunkname == "AXIS":
788             tobj.pprint("------------ AXIS")
789             ena, = struct.unpack(">h", data.read(2))
790             my_dict['DISPLAXIS'] = ena
791             sub2chunklen -= 2
792         else:                                                       # Misc Chunks
793             tobj.pprint("------------ SURF: BLOK: IMAP: %s: skipping" % sub2chunkname)
794             discard = data.read(sub2chunklen)
795     #end loop on blok header subchunks
796     ##############################################################
797     # blok attributes sub-chunk
798     ##############################################################
799     subchunkname, = struct.unpack("4s", data.read(4))
800     subchunklen, = struct.unpack(">h", data.read(2))
801     accumulate_i += subchunklen + 6
802     if subchunkname != 'TMAP':
803         tobj.pprint("---------- SURF: BLOK: %s: block aborting" % subchunkname)
804         return {}, ""
805     tobj.pprint ("---------- TMAP")
806     i = 0
807     while(i < subchunklen): # -----------left 6----------------------- loop on header parameters
808         sub2chunkname, = struct.unpack("4s", data.read(4))
809         sub2chunklen, = struct.unpack(">h", data.read(2))
810         i = i + 6 + sub2chunklen
811         if sub2chunkname == "CNTR":
812             tobj.pprint("------------ CNTR")
813             x, y, z = struct.unpack(">fff", data.read(12))
814             envelope, env_size = read_vx(data)
815             my_dict['CNTR'] = [x, y, z]
816             sub2chunklen -= (12+env_size)
817         elif sub2chunkname == "SIZE":
818             tobj.pprint("------------ SIZE")
819             x, y, z = struct.unpack(">fff", data.read(12))
820             envelope, env_size = read_vx(data)
821             my_dict['SIZE'] = [x, y, z]
822             sub2chunklen -= (12+env_size)
823         elif sub2chunkname == "ROTA":
824             tobj.pprint("------------ ROTA")
825             x, y, z = struct.unpack(">fff", data.read(12))
826             envelope, env_size = read_vx(data)
827             my_dict['ROTA'] = [x, y, z]
828             sub2chunklen -= (12+env_size)
829         elif sub2chunkname == "CSYS":
830             tobj.pprint("------------ CSYS")
831             ena, = struct.unpack(">h", data.read(2))
832             my_dict['CSYS'] = ena
833             sub2chunklen -= 2
834         else:                                                       # Misc Chunks
835             tobj.pprint("------------ SURF: BLOK: TMAP: %s: skipping" % sub2chunkname)
836         if  sub2chunklen > 0:
837             discard = data.read(sub2chunklen)
838     #end loop on blok attributes subchunks
839     ##############################################################
840     # ok, now other attributes without sub_chunks
841     ##############################################################
842     while(accumulate_i < lenght): # ---------------------------------- loop on header parameters: lenght has already stripped the 6 bypes header
843         subchunkname, = struct.unpack("4s", data.read(4))
844         subchunklen, = struct.unpack(">H", data.read(2))
845         accumulate_i = accumulate_i + 6 + subchunklen
846         if subchunkname == "PROJ":
847             tobj.pprint("---------- PROJ")
848             p, = struct.unpack(">h", data.read(2))
849             my_dict['PROJ'] = p
850             subchunklen -= 2
851         elif subchunkname == "AXIS":
852             tobj.pprint("---------- AXIS")
853             a, = struct.unpack(">h", data.read(2))
854             my_dict['MAJAXIS'] = a
855             subchunklen -= 2
856         elif subchunkname == "IMAG":
857             tobj.pprint("---------- IMAG")
858             i, i_size = read_vx(data)
859             my_dict['IMAG'] = i
860             subchunklen -= i_size
861         elif subchunkname == "WRAP":
862             tobj.pprint("---------- WRAP")
863             ww, wh = struct.unpack(">hh", data.read(4))
864             #reduce width and height to just 1 parameter for both
865             my_dict['WRAP'] = max([ww,wh])
866             #my_dict['WRAPWIDTH'] = ww
867             #my_dict['WRAPHEIGHT'] = wh
868             subchunklen -= 4
869         elif subchunkname == "WRPW":
870             tobj.pprint("---------- WRPW")
871             w, = struct.unpack(">f", data.read(4))
872             my_dict['WRPW'] = w
873             envelope, env_size = read_vx(data)
874             subchunklen -= (env_size+4)
875         elif subchunkname == "WRPH":
876             tobj.pprint("---------- WRPH")
877             w, = struct.unpack(">f", data.read(4))
878             my_dict['WRPH'] = w
879             envelope, env_size = read_vx(data)
880             subchunklen -= (env_size+4)
881         elif subchunkname == "VMAP":
882             tobj.pprint("---------- VMAP")
883             vmp, i = read_name(data)
884             my_dict['VMAP'] = vmp
885             my_uvname = vmp
886             subchunklen -= i
887         else:                                                    # Misc Chunks
888             tobj.pprint("---------- SURF: BLOK: %s: skipping" % subchunkname)
889         if  subchunklen > 0:
890             discard = data.read(subchunklen)
891     #end loop on blok subchunks
892     return my_dict, my_uvname
893
894
895 # =====================
896 # === Read Surfaces ===
897 # =====================
898 def read_surfs(lwochunk, surf_list, tag_list):
899     my_dict = {}
900     data = cStringIO.StringIO(lwochunk.read())
901     surf_name, i = read_name(data)
902     parent_name, j = read_name(data)
903     i += j
904     if (surf_name == "") or not(surf_name in tag_list):
905         tobj.pprint ("Reading SURF: Actually empty surf name not allowed. Skipping")
906         return {}
907     if (parent_name != ""):
908         parent_index = [x['NAME'] for x in surf_list].count(parent_name)
909         if parent_index >0:
910             my_dict = surf_list[parent_index-1]
911     my_dict['NAME'] = surf_name
912     tobj.pprint ("Surface data for TAG %s" % surf_name)
913     while(i < lwochunk.chunksize):
914         subchunkname, = struct.unpack("4s", data.read(4))
915         subchunklen, = struct.unpack(">H", data.read(2))
916         i = i + 6 + subchunklen #6 bytes subchunk header
917         if subchunkname == "COLR":                             #color: mapped on color
918             tobj.pprint("-------- COLR")
919             r, g, b = struct.unpack(">fff", data.read(12))
920             envelope, env_size = read_vx(data)
921             my_dict['COLR'] = [r, g, b]
922             subchunklen -= (12+env_size)
923         elif subchunkname == "DIFF":                           #diffusion: mapped on reflection (diffuse shader)
924             tobj.pprint("-------- DIFF")
925             s, = struct.unpack(">f", data.read(4))
926             envelope, env_size = read_vx(data)
927             my_dict['DIFF'] = s
928             subchunklen -= (4+env_size)
929         elif subchunkname == "SPEC":                           #specularity: mapped to specularity (spec shader)
930             tobj.pprint("-------- SPEC")
931             s, = struct.unpack(">f", data.read(4))
932             envelope, env_size = read_vx(data)
933             my_dict['SPEC'] = s
934             subchunklen -= (4+env_size)
935         elif subchunkname == "REFL":                           #reflection: mapped on raymirror
936             tobj.pprint("-------- REFL")
937             s, = struct.unpack(">f", data.read(4))
938             envelope, env_size = read_vx(data)
939             my_dict['REFL'] = s
940             subchunklen -= (4+env_size)
941         elif subchunkname == "TRNL":                           #translucency: mapped on same param
942             tobj.pprint("-------- TRNL")
943             s, = struct.unpack(">f", data.read(4))
944             envelope, env_size = read_vx(data)
945             my_dict['TRNL'] = s
946             subchunklen -= (4+env_size)
947         elif subchunkname == "GLOS":                           #glossiness: mapped on specularity hardness (spec shader)
948             tobj.pprint("-------- GLOS")
949             s, = struct.unpack(">f", data.read(4))
950             envelope, env_size = read_vx(data)
951             my_dict['GLOS'] = s
952             subchunklen -= (4+env_size)
953         elif subchunkname == "TRAN":                           #transparency: inverted and mapped on alpha channel
954             tobj.pprint("-------- TRAN")
955             s, = struct.unpack(">f", data.read(4))
956             envelope, env_size = read_vx(data)
957             my_dict['TRAN'] = s
958             subchunklen -= (4+env_size)
959         elif subchunkname == "LUMI":                           #luminosity: mapped on emit channel
960             tobj.pprint("-------- LUMI")
961             s, = struct.unpack(">f", data.read(4))
962             envelope, env_size = read_vx(data)
963             my_dict['LUMI'] = s
964             subchunklen -= (4+env_size)
965         elif subchunkname == "GVAL":                           #glow: mapped on add channel
966             tobj.pprint("-------- GVAL")
967             s, = struct.unpack(">f", data.read(4))
968             envelope, env_size = read_vx(data)
969             my_dict['GVAL'] = s
970             subchunklen -= (4+env_size)
971         elif subchunkname == "SMAN":                           #smoothing angle
972             tobj.pprint("-------- SMAN")
973             s, = struct.unpack(">f", data.read(4))
974             my_dict['SMAN'] = s
975             subchunklen -= 4
976         elif subchunkname == "SIDE":                           #double sided?
977             tobj.pprint("-------- SIDE")                             #if 1 side do not define key
978             s, = struct.unpack(">H", data.read(2))
979             if s == 3:
980                 my_dict['SIDE'] = s
981             subchunklen -= 2
982         elif subchunkname == "RIND":                           #Refraction: mapped on IOR
983             tobj.pprint("-------- RIND")
984             s, = struct.unpack(">f", data.read(4))
985             envelope, env_size = read_vx(data)
986             my_dict['RIND'] = s
987             subchunklen -= (4+env_size)
988         elif subchunkname == "BLOK":                           #blocks
989             tobj.pprint("-------- BLOK")
990             rr, uvname = read_surfblok(data.read(subchunklen))
991             #paranoia setting: preventing adding an empty dict
992             if rr != {}:
993                 try:
994                     my_dict['BLOK'].append(rr)
995                 except:
996                     my_dict['BLOK'] = [rr]
997
998             if uvname != "":
999                 my_dict['UVNAME'] = uvname                            #theoretically there could be a number of them: only one used per surf
1000             if not(my_dict.has_key('g_IMAG')) and (rr.has_key('CHAN')) and (rr.has_key('OPAC')) and (rr.has_key('IMAG')):
1001                 if (rr['CHAN'] == 'COLR') and (rr['OPAC'] == 0):
1002                     my_dict['g_IMAG'] = rr['IMAG']                 #do not set anything, just save image object for later assignment
1003             subchunklen = 0 #force ending
1004         else:                                                       # Misc Chunks
1005             tobj.pprint("-------- SURF:%s: skipping" % subchunkname)
1006         if  subchunklen > 0:
1007             discard = data.read(subchunklen)
1008     #end loop on surf chunks
1009     try: #if my_dict.has_key('BLOK'):
1010        my_dict['BLOK'].reverse() #texture applied in reverse order with respect to reading from lwo
1011     except:
1012        pass
1013     #uncomment this if material pre-allocated by read_surf
1014     my_dict['g_MAT'] = Blender.Material.New(my_dict['NAME'])
1015     tobj.pprint("-> Material pre-allocated.")
1016     return my_dict
1017
1018
1019 # ===========================================================
1020 # === Generation Routines ===================================
1021 # ===========================================================
1022 # ==================================================
1023 # === Compute vector distance between two points ===
1024 # ==================================================
1025 def dist_vector (head, tail): #vector from head to tail
1026     return Blender.Mathutils.Vector([head[0] - tail[0], head[1] - tail[1], head[2] - tail[2]])
1027
1028
1029 # ================
1030 # === Find Ear ===
1031 # ================
1032 def find_ear(normal, list_dict, verts, face):
1033     nv = len(list_dict['MF'])
1034     #looping through vertexes trying to find an ear
1035     #most likely in case of panic
1036     mlc = 0
1037     mla = 1
1038     mlb = 2
1039
1040     for c in xrange(nv):
1041         a = (c+1) % nv; b = (a+1) % nv
1042
1043         if list_dict['P'][a] > 0.0: #we have to start from a convex vertex
1044         #if (list_dict['P'][a] > 0.0) and (list_dict['P'][b] <= 0.0): #we have to start from a convex vertex
1045             mlc = c
1046             mla = a
1047             mlb = b
1048             #tobj.pprint ("## mmindex: %s, %s, %s  'P': %s, %s, %s" % (c, a, b, list_dict['P'][c],list_dict['P'][a],list_dict['P'][b]))
1049             #tobj.pprint ("   ok, this one passed")
1050             concave = 0
1051             concave_inside = 0
1052             for xx in xrange(nv): #looking for concave vertex
1053                 if (list_dict['P'][xx] <= 0.0) and (xx != b) and (xx != c): #cannot be a: it's convex
1054                     #ok, found concave vertex
1055                     concave = 1
1056                     #a, b, c, xx are all meta-meta vertex indexes
1057                     mva = list_dict['MF'][a] #meta-vertex-index
1058                     mvb = list_dict['MF'][b]
1059                     mvc = list_dict['MF'][c]
1060                     mvxx = list_dict['MF'][xx]
1061                     va = face[mva] #vertex
1062                     vb = face[mvb]
1063                     vc = face[mvc]
1064                     vxx = face[mvxx]
1065
1066                     #Distances
1067                     d_ac_v = list_dict['D'][c]
1068                     d_ba_v = list_dict['D'][a]
1069                     d_cb_v = dist_vector(verts[vc], verts[vb])
1070
1071                     #distance from triangle points
1072                     d_xxa_v = dist_vector(verts[vxx], verts[va])
1073                     d_xxb_v = dist_vector(verts[vxx], verts[vb])
1074                     d_xxc_v = dist_vector(verts[vxx], verts[vc])
1075
1076                     #normals
1077                     n_xxa_v = Blender.Mathutils.CrossVecs(d_ba_v, d_xxa_v)
1078                     n_xxb_v = Blender.Mathutils.CrossVecs(d_cb_v, d_xxb_v)
1079                     n_xxc_v = Blender.Mathutils.CrossVecs(d_ac_v, d_xxc_v)
1080
1081                     #how are oriented the normals?
1082                     p_xxa_v = Blender.Mathutils.DotVecs(normal, n_xxa_v)
1083                     p_xxb_v = Blender.Mathutils.DotVecs(normal, n_xxb_v)
1084                     p_xxc_v = Blender.Mathutils.DotVecs(normal, n_xxc_v)
1085
1086                     #if normals are oriented all to same directions - so it is insida
1087                     if ((p_xxa_v > 0.0) and (p_xxb_v > 0.0) and (p_xxc_v > 0.0)) or ((p_xxa_v <= 0.0) and (p_xxb_v <= 0.0) and (p_xxc_v <= 0.0)):
1088                         #print "vertex %d: concave inside" % xx
1089                         concave_inside = 1
1090                         break
1091                 #endif found a concave vertex
1092             #end loop looking for concave vertexes
1093             if (concave == 0) or (concave_inside == 0):
1094                 #no concave vertexes in polygon (should not be): return immediately
1095                 #looped all concave vertexes and no one inside found
1096                 return [c, a, b]
1097         #no convex vertex, try another one
1098     #end loop to find a suitable base vertex for ear
1099     #looped all candidate ears and find no-one suitable
1100     tobj.pprint ("Reducing face: no valid ear found to reduce!")
1101     return [mlc, mla, mlb] #uses most likely
1102
1103
1104
1105
1106 # ====================
1107 # === Reduce Faces ===
1108 # ====================
1109 # http://www-cgrl.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/cutting_ears.html per l'import
1110 def reduce_face(verts, face):
1111     nv = len (face)
1112     if nv == 3: return [[0,1,2]] #trivial decomposition list
1113     list_dict = {}
1114     #meta-vertex indexes
1115     list_dict['MF'] = range(nv) # these are meta-vertex-indexes
1116     list_dict['D'] = [None] * nv
1117     list_dict['X'] = [None] * nv
1118     list_dict['P'] = [None] * nv
1119     #list of distances
1120     for mvi in list_dict['MF']:
1121         #vector between two vertexes
1122         mvi_hiend = (mvi+1) % nv      #last-to-first
1123         vi_hiend = face[mvi_hiend] #vertex
1124         vi = face[mvi]
1125         list_dict['D'][mvi] = dist_vector(verts[vi_hiend], verts[vi])
1126     #list of cross products - normals evaluated into vertexes
1127     for vi in xrange(nv):
1128         list_dict['X'][vi] = Blender.Mathutils.CrossVecs(list_dict['D'][vi], list_dict['D'][vi-1])
1129     my_face_normal = Blender.Mathutils.Vector([list_dict['X'][0][0], list_dict['X'][0][1], list_dict['X'][0][2]])
1130     #list of dot products
1131     list_dict['P'][0] = 1.0
1132     for vi in xrange(1, nv):
1133         list_dict['P'][vi] = Blender.Mathutils.DotVecs(my_face_normal, list_dict['X'][vi])
1134     #is there at least one concave vertex?
1135     #one_concave = reduce(lambda x, y: (x) or (y<=0.0), list_dict['P'], 0)
1136     one_concave = reduce(lambda x, y: (x) + (y<0.0), list_dict['P'], 0)
1137     decomposition_list = []
1138
1139     while 1:
1140         if nv == 3: break
1141         if one_concave:
1142             #look for triangle
1143             ct = find_ear(my_face_normal, list_dict, verts, face)
1144             mv0 = list_dict['MF'][ct[0]] #meta-vertex-index
1145             mv1 = list_dict['MF'][ct[1]]
1146             mv2 = list_dict['MF'][ct[2]]
1147             #add the triangle to output list
1148             decomposition_list.append([mv0, mv1, mv2])
1149             #update data structures removing remove middle vertex from list
1150             #distances
1151             v0 = face[mv0] #vertex
1152             v1 = face[mv1]
1153             v2 = face[mv2]
1154             list_dict['D'][ct[0]] = dist_vector(verts[v2], verts[v0])
1155             #cross products
1156             list_dict['X'][ct[0]] = Blender.Mathutils.CrossVecs(list_dict['D'][ct[0]], list_dict['D'][ct[0]-1])
1157             list_dict['X'][ct[2]] = Blender.Mathutils.CrossVecs(list_dict['D'][ct[2]], list_dict['D'][ct[0]])
1158             #list of dot products
1159             list_dict['P'][ct[0]] = Blender.Mathutils.DotVecs(my_face_normal, list_dict['X'][ct[0]])
1160             list_dict['P'][ct[2]] = Blender.Mathutils.DotVecs(my_face_normal, list_dict['X'][ct[2]])
1161             #physical removal
1162             list_dict['MF'].pop(ct[1])
1163             list_dict['D'].pop(ct[1])
1164             list_dict['X'].pop(ct[1])
1165             list_dict['P'].pop(ct[1])
1166             one_concave = reduce(lambda x, y: (x) or (y<0.0), list_dict['P'], 0)
1167             nv -=1
1168         else: #here if no more concave vertexes
1169             if nv == 4: break  #quads only if no concave vertexes
1170             decomposition_list.append([list_dict['MF'][0], list_dict['MF'][1], list_dict['MF'][2]])
1171             #physical removal
1172             list_dict['MF'].pop(1)
1173             nv -=1
1174     #end while there are more my_face to triangulate
1175     decomposition_list.append(list_dict['MF'])
1176     return decomposition_list
1177
1178
1179 # =========================
1180 # === Recalculate Faces ===
1181 # =========================
1182
1183 def get_uvface(complete_list, facenum):
1184     # extract from the complete list only vertexes of the desired polygon
1185     my_facelist = []
1186     for elem in complete_list:
1187         if elem[0] == facenum:
1188             my_facelist.append(elem)
1189     return my_facelist
1190
1191 def get_newindex(polygon_list, vertnum):
1192     # extract from the polygon list the new index associated to a vertex
1193     if polygon_list == []:
1194         return -1
1195     for elem in polygon_list:
1196         if elem[1] == vertnum:
1197             return elem[2]
1198     #tobj.pprint("WARNING: expected vertex %s for polygon %s. Polygon_list dump follows" % (vertnum, polygon_list[0][0]))
1199     #tobj.pprint(polygon_list)
1200     return -1
1201
1202 def get_surf(surf_list, cur_tag):
1203     for elem in surf_list:
1204         if elem['NAME'] == cur_tag:
1205             return elem
1206     return {}
1207
1208
1209
1210 # ==========================================
1211 # === Revert list (keeping first vertex) ===
1212 # ==========================================
1213 def revert (llist):
1214     #different flavors: the reverse one is the one that works better
1215     #rhead = [llist[0]]
1216     #rtail = llist[1:]
1217     #rhead.extend(rtail)
1218     #return rhead
1219     #--------------
1220     rhead=rlcopy(llist)
1221     rhead.reverse()
1222     return rhead
1223     #--------------
1224     #return llist
1225
1226
1227 # ====================================
1228 # === Modified Create Blender Mesh ===
1229 # ====================================
1230 def my_create_mesh(clip_list, surf, objspec_list, current_facelist, objname, not_used_faces):
1231     #take the needed faces and update the not-used face list
1232     complete_vertlist = objspec_list[2]
1233     complete_facelist = objspec_list[3]
1234     uvcoords_dict = objspec_list[7]
1235     facesuv_dict = objspec_list[8]
1236     vertex_map = {} #implementation as dict
1237     cur_ptag_faces = []
1238     cur_ptag_faces_indexes = []
1239     maxface = len(complete_facelist)
1240     for ff in current_facelist:
1241         if ff >= maxface:
1242             tobj.logcon("Non existent face addressed: Giving up with this object")
1243             return None, not_used_faces              #return the created object
1244         cur_face = complete_facelist[ff]
1245         cur_ptag_faces_indexes.append(ff)
1246         if not_used_faces != []: not_used_faces[ff] = -1
1247         for vv in cur_face: vertex_map[vv] = 1
1248     #end loop on faces
1249     store_edge = 0
1250
1251     msh = Blender.NMesh.GetRaw()
1252     # Name the Object
1253     if not my_meshtools.overwrite_mesh_name:
1254         objname = my_meshtools.versioned_name(objname)
1255     Blender.NMesh.PutRaw(msh, objname)    # Name the Mesh
1256     obj = Blender.Object.GetSelected()[0]
1257     obj.name=objname
1258     # Associate material and mesh properties => from create materials
1259     msh = obj.getData()
1260     mat_index = len(msh.getMaterials(1))
1261     mat = None
1262     if surf.has_key('g_MAT'):
1263         mat = surf['g_MAT']
1264         msh.addMaterial(mat)
1265     msh.mode |= Blender.NMesh.Modes.AUTOSMOOTH #smooth it anyway
1266     if surf.has_key('SMAN'):
1267         #not allowed mixed mode mesh (all the mesh is smoothed and all with the same angle)
1268         #only one smoothing angle will be active! => take the max one
1269         s = int(surf['SMAN']/3.1415926535897932384626433832795*180.0)     #lwo in radians - blender in degrees
1270         if msh.getMaxSmoothAngle() < s: msh.setMaxSmoothAngle(s)
1271
1272     img = None
1273     if surf.has_key('g_IMAG'):
1274         ima = lookup_imag(clip_list, surf['g_IMAG'])
1275         if ima != None:
1276             img = ima['g_IMG']
1277
1278     #uv_flag = ((surf.has_key('UVNAME')) and (uvcoords_dict.has_key(surf['UVNAME'])) and (img != None))
1279     uv_flag = ((surf.has_key('UVNAME')) and (uvcoords_dict.has_key(surf['UVNAME'])))
1280
1281     if uv_flag:        #assign uv-data; settings at mesh level
1282         msh.hasFaceUV(1)
1283     msh.update(1)
1284
1285     tobj.pprint ("\n#===================================================================#")
1286     tobj.pprint("Processing Object: %s" % objname)
1287     tobj.pprint ("#===================================================================#")
1288
1289     jj = 0
1290     vertlen = len(vertex_map)
1291     maxvert = len(complete_vertlist)
1292     for i in vertex_map.iterkeys():
1293         if not jj%1000 and my_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/vertlen, "Generating Verts")
1294         if i >= maxvert:
1295             tobj.logcon("Non existent vertex addressed: Giving up with this object")
1296             return obj, not_used_faces              #return the created object
1297         x, y, z = complete_vertlist[i]
1298         msh.verts.append(Blender.NMesh.Vert(x, y, z))
1299         vertex_map[i] = jj
1300         jj += 1
1301     #end sweep over vertexes
1302     
1303     ALPHA_FACE_MODE = (surf.has_key('TRAN') and mat.getAlpha()<1.0)
1304     #append faces
1305     jj = 0
1306     for i in cur_ptag_faces_indexes:
1307         if not jj%1000 and my_meshtools.show_progress: Blender.Window.DrawProgressBar(float(jj)/len(cur_ptag_faces_indexes), "Generating Faces")
1308         cur_face = complete_facelist[i]
1309         numfaceverts = len(cur_face)
1310         vmad_list = []    #empty VMAD in any case
1311         if uv_flag:    #settings at original face level
1312             if facesuv_dict.has_key(surf['UVNAME']): #yes = has VMAD; no = has VMAP only
1313                 vmad_list = get_uvface(facesuv_dict[surf['UVNAME']],i)  #this for VMAD
1314
1315         if numfaceverts == 2:
1316             #This is not a face is an edge
1317             store_edge = 1
1318             if msh.edges == None:  #first run
1319                 msh.addEdgeData()
1320             #rev_face = revert(cur_face)
1321             i1 = vertex_map[cur_face[1]]
1322             i2 = vertex_map[cur_face[0]]
1323             ee = msh.addEdge(msh.verts[i1],msh.verts[i2])
1324             ee.flag |= Blender.NMesh.EdgeFlags.EDGEDRAW
1325             ee.flag |= Blender.NMesh.EdgeFlags.EDGERENDER
1326
1327         elif numfaceverts == 3:
1328             #This face is a triangle skip face reduction
1329             face = Blender.NMesh.Face()
1330             msh.faces.append(face)
1331             # Associate face properties => from create materials
1332             if mat != None: face.materialIndex = mat_index
1333             face.smooth = 1 #smooth it anyway
1334
1335             #rev_face = revert(cur_face)
1336             rev_face = [cur_face[2], cur_face[1], cur_face[0]]
1337
1338             for vi in rev_face:
1339                 index = vertex_map[vi]
1340                 face.v.append(msh.verts[index])
1341
1342                 if uv_flag:
1343                     ni = get_newindex(vmad_list, vi)
1344                     if ni > -1:
1345                         uv_index = ni
1346                     else: #VMAP - uses the same criteria as face
1347                         uv_index = vi
1348                     try: #if uvcoords_dict[surf['UVNAME']].has_key(uv_index):
1349                         uv_tuple = uvcoords_dict[surf['UVNAME']][uv_index]
1350                     except: #else:
1351                         uv_tuple = (0,0)
1352                     face.uv.append(uv_tuple)
1353
1354             if uv_flag and img != None:
1355                 face.mode |= Blender.NMesh.FaceModes['TEX']
1356                 face.image = img
1357                 face.mode |= Blender.NMesh.FaceModes.TWOSIDE                  #set it anyway
1358                 face.transp = Blender.NMesh.FaceTranspModes['SOLID']
1359                 face.flag = Blender.NMesh.FaceTranspModes['SOLID']
1360                 #if surf.has_key('SIDE'):
1361                 #    msh.faces[f].mode |= Blender.NMesh.FaceModes.TWOSIDE             #set it anyway
1362                 if ALPHA_FACE_MODE:
1363                     face.transp = Blender.NMesh.FaceTranspModes['ALPHA']
1364
1365         elif numfaceverts > 3:
1366             #Reduce all the faces with more than 3 vertexes (& test if the quad is concave .....)
1367
1368             meta_faces = reduce_face(complete_vertlist, cur_face)        # Indices of triangles.
1369             for mf in meta_faces:
1370                 face = Blender.NMesh.Face()
1371                 msh.faces.append(face)
1372
1373                 if len(mf) == 3: #triangle
1374                     #rev_face = revert([cur_face[mf[0]], cur_face[mf[1]], cur_face[mf[2]]])
1375                     rev_face = [cur_face[mf[2]], cur_face[mf[1]], cur_face[mf[0]]]
1376                 else:        #quads
1377                     #rev_face = revert([cur_face[mf[0]], cur_face[mf[1]], cur_face[mf[2]], cur_face[mf[3]]])
1378                     rev_face = [cur_face[mf[3]], cur_face[mf[2]], cur_face[mf[1]], cur_face[mf[0]]]
1379
1380                 # Associate face properties => from create materials
1381                 if mat != None: face.materialIndex = mat_index
1382                 face.smooth = 1 #smooth it anyway
1383
1384                 for vi in rev_face:
1385                     index = vertex_map[vi]
1386                     face.v.append(msh.verts[index])
1387
1388                     if uv_flag:
1389                         ni = get_newindex(vmad_list, vi)
1390                         if ni > -1:
1391                             uv_index = ni
1392                         else: #VMAP - uses the same criteria as face
1393                             uv_index = vi
1394                         try: #if uvcoords_dict[surf['UVNAME']].has_key(uv_index):
1395                             uv_tuple = uvcoords_dict[surf['UVNAME']][uv_index]
1396                         except: #else:
1397                             uv_tuple = (0,0)
1398                         face.uv.append(uv_tuple)
1399
1400                 if uv_flag and img != None:
1401                     face.mode |= Blender.NMesh.FaceModes['TEX']
1402                     face.image = img
1403                     face.mode |= Blender.NMesh.FaceModes.TWOSIDE                  #set it anyway
1404                     face.transp = Blender.NMesh.FaceTranspModes['SOLID']
1405                     face.flag = Blender.NMesh.FaceTranspModes['SOLID']
1406                     #if surf.has_key('SIDE'):
1407                     #    msh.faces[f].mode |= Blender.NMesh.FaceModes.TWOSIDE             #set it anyway
1408                     if ALPHA_FACE_MODE:
1409                         face.transp = Blender.NMesh.FaceTranspModes['ALPHA']
1410
1411         jj += 1
1412
1413     if not(uv_flag):        #clear eventual UV data
1414         msh.hasFaceUV(0)
1415     msh.update(1,store_edge)
1416     Blender.Redraw()
1417     return obj, not_used_faces              #return the created object
1418
1419
1420 # ============================================
1421 # === Set Subsurf attributes on given mesh ===
1422 # ============================================
1423 def set_subsurf(obj):
1424     msh = obj.getData()
1425     msh.setSubDivLevels([2, 2])
1426     #does not work any more in 2.40 alpha 2
1427     #msh.mode |= Blender.NMesh.Modes.SUBSURF
1428     if msh.edges != None:
1429         msh.update(1,1)
1430     else:
1431         msh.update(1)
1432     obj.makeDisplayList()
1433     return
1434
1435
1436 # =================================
1437 # === object size and dimension ===
1438 # =================================
1439 def obj_size_pos(obj):
1440     bbox = obj.getBoundBox()
1441     bbox_min = map(lambda *row: min(row), *bbox) #transpose & get min
1442     bbox_max = map(lambda *row: max(row), *bbox) #transpose & get max
1443     obj_size = (bbox_max[0]-bbox_min[0], bbox_max[1]-bbox_min[1], bbox_max[2]-bbox_min[2])
1444     obj_pos = ( (bbox_max[0]+bbox_min[0]) / 2, (bbox_max[1]+bbox_min[1]) / 2, (bbox_max[2]+bbox_min[2]) / 2)
1445     return (obj_size, obj_pos)
1446
1447
1448 # =========================
1449 # === Create the object ===
1450 # =========================
1451 def create_objects(clip_list, objspec_list, surf_list):
1452     nf = len(objspec_list[3])
1453     not_used_faces = range(nf)
1454     ptag_dict = objspec_list[5]
1455     obj_dict = {}  #links tag names to object, used for material assignments
1456     obj_dim_dict = {}
1457     obj_list = []  #have it handy for parent association
1458     middlechar = "+"
1459     endchar = ""
1460     if (objspec_list[6] == 1):
1461         middlechar = endchar = "#"
1462     for cur_tag in ptag_dict.iterkeys():
1463         if ptag_dict[cur_tag] != []:
1464             cur_surf = get_surf(surf_list, cur_tag)
1465             cur_obj, not_used_faces=  my_create_mesh(clip_list, cur_surf, objspec_list, ptag_dict[cur_tag], objspec_list[0][:9]+middlechar+cur_tag[:9], not_used_faces)
1466             #does not work any more in 2.40 alpha 2
1467             #if objspec_list[6] == 1:
1468             #    set_subsurf(cur_obj)
1469             if cur_obj != None:
1470                 obj_dict[cur_tag] = cur_obj
1471                 obj_dim_dict[cur_tag] = obj_size_pos(cur_obj)
1472                 obj_list.append(cur_obj)
1473     #end loop on current group
1474     #and what if some faces not used in any named PTAG? get rid of unused faces
1475     orphans = []
1476     for tt in not_used_faces:
1477         if tt > -1: orphans.append(tt)
1478     #end sweep on unused face list
1479     not_used_faces = None
1480     if orphans != []:
1481         cur_surf = get_surf(surf_list, "_Orphans")
1482         cur_obj, not_used_faces = my_create_mesh(clip_list, cur_surf, objspec_list, orphans, objspec_list[0][:9]+middlechar+"Orphans", [])
1483         if cur_obj != None:
1484             if objspec_list[6] == 1:
1485                 set_subsurf(cur_obj)
1486             obj_dict["_Orphans"] = cur_obj
1487             obj_dim_dict["_Orphans"] = obj_size_pos(cur_obj)
1488             obj_list.append(cur_obj)
1489     objspec_list[1] = obj_dict
1490     objspec_list[4] = obj_dim_dict
1491     scene = Blender.Scene.GetCurrent ()                   # get the current scene
1492     ob = Blender.Object.New ('Empty', objspec_list[0]+endchar)    # make empty object
1493     scene.link (ob)                                       # link the object into the scene
1494     ob.makeParent(obj_list, 1, 0)                         # set the root for created objects (no inverse, update scene hyerarchy (slow))
1495     Blender.Redraw()
1496     return
1497
1498
1499 # =====================
1500 # === Load an image ===
1501 # =====================
1502 #extensively search for image name
1503 def load_image(dir_part, name):
1504     img = None
1505     nname = Blender.sys.splitext(name)
1506     lname = [c.lower() for c in nname]
1507     ext_list = []
1508     if lname[1] != nname[1]:
1509         ext_list.append(lname[1])
1510     ext_list.extend(['.tga', '.png', '.jpg', '.gif', '.bmp'])  #order from best to worst (personal judgement) bmp last cause of nasty bug
1511     #first round: original "case"
1512     current = Blender.sys.join(dir_part, name)
1513     name_list = [current]
1514     name_list.extend([Blender.sys.makename(current, ext) for ext in ext_list])
1515     #second round: lower "case"
1516     if lname[0] != nname[0]:
1517         current = Blender.sys.join(dir_part, lname[0])
1518         name_list.extend([Blender.sys.makename(current, ext) for ext in ext_list])
1519     for nn in name_list:
1520         if Blender.sys.exists(nn) == 1:
1521             break
1522     try:
1523         img = Blender.Image.Load(nn)
1524         return img
1525     except IOError:
1526         return None
1527
1528
1529 # ===========================================
1530 # === Lookup for image index in clip_list ===
1531 # ===========================================
1532 def lookup_imag(clip_list,ima_id):
1533     for ii in clip_list:
1534         if ii['ID'] == ima_id:
1535             if ii.has_key('XREF'):
1536                 #cross reference - recursively look for images
1537                 return lookup_imag(clip_list, ii['XREF'])
1538             else:
1539                 return ii
1540     return None
1541
1542
1543 # ===================================================
1544 # === Create and assign image mapping to material ===
1545 # ===================================================
1546 def create_blok(surf, mat, clip_list, obj_size, obj_pos):
1547
1548     def output_size_ofs(size, pos, blok):
1549         #just automate repetitive task
1550         c_map = [0,1,2]
1551         c_map_txt = ["    X--", "    -Y-", "    --Z"]
1552         if blok['MAJAXIS'] == 0:
1553             c_map = [1,2,0]
1554         if blok['MAJAXIS'] == 2:
1555             c_map = [0,2,1]
1556         tobj.pprint ("!!!axis mapping:")
1557         for mp in c_map: tobj.pprint (c_map_txt[mp])
1558
1559         s = ["1.0 (Forced)"] * 3
1560         o = ["0.0 (Forced)"] * 3
1561         if blok['SIZE'][0] > 0.0:          #paranoia controls
1562             s[0] = "%.5f" % (size[0]/blok['SIZE'][0])
1563             o[0] = "%.5f" % ((blok['CNTR'][0]-pos[0])/blok['SIZE'][0])
1564         if blok['SIZE'][1] > 0.0:
1565             s[2] = "%.5f" % (size[2]/blok['SIZE'][1])
1566             o[2] = "%.5f" % ((blok['CNTR'][1]-pos[2])/blok['SIZE'][1])
1567         if blok['SIZE'][2] > 0.0:
1568             s[1] = "%.5f" % (size[1]/blok['SIZE'][2])
1569             o[1] = "%.5f" % ((blok['CNTR'][2]-pos[1])/blok['SIZE'][2])
1570         tobj.pprint ("!!!texture size and offsets:")
1571         tobj.pprint ("    sizeX = %s; sizeY = %s; sizeZ = %s" % (s[c_map[0]], s[c_map[1]], s[c_map[2]]))
1572         tobj.pprint ("    ofsX = %s; ofsY = %s; ofsZ = %s" % (o[c_map[0]], o[c_map[1]], o[c_map[2]]))
1573         return
1574
1575     ti = 0
1576     for blok in surf['BLOK']:
1577         tobj.pprint ("#...................................................................#")
1578         tobj.pprint ("# Processing texture block no.%s for surf %s" % (ti,surf['NAME']))
1579         tobj.pprint ("#...................................................................#")
1580         tobj.pdict (blok)
1581         if ti > 9: break                                    #only 8 channels 0..7 allowed for texture mapping
1582         if not blok['ENAB']:
1583             tobj.pprint (  "***Image is not ENABled! Quitting this block")
1584             break
1585         if not(blok.has_key('IMAG')):
1586             tobj.pprint (  "***No IMAGe for this block? Quitting")
1587             break                 #extract out the image index within the clip_list
1588         tobj.pprint ("looking for image number %d" % blok['IMAG'])
1589         ima = lookup_imag(clip_list, blok['IMAG'])
1590         if ima == None:
1591             tobj.pprint (  "***Block index image not within CLIP list? Quitting Block")
1592             break                              #safety check (paranoia setting)
1593         img = ima['g_IMG']
1594         if img == None:
1595             tobj.pprint ("***Failed to pre-allocate image %s found: giving up" % ima['BASENAME'])
1596             break
1597         tname = str(ima['ID'])
1598         if blok.has_key('CHAN'):
1599             tname = tname + "+" + blok['CHAN']
1600         newtex = Blender.Texture.New(tname)
1601         newtex.setType('Image')                 # make it an image texture
1602         newtex.image = img
1603         #how does it extends beyond borders
1604         if blok.has_key('WRAP'):
1605             if (blok['WRAP'] == 3) or (blok['WRAP'] == 2):
1606                 newtex.setExtend('Extend')
1607             elif (blok['WRAP'] == 1):
1608                 newtex.setExtend('Repeat')
1609             elif (blok['WRAP'] == 0):
1610                 newtex.setExtend('Clip')
1611         tobj.pprint ("generated texture %s" % tname)
1612
1613         blendmode_list = ['Mix',
1614                          'Subtractive',
1615                          'Difference',
1616                          'Multiply',
1617                          'Divide',
1618                          'Mix (CalcAlpha already set; try setting Stencil!',
1619                          'Texture Displacement',
1620                          'Additive']
1621         set_blendmode = 7 #default additive
1622         if blok.has_key('OPAC'):
1623             set_blendmode = blok['OPAC']
1624         if set_blendmode == 5: #transparency
1625             newtex.imageFlags |= Blender.Texture.ImageFlags.CALCALPHA
1626         tobj.pprint ("!!!Set Texture -> MapTo -> Blending Mode = %s" % blendmode_list[set_blendmode])
1627
1628         set_dvar = 1.0
1629         if blok.has_key('OPACVAL'):
1630             set_dvar = blok['OPACVAL']
1631
1632         #MapTo is determined by CHAN parameter
1633         mapflag = Blender.Texture.MapTo.COL  #default to color
1634         if blok.has_key('CHAN'):
1635             if blok['CHAN'] == 'COLR':
1636                 tobj.pprint ("!!!Set Texture -> MapTo -> Col = %.3f" % set_dvar)
1637             if blok['CHAN'] == 'BUMP':
1638                 mapflag = Blender.Texture.MapTo.NOR
1639                 tobj.pprint ("!!!Set Texture -> MapTo -> Nor = %.3f" % set_dvar)
1640             if blok['CHAN'] == 'LUMI':
1641                 mapflag = Blender.Texture.MapTo.EMIT
1642                 tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
1643             if blok['CHAN'] == 'DIFF':
1644                 mapflag = Blender.Texture.MapTo.REF
1645                 tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
1646             if blok['CHAN'] == 'SPEC':
1647                 mapflag = Blender.Texture.MapTo.SPEC
1648                 tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
1649             if blok['CHAN'] == 'TRAN':
1650                 mapflag = Blender.Texture.MapTo.ALPHA
1651                 tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
1652         if blok.has_key('NEGA'):
1653             tobj.pprint ("!!!Watch-out: effect of this texture channel must be INVERTED!")
1654
1655         #the TexCo flag is determined by PROJ parameter
1656         if blok.has_key('PROJ'):
1657             if blok['PROJ'] == 0: #0 - Planar
1658                tobj.pprint ("!!!Flat projection")
1659                coordflag = Blender.Texture.TexCo.ORCO
1660                output_size_ofs(obj_size, obj_pos, blok)
1661             elif blok['PROJ'] == 1: #1 - Cylindrical
1662                tobj.pprint ("!!!Cylindrical projection")
1663                coordflag = Blender.Texture.TexCo.ORCO
1664                output_size_ofs(obj_size, obj_pos, blok)
1665             elif blok['PROJ'] == 2: #2 - Spherical
1666                tobj.pprint ("!!!Spherical projection")
1667                coordflag = Blender.Texture.TexCo.ORCO
1668                output_size_ofs(obj_size, obj_pos, blok)
1669             elif blok['PROJ'] == 3: #3 - Cubic
1670                tobj.pprint ("!!!Cubic projection")
1671                coordflag = Blender.Texture.TexCo.ORCO
1672                output_size_ofs(obj_size, obj_pos, blok)
1673             elif blok['PROJ'] == 4: #4 - Front Projection
1674                tobj.pprint ("!!!Front projection")
1675                coordflag = Blender.Texture.TexCo.ORCO
1676                output_size_ofs(obj_size, obj_pos, blok)
1677             elif blok['PROJ'] == 5: #5 - UV
1678                tobj.pprint ("UVMapped")
1679                coordflag = Blender.Texture.TexCo.UV
1680         mat.setTexture(ti, newtex, coordflag, mapflag)
1681         ti += 1
1682     #end loop over bloks
1683     return
1684
1685
1686
1687
1688 # ========================================
1689 # === Create and assign a new material ===
1690 # ========================================
1691 #def update_material(surf_list, ptag_dict, obj, clip_list, uv_dict, dir_part):
1692 def update_material(clip_list, objspec, surf_list):
1693     if (surf_list == []) or (objspec[5] == {}) or (objspec[1] == {}):
1694         tobj.pprint( "something getting wrong in update_material: dump follows  ...")
1695         tobj.pprint( surf_list)
1696         tobj.pprint( objspec[5])
1697         tobj.pprint( objspec[1])
1698         return
1699     obj_dict = objspec[1]
1700     all_faces = objspec[3]
1701     obj_dim_dict = objspec[4]
1702     ptag_dict = objspec[5]
1703     uvcoords_dict = objspec[7]
1704     facesuv_dict = objspec[8]
1705     for surf in surf_list:
1706         if (surf['NAME'] in ptag_dict.iterkeys()):
1707             tobj.pprint ("#-------------------------------------------------------------------#")
1708             tobj.pprint ("Processing surface (material): %s" % surf['NAME'])
1709             tobj.pprint ("#-------------------------------------------------------------------#")
1710             #material set up
1711             facelist = ptag_dict[surf['NAME']]
1712             #bounding box and position
1713             cur_obj = obj_dict[surf['NAME']]
1714             obj_size = obj_dim_dict[surf['NAME']][0]
1715             obj_pos = obj_dim_dict[surf['NAME']][1]
1716             tobj.pprint(surf)
1717             #uncomment this if material pre-allocated by read_surf
1718             mat = surf['g_MAT']
1719             if mat == None:
1720                 tobj.pprint ("Sorry, no pre-allocated material to update. Giving up for %s." % surf['NAME'])
1721                 break
1722             #mat = Blender.Material.New(surf['NAME'])
1723             #surf['g_MAT'] = mat
1724             if surf.has_key('COLR'):
1725                 mat.rgbCol = surf['COLR']
1726             if surf.has_key('LUMI'):
1727                 mat.setEmit(surf['LUMI'])
1728             if surf.has_key('GVAL'):
1729                 mat.setAdd(surf['GVAL'])
1730             if surf.has_key('SPEC'):
1731                 mat.setSpec(surf['SPEC'])                        #it should be * 2 but seems to be a bit higher lwo [0.0, 1.0] - blender [0.0, 2.0]
1732             if surf.has_key('DIFF'):
1733                 mat.setRef(surf['DIFF'])                         #lwo [0.0, 1.0] - blender [0.0, 1.0]
1734             if surf.has_key('REFL'):
1735                 mat.setRayMirr(surf['REFL'])                     #lwo [0.0, 1.0] - blender [0.0, 1.0]
1736                 #mat.setMode('RAYMIRROR') NO! this will reset all the other modes
1737                 #mat.mode |= Blender.Material.Modes.RAYMIRROR No more usable?
1738                 mm = mat.getMode()
1739                 mm |= Blender.Material.Modes.RAYMIRROR
1740                 mm &= 327679 #4FFFF this is implementation dependent
1741                 mat.setMode(mm)
1742             #WARNING translucency not implemented yet check 2.36 API
1743             #if surf.has_key('TRNL'):
1744             #
1745             if surf.has_key('GLOS'):                             #lwo [0.0, 1.0] - blender [0, 255]
1746                 glo = int(371.67 * surf['GLOS'] - 42.334)        #linear mapping - seems to work better than exp mapping
1747                 if glo <32:  glo = 32                            #clamped to 32-255
1748                 if glo >255: glo = 255
1749                 mat.setHardness(glo)
1750             if surf.has_key('TRAN'):
1751                 mat.setAlpha(1.0-surf['TRAN'])                                        #lwo [0.0, 1.0] - blender [1.0, 0.0]
1752                 #mat.mode |= Blender.Material.Modes.RAYTRANSP
1753                 mm = mat.getMode()
1754                 mm |= Blender.Material.Modes.RAYTRANSP
1755                 mm &= 327679 #4FFFF this is implementation dependent
1756                 mat.setMode(mm)
1757             if surf.has_key('RIND'):
1758                 s = surf['RIND']
1759                 if s < 1.0: s = 1.0
1760                 if s > 3.0: s = 3.0
1761                 mat.setIOR(s)                                                         #clipped to blender [1.0, 3.0]
1762                 #mat.mode |= Blender.Material.Modes.RAYTRANSP
1763                 mm = mat.getMode()
1764                 mm |= Blender.Material.Modes.RAYTRANSP
1765                 mm &= 327679 #4FFFF this is implementation dependent
1766                 mat.setMode(mm)
1767             if surf.has_key('BLOK') and surf['BLOK'] != []:
1768                 #update the material according to texture.
1769                 create_blok(surf, mat, clip_list, obj_size, obj_pos)
1770             #finished setting up the material
1771         #end if exist SURF
1772     #end loop on materials (SURFs)
1773     return
1774
1775
1776 # ======================
1777 # === Read Faces 6.0 ===
1778 # ======================
1779 def read_faces_6(lwochunk):
1780     data = cStringIO.StringIO(lwochunk.read())
1781     faces = []
1782     polygon_type = data.read(4)
1783     subsurf = 0
1784     if polygon_type != "FACE" and polygon_type != "PTCH":
1785         tobj.pprint("No FACE/PATCH Were Found. Polygon Type: %s" % polygon_type)
1786         return "", 2
1787     if polygon_type == 'PTCH': subsurf = 1
1788     i = 0
1789     while(i < lwochunk.chunksize-4):
1790         if not i%1000 and my_meshtools.show_progress:
1791             Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
1792         facev = []
1793         numfaceverts, = struct.unpack(">H", data.read(2))
1794         i += 2
1795
1796         for j in xrange(numfaceverts):
1797             index, index_size = read_vx(data)
1798             i += index_size
1799             facev.append(index)
1800         faces.append(facev)
1801     tobj.pprint("read %s faces; type of block %d (0=FACE; 1=PATCH)" % (len(faces), subsurf))
1802     return faces, subsurf
1803
1804
1805
1806 # ===========================================================
1807 # === Start the show and main callback ======================
1808 # ===========================================================
1809
1810 def fs_callback(filename):
1811     read(filename)
1812
1813 Blender.Window.FileSelector(fs_callback, "Import LWO")
1814