3 Name: 'LightWave (.lwo)...'
6 Tooltip: 'Import LightWave Object File Format'
9 __author__ = ["Alessandro Pirovano, Anthony D'Agostino (Scorpius)", "Campbell Barton (ideasman42)", "ZanQdo"]
10 __url__ = ("www.blender.org", "blenderartist.org",
11 "Anthony's homepage, http://www.redrival.com/scorpius", "Alessandro's homepage, http://uaraus.altervista.org")
13 importername = "lwo_import 0.4.0"
15 # +---------------------------------------------------------+
16 # | Save your work before and after use. |
17 # | Please report any useful comment to: |
18 # | uaraus-dem@yahoo.it |
20 # +---------------------------------------------------------+
21 # +---------------------------------------------------------+
22 # | Copyright (c) 2002 Anthony D'Agostino |
23 # | http://www.redrival.com/scorpius |
24 # | scorpius@netzero.com |
26 # | Import Export Suite v0.5 |
27 # +---------------------------------------------------------+
28 # | Read and write LightWave Object File Format (*.lwo) |
29 # +---------------------------------------------------------+
30 # +---------------------------------------------------------+
31 # | Alessandro Pirovano tweaked starting on March 2005 |
32 # | http://uaraus.altervista.org |
33 # +---------------------------------------------------------+
34 # +----------------------------------------------------------
37 # | This program is free software; you can redistribute it and/or modify
38 # | it under the terms of the GNU General Public License as published by
39 # | the Free Software Foundation; either version 2 of the License, or
40 # | (at your option) any later version.
42 # | This program is distributed in the hope that it will be useful,
43 # | but WITHOUT ANY WARRANTY; without even the implied warranty of
44 # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 # | GNU General Public License for more details.
47 # | You should have received a copy of the GNU General Public License
48 # | along with this program; if not, write to the Free Software
49 # | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
50 # +----------------------------------------------------------
51 # +---------------------------------------------------------+
53 # | 0.4.0 : Updated for blender 2.44 |
54 # | ZanQdo - made the mesh import the right way up |
55 # | Ideasman42 - Updated functions for the bew API |
56 # | as well as removing the text object class |
57 # | 0.2.2 : This code works with Blender 2.42 RC3 |
58 # | Added a new PolyFill function for BPYMesh's |
59 # | ngon() to use, checked compatibility |
60 # | lightwaves ngons are imported as fgons |
61 # | Checked compatibility against 1711 lwo files |
62 # | 0.2.1 : This code works with Blender 2.40 RC1 |
63 # | modified material mode assignment to deal with |
64 # | Python API modification |
65 # | Changed script license to GNU GPL |
66 # | 0.2.0: This code works with Blender 2.40a2 or up |
67 # | Major rewrite to deal with large meshes |
68 # | - 2 pass file parsing |
69 # | - lower memory foot###if DEBUG: print |
70 # | (as long as python gc allows) |
71 # | 2.40a2 - Removed subsurf settings patches=poly |
72 # | 2.40a2 - Edge generation instead of 2vert faces |
73 # | 0.1.16: fixed (try 2) texture offset calculations |
74 # | added hint on axis mapping |
75 # | added hint on texture blending mode |
76 # | added hint on texture transparency setting |
77 # | search images in original directory first |
78 # | fixed texture order application |
79 # | 0.1.15: added release log |
80 # | fixed texture offset calculations (non-UV) |
81 # | fixed reverting vertex order in face generation |
82 # | associate texture on game-engine settings |
83 # | vector math definitely based on mathutils |
84 # | search images in "Images" and "../Images" dir |
85 # | revised logging facility |
86 # | fixed subsurf texture and material mappings |
87 # | 0.1.14: patched missing mod_vector (not definitive) |
88 # | 0.1.13: first public release |
89 # +---------------------------------------------------------+
91 #blender related import
95 # use for comprehensiveImageLoad
98 # Use this ngon function
103 #python specific modules import
105 import struct, chunk, cStringIO
107 struct= chunk= cStringIO= None
109 ### # Debuggin disabled in release.
110 ### # do a search replace to enabe debug prints
113 # ===========================================================
114 # === Utility Preamble ======================================
115 # ===========================================================
118 #uncomment the following line to enable logging facility to the named text object
119 #textname = "lwo_log"
121 TXMTX = Blender.Mathutils.Matrix(\
127 # ===========================================================
128 # === Make sure it is a string ... deal with strange chars ==
129 # ===========================================================
132 for ll in xrange(len(st)):
139 # ===========================================================
140 # === Main read functions ===================================
141 # ===========================================================
143 # =============================
144 # === Read LightWave Format ===
145 # =============================
147 if BPyMessages.Error_NoFile(filename):
150 print "This is: %s" % importername
151 print "Importing file:", filename
152 bpy.data.scenes.active.objects.selected = []
154 start = Blender.sys.time()
155 file = open(filename, "rb")
157 editmode = Blender.Window.EditMode() # are we in edit mode? If so ...
158 if editmode: Blender.Window.EditMode(0) # leave edit mode before getting the mesh # === LWO header ===
161 form_id, form_size, form_type = struct.unpack(">4s1L4s", file.read(12))
163 Blender.Draw.PupMenu('Error%t|This is not a lightwave file')
166 if (form_type == "LWOB"):
167 read_lwob(file, filename)
168 elif (form_type == "LWO2"):
169 read_lwo2(file, filename)
171 print "Can't read a file with the form_type: %s" % form_type
174 Blender.Window.DrawProgressBar(1.0, "") # clear progressbar
176 end = Blender.sys.time()
177 seconds = " in %.2f %s" % (end-start, "seconds")
178 if form_type == "LWO2": fmt = " (v6.0 Format)"
179 if form_type == "LWOB": fmt = " (v5.5 Format)"
180 print "Successfully imported " + filename.split('\\')[-1].split('/')[-1] + fmt + seconds
182 if editmode: Blender.Window.EditMode(1) # optional, just being nice
188 # =================================
189 # === Read LightWave 5.5 format ===
190 # =================================
191 def read_lwob(file, filename):
192 #This function is directly derived from the LWO2 import routine
193 #dropping all the material analysis parts
195 ###if DEBUG: print "LightWave 5.5 format"
197 dir_part = Blender.sys.dirname(filename)
198 fname_part = Blender.sys.basename(filename)
201 #first initialization of data structures
202 defaultname = Blender.sys.splitext(fname_part)[0]
203 tag_list = [] #tag list: global for the whole file?
204 surf_list = [] #surf list: global for the whole file?
205 clip_list = [] #clip list: global for the whole file?
210 #add default material for orphaned faces, if any
211 surf_list.append({'NAME': "_Orphans", 'g_MAT': bpy.data.materials.new("_Orphans")})
213 #pass 2: effectively generate objects
214 ###if DEBUG: print "Pass 1: dry import"
216 objspec_list = ["imported", {}, [], [], {}, {}, 0, {}, {}]
218 form_id, form_size, form_type = struct.unpack(">4s1L4s", file.read(12))
219 if (form_type != "LWOB"):
220 ###if DEBUG: print "??? Inconsistent file type: %s" % form_type
224 lwochunk = chunk.Chunk(file)
227 ###if DEBUG: print ' ',
228 if lwochunk.chunkname == "LAYR":
229 ###if DEBUG: print "---- LAYR",
230 objname = read_layr(lwochunk)
231 ###if DEBUG: print objname
232 if objspec_list != None: #create the object
233 create_objects(clip_list, objspec_list, surf_list)
234 update_material(clip_list, objspec_list, surf_list) #give it all the object
235 objspec_list = [objname, {}, [], [], {}, {}, 0, {}, {}]
237 elif lwochunk.chunkname == "PNTS": # Verts
238 ###if DEBUG: print "---- PNTS",
239 verts = read_verts(lwochunk)
240 objspec_list[2] = verts
241 elif lwochunk.chunkname == "POLS": # Faces v5.5
242 ###if DEBUG: print "-------- POLS(5.5)"
243 faces = read_faces_5(lwochunk)
245 #flag is 0 for regular polygon, 1 for patches (= subsurf), 2 for anything else to be ignored
247 if objspec_list[3] != []:
248 #create immediately the object
249 create_objects(clip_list, objspec_list, surf_list)
250 update_material(clip_list, objspec_list, surf_list) #give it all the object
251 #update with new data
252 objspec_list = [objspec_list[0], #update name
254 objspec_list[2], #same vertexes
255 faces, #give it the new faces
256 {}, #no need to copy - filled at runtime
257 {}, #polygon tagging will follow
259 objspec_list[7], #same uvcoords
262 #end if already has a face list
263 objspec_list[3] = faces
264 objname = objspec_list[0]
266 objname = defaultname
267 #end if processing a valid poly type
269 ###if DEBUG: print "---- %s: skipping (definitely!)" % lwochunk.chunkname
271 #uncomment here to log data structure as it is built
272 # ###if DEBUG: print object_list
274 create_objects(clip_list, objspec_list, surf_list)
275 update_material(clip_list, objspec_list, surf_list) #give it all the object
281 ###if DEBUG: print "\nFound %d objects:" % object_index
286 # =============================
287 # === Read LightWave Format ===
288 # =============================
289 def read_lwo2(file, filename, typ="LWO2"):
291 ###if DEBUG: print "LightWave 6 (and above) format"
293 dir_part = Blender.sys.dirname(filename)
294 fname_part = Blender.sys.basename(filename)
297 #first initialization of data structures
298 defaultname = Blender.sys.splitext(fname_part)[0]
299 tag_list = [] #tag list: global for the whole file?
300 surf_list = [] #surf list: global for the whole file?
301 clip_list = [] #clip list: global for the whole file?
305 # init value is: object_list = [[None, {}, [], [], {}, {}, 0, {}, {}]]
306 #0 - objname #original name
307 #1 - obj_dict = {TAG} #objects created
308 #2 - verts = [] #object vertexes
309 #3 - faces = [] #object faces (associations poly -> vertexes)
310 #4 - obj_dim_dict = {TAG} #tuples size and pos in local object coords - used for NON-UV mappings
311 #5 - polytag_dict = {TAG} #tag to polygons mapping
312 #6 - patch_flag #0 = surf; 1 = patch (subdivision surface) - it was the image list
313 #7 - uvcoords_dict = {name} #uvmap coordinates (mixed mode per vertex/per face)
314 #8 - facesuv_dict = {name} #vmad only coordinates associations poly & vertex -> uv tuples
316 #pass 1: look in advance for materials
317 ###if DEBUG: print "Starting Pass 1: hold on tight"
320 lwochunk = chunk.Chunk(file)
323 ###if DEBUG: print ' ',
324 if lwochunk.chunkname == "TAGS": # Tags
325 ###if DEBUG: print "---- TAGS"
326 tag_list.extend(read_tags(lwochunk))
327 elif lwochunk.chunkname == "SURF": # surfaces
328 ###if DEBUG: print "---- SURF"
329 surf_list.append(read_surfs(lwochunk, surf_list, tag_list))
330 elif lwochunk.chunkname == "CLIP": # texture images
331 ###if DEBUG: print "---- CLIP"
332 clip_list.append(read_clip(lwochunk, dir_part))
333 ###if DEBUG: print "read total %s clips up to now" % len(clip_list)
336 ckname = safestring(lwochunk.chunkname)
338 choice = Blender.Draw.PupMenu("WARNING: file could be corrupted.%t|Import anyway|Give up")
340 ###if DEBUG: print "---- %s: Maybe file corrupted. Terminated by user" % lwochunk.chunkname
343 ###if DEBUG: print "---- %s: skipping (maybe later)" % lwochunk.chunkname
346 #add default material for orphaned faces, if any
347 surf_list.append({'NAME': "_Orphans", 'g_MAT': bpy.data.materials.new("_Orphans")})
349 #pass 2: effectively generate objects
350 ###if DEBUG: print "Pass 2: now for the hard part"
353 form_id, form_size, form_type = struct.unpack(">4s1L4s", file.read(12))
354 if (form_type != "LWO2"):
355 ###if DEBUG: print "??? Inconsistent file type: %s" % form_type
359 lwochunk = chunk.Chunk(file)
362 ###if DEBUG: print ' ',
363 if lwochunk.chunkname == "LAYR":
364 ###if DEBUG: print "---- LAYR"
365 objname = read_layr(lwochunk)
366 ###if DEBUG: print objname
367 if objspec_list != None: #create the object
368 create_objects(clip_list, objspec_list, surf_list)
369 update_material(clip_list, objspec_list, surf_list) #give it all the object
370 objspec_list = [objname, {}, [], [], {}, {}, 0, {}, {}]
372 elif lwochunk.chunkname == "PNTS": # Verts
373 ###if DEBUG: print "---- PNTS"
374 verts = read_verts(lwochunk)
375 objspec_list[2] = verts
376 elif lwochunk.chunkname == "VMAP": # MAPS (UV)
377 ###if DEBUG: print "---- VMAP"
378 #objspec_list[7] = read_vmap(objspec_list[7], len(objspec_list[2]), lwochunk)
379 read_vmap(objspec_list[7], len(objspec_list[2]), lwochunk)
380 elif lwochunk.chunkname == "VMAD": # MAPS (UV) per-face
381 ###if DEBUG: print "---- VMAD"
382 #objspec_list[7], objspec_list[8] = read_vmad(objspec_list[7], objspec_list[8], len(objspec_list[3]), len(objspec_list[2]), lwochunk)
383 read_vmad(objspec_list[7], objspec_list[8], len(objspec_list[3]), len(objspec_list[2]), lwochunk)
384 elif lwochunk.chunkname == "POLS": # Faces v6.0
385 ###if DEBUG: print "-------- POLS(6)"
386 faces, flag = read_faces_6(lwochunk)
387 #flag is 0 for regular polygon, 1 for patches (= subsurf), 2 for anything else to be ignored
389 if objspec_list[3] != []:
390 #create immediately the object
391 create_objects(clip_list, objspec_list, surf_list)
392 update_material(clip_list, objspec_list, surf_list) #give it all the object
393 #update with new data
394 objspec_list = [objspec_list[0], #update name
396 objspec_list[2], #same vertexes
397 faces, #give it the new faces
398 {}, #no need to copy - filled at runtime
399 {}, #polygon tagging will follow
401 objspec_list[7], #same uvcoords
404 #end if already has a face list
405 objspec_list[3] = faces
406 objname = objspec_list[0]
408 objname = defaultname
409 #end if processing a valid poly type
410 elif lwochunk.chunkname == "PTAG": # PTags
411 ###if DEBUG: print "---- PTAG"
412 polytag_dict = read_ptags(lwochunk, tag_list)
413 for kk, polytag_dict_val in polytag_dict.iteritems(): objspec_list[5][kk] = polytag_dict_val
415 ###if DEBUG: print "---- %s: skipping (definitely!)" % lwochunk.chunkname
417 #uncomment here to log data structure as it is built
420 create_objects(clip_list, objspec_list, surf_list)
421 update_material(clip_list, objspec_list, surf_list) #give it all the object
426 ###if DEBUG: print "\nFound %d objects:" % object_index
434 # ===========================================================
435 # === File reading routines =================================
436 # ===========================================================
440 def read_verts(lwochunk):
441 #data = cStringIO.StringIO(lwochunk.read())
442 numverts = lwochunk.chunksize/12
443 return [struct.unpack(">fff", lwochunk.read(12)) for i in xrange(numverts)]
450 # modified to deal with odd lenght strings
455 if char == "\0": break
457 len_name = len(name) + 1 #count the trailing zero
459 char = file.read(1) #remove zero padding to even lenght
461 return name, len_name
467 def read_layr(lwochunk):
468 data = cStringIO.StringIO(lwochunk.read())
469 idx, flags = struct.unpack(">hh", data.read(4))
470 pivot = struct.unpack(">fff", data.read(12))
471 layer_name, discard = read_name(data)
472 if not layer_name: layer_name = "NoName"
477 # ======================
478 # === Read Faces 5.5 ===
479 # ======================
480 def read_faces_5(lwochunk):
481 data = cStringIO.StringIO(lwochunk.read())
484 while i < lwochunk.chunksize:
485 #if not i%1000 and my_meshtools.show_progress:
486 # Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
488 numfaceverts, = struct.unpack(">H", data.read(2))
489 facev = [struct.unpack(">H", data.read(2))[0] for j in xrange(numfaceverts)]
492 surfaceindex, = struct.unpack(">H", data.read(2))
494 ###if DEBUG: print "***Error. Referencing uncorrect surface index"
496 i += (4+numfaceverts*2)
500 # ==================================
501 # === Read Variable-Length Index ===
502 # ==================================
504 byte1, = struct.unpack(">B", data.read(1))
505 if byte1 != 0xFF: # 2-byte index
506 byte2, = struct.unpack(">B", data.read(1))
507 index = byte1*256 + byte2
510 byte2, byte3, byte4 = struct.unpack(">3B", data.read(3))
511 index = byte2*65536 + byte3*256 + byte4
513 return index, index_size
516 # ======================
517 # === Read uvmapping ===
518 # ======================
519 def read_vmap(uvcoords_dict, maxvertnum, lwochunk):
522 ###if DEBUG: print "Found VMAP but no vertexes to map!"
524 data = cStringIO.StringIO(lwochunk.read())
525 map_type = data.read(4)
526 if map_type != "TXUV":
527 ###if DEBUG: print "Reading VMAP: No Texture UV map Were Found. Map Type: %s" % map_type
529 dimension, = struct.unpack(">H", data.read(2))
530 name, i = read_name(data) #i initialized with string lenght + zeros
531 ###if DEBUG: print "TXUV %d %s" % (dimension, name)
532 #note if there is already a VMAD it will be lost
533 #it is assumed that VMAD will follow the corresponding VMAP
534 Vector = Blender.Mathutils.Vector
535 try: #if uvcoords_dict.has_key(name):
536 my_uv_dict = uvcoords_dict[name] #update existing
538 my_uv_dict = {} #start a brand new: this could be made more smart
539 while (i < lwochunk.chunksize - 6): #4+2 header bytes already read
540 vertnum, vnum_size = read_vx(data)
541 uv = struct.unpack(">ff", data.read(8))
542 if vertnum >= maxvertnum:
543 ###if DEBUG: print "Hem: more uvmap than vertexes? ignoring uv data for vertex %d" % vertnum
546 my_uv_dict[vertnum] = Vector(uv)
548 #end loop on uv pairs
549 uvcoords_dict[name] = my_uv_dict
550 #this is a per-vertex mapping AND the uv tuple is vertex-ordered, so faces_uv is the same as faces
551 #return uvcoords_dict
554 # ========================
555 # === Read uvmapping 2 ===
556 # ========================
557 def read_vmad(uvcoords_dict, facesuv_dict, maxfacenum, maxvertnum, lwochunk):
558 if maxvertnum == 0 or maxfacenum == 0:
559 ###if DEBUG: print "Found VMAD but no vertexes to map!"
560 return uvcoords_dict, facesuv_dict
561 data = cStringIO.StringIO(lwochunk.read())
562 map_type = data.read(4)
563 if map_type != "TXUV":
564 ###if DEBUG: print "Reading VMAD: No Texture UV map Were Found. Map Type: %s" % map_type
565 return uvcoords_dict, facesuv_dict
566 dimension, = struct.unpack(">H", data.read(2))
567 name, i = read_name(data) #i initialized with string lenght + zeros
568 ###if DEBUG: print "TXUV %d %s" % (dimension, name)
569 try: #if uvcoords_dict.has_key(name):
570 my_uv_dict = uvcoords_dict[name] #update existing
572 my_uv_dict = {} #start a brand new: this could be made more smart
574 newindex = maxvertnum + 10 #why +10? Why not?
575 #end variable initialization
576 Vector = Blender.Mathutils.Vector
577 while (i < lwochunk.chunksize - 6): #4+2 header bytes already read
578 vertnum, vnum_size = read_vx(data)
580 polynum, vnum_size = read_vx(data)
582 uv = struct.unpack(">ff", data.read(8))
583 if polynum >= maxfacenum or vertnum >= maxvertnum:
584 ###if DEBUG: print "Hem: more uvmap than vertexes? ignorig uv data for vertex %d" % vertnum
587 my_uv_dict[newindex] = Vector(uv)
588 my_facesuv_list.append([polynum, vertnum, newindex])
591 #end loop on uv pairs
592 uvcoords_dict[name] = my_uv_dict
593 facesuv_dict[name] = my_facesuv_list
594 ###if DEBUG: print "updated %d vertexes data" % (newindex-maxvertnum-10)
601 def read_tags(lwochunk):
602 data = cStringIO.StringIO(lwochunk.read())
606 while i < lwochunk.chunksize:
609 tag_list.append(current_tag)
610 if (len(current_tag) % 2 == 0): char = data.read(1)
615 ###if DEBUG: print "read %d tags, list follows: %s" % (len(tag_list), tag_list)
622 def read_ptags(lwochunk, tag_list):
623 data = cStringIO.StringIO(lwochunk.read())
624 polygon_type = data.read(4)
625 if polygon_type != "SURF":
626 ###if DEBUG: print "No Surf Were Found. Polygon Type: %s" % polygon_type
630 while(i < lwochunk.chunksize-4): #4 bytes polygon type already read
631 #if not i%1000 and my_meshtools.show_progress:
632 # Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading PTAGS")
633 poln, poln_size = read_vx(data)
635 tag_index, = struct.unpack(">H", data.read(2))
636 if tag_index > (len(tag_list)):
637 ###if DEBUG: print "Reading PTAG: Surf belonging to undefined TAG: %d. Skipping" % tag_index
640 tag_key = tag_list[tag_index]
642 ptag_dict[tag_list[tag_index]].append(poln)
643 except: #if not(ptag_dict.has_key(tag_key)):
644 ptag_dict[tag_list[tag_index]] = [poln]
646 ###if DEBUG: for i, ptag_dict_val in ptag_dict.iteritems(): print "read %d polygons belonging to TAG %s" % (len(ptag_dict_val ), i)
654 def read_clip(lwochunk, dir_part):
655 # img, IMG, g_IMG refers to blender image objects
656 # ima, IMAG, g_IMAG refers to clip dictionary 'ID' entries: refer to blok and surf
658 data = cStringIO.StringIO(lwochunk.read())
659 data_str = data.read(4)
660 if len(data_str) < 4: # can be zero also??? :/
661 # Should not happen but lw can import so we should too
664 image_index, = struct.unpack(">L", data_str)
665 clip_dict['ID'] = image_index
667 while(i < lwochunk.chunksize):
668 subchunkname, = struct.unpack("4s", data.read(4))
669 subchunklen, = struct.unpack(">H", data.read(2))
670 if subchunkname == "STIL":
671 ###if DEBUG: print "-------- STIL"
672 clip_name, k = read_name(data)
673 #now split text independently from platform
674 #depend on the system where image was saved. NOT the one where the script is run
676 if Blender.sys.sep == no_sep: no_sep ="/"
677 if (no_sep in clip_name):
678 clip_name = clip_name.replace(no_sep, Blender.sys.sep)
679 short_name = Blender.sys.basename(clip_name)
680 if clip_name == "" or short_name == "":
681 ###if DEBUG: print "Reading CLIP: Empty clip name not allowed. Skipping"
682 discard = data.read(subchunklen-k)
683 clip_dict['NAME'] = clip_name
684 clip_dict['BASENAME'] = short_name
685 elif subchunkname == "XREF": #cross reference another image
686 ###if DEBUG: print "-------- XREF"
687 image_index, = struct.unpack(">L", data.read(4))
688 clip_name, k = read_name(data)
689 clip_dict['NAME'] = clip_name
690 clip_dict['XREF'] = image_index
691 elif subchunkname == "NEGA": #negate texture effect
692 ###if DEBUG: print "-------- NEGA"
693 n, = struct.unpack(">H", data.read(2))
694 clip_dict['NEGA'] = n
696 ###if DEBUG: print "-------- CLIP:%s: skipping" % subchunkname
697 discard = data.read(subchunklen)
698 i = i + 6 + subchunklen
699 #end loop on surf chunks
700 ###if DEBUG: print "read image:%s" % clip_dict
701 if 'XREF' in clip_dict: # has_key
702 ###if DEBUG: print "Cross-reference: no image pre-allocated."
705 #img = load_image("",clip_dict['NAME'])
709 NAME= clip_dict['NAME']
710 BASENAME= clip_dict['BASENAME']
712 clip_dict['g_IMG'] = None
714 # ###if DEBUG: print 'test', NAME, BASENAME
715 img = BPyImage.comprehensiveImageLoad(NAME, dir_part, PLACE_HOLDER= False, RECURSIVE=False)
717 ###if DEBUG: print "***No image %s found: trying LWO file subdir" % NAME
718 img = BPyImage.comprehensiveImageLoad(BASENAME, dir_part, PLACE_HOLDER= False, RECURSIVE=False)
720 ###if DEBUG: if not img: print "***No image %s found: giving up" % BASENAME
721 #lucky we are: we have an image
722 ###if DEBUG: print "Image pre-allocated."
723 clip_dict['g_IMG'] = img
728 # ===========================
729 # === Read Surfaces Block ===
730 # ===========================
731 def read_surfblok(subchunkdata):
732 lenght = len(subchunkdata)
735 data = cStringIO.StringIO(subchunkdata)
736 ##############################################################
737 # blok header sub-chunk
738 ##############################################################
739 subchunkname, = struct.unpack("4s", data.read(4))
740 subchunklen, = struct.unpack(">h", data.read(2))
741 accumulate_i = subchunklen + 6
742 if subchunkname != 'IMAP':
743 ###if DEBUG: print "---------- SURF: BLOK: %s: block aborting" % subchunkname
745 ###if DEBUG: print "---------- IMAP"
746 ordinal, i = read_name(data)
747 my_dict['ORD'] = ordinal
748 #my_dict['g_ORD'] = -1
749 my_dict['ENAB'] = True
750 while(i < subchunklen): # ---------left 6------------------------- loop on header parameters
751 sub2chunkname, = struct.unpack("4s", data.read(4))
752 sub2chunklen, = struct.unpack(">h", data.read(2))
753 i = i + 6 + sub2chunklen
754 if sub2chunkname == "CHAN":
755 ###if DEBUG: print "------------ CHAN"
756 sub2chunkname, = struct.unpack("4s", data.read(4))
757 my_dict['CHAN'] = sub2chunkname
759 elif sub2chunkname == "ENAB": #only present if is to be disabled
760 ###if DEBUG: print "------------ ENAB"
761 ena, = struct.unpack(">h", data.read(2))
762 my_dict['ENAB'] = ena
764 elif sub2chunkname == "NEGA": #only present if is to be enabled
765 ###if DEBUG: print "------------ NEGA"
766 ena, = struct.unpack(">h", data.read(2))
768 my_dict['NEGA'] = ena
770 elif sub2chunkname == "OPAC": #only present if is to be disabled
771 ###if DEBUG: print "------------ OPAC"
772 opa, = struct.unpack(">h", data.read(2))
773 s, = struct.unpack(">f", data.read(4))
774 envelope, env_size = read_vx(data)
775 my_dict['OPAC'] = opa
776 my_dict['OPACVAL'] = s
778 elif sub2chunkname == "AXIS":
779 ###if DEBUG: print "------------ AXIS"
780 ena, = struct.unpack(">h", data.read(2))
781 my_dict['DISPLAXIS'] = ena
784 ###if DEBUG: print "------------ SURF: BLOK: IMAP: %s: skipping" % sub2chunkname
785 discard = data.read(sub2chunklen)
786 #end loop on blok header subchunks
787 ##############################################################
788 # blok attributes sub-chunk
789 ##############################################################
790 subchunkname, = struct.unpack("4s", data.read(4))
791 subchunklen, = struct.unpack(">h", data.read(2))
792 accumulate_i += subchunklen + 6
793 if subchunkname != 'TMAP':
794 ###if DEBUG: print "---------- SURF: BLOK: %s: block aborting" % subchunkname
796 ###if DEBUG: print "---------- TMAP"
798 while(i < subchunklen): # -----------left 6----------------------- loop on header parameters
799 sub2chunkname, = struct.unpack("4s", data.read(4))
800 sub2chunklen, = struct.unpack(">h", data.read(2))
801 i = i + 6 + sub2chunklen
802 if sub2chunkname == "CNTR":
803 ###if DEBUG: print "------------ CNTR"
804 x, y, z = struct.unpack(">fff", data.read(12))
805 envelope, env_size = read_vx(data)
806 my_dict['CNTR'] = [x, y, z]
807 sub2chunklen -= (12+env_size)
808 elif sub2chunkname == "SIZE":
809 ###if DEBUG: print "------------ SIZE"
810 x, y, z = struct.unpack(">fff", data.read(12))
811 envelope, env_size = read_vx(data)
812 my_dict['SIZE'] = [x, y, z]
813 sub2chunklen -= (12+env_size)
814 elif sub2chunkname == "ROTA":
815 ###if DEBUG: print "------------ ROTA"
816 x, y, z = struct.unpack(">fff", data.read(12))
817 envelope, env_size = read_vx(data)
818 my_dict['ROTA'] = [x, y, z]
819 sub2chunklen -= (12+env_size)
820 elif sub2chunkname == "CSYS":
821 ###if DEBUG: print "------------ CSYS"
822 ena, = struct.unpack(">h", data.read(2))
823 my_dict['CSYS'] = ena
826 ###if DEBUG: print "------------ SURF: BLOK: TMAP: %s: skipping" % sub2chunkname
829 discard = data.read(sub2chunklen)
830 #end loop on blok attributes subchunks
831 ##############################################################
832 # ok, now other attributes without sub_chunks
833 ##############################################################
834 while(accumulate_i < lenght): # ---------------------------------- loop on header parameters: lenght has already stripped the 6 bypes header
835 subchunkname, = struct.unpack("4s", data.read(4))
836 subchunklen, = struct.unpack(">H", data.read(2))
837 accumulate_i = accumulate_i + 6 + subchunklen
838 if subchunkname == "PROJ":
839 ###if DEBUG: print "---------- PROJ"
840 p, = struct.unpack(">h", data.read(2))
843 elif subchunkname == "AXIS":
844 ###if DEBUG: print "---------- AXIS"
845 a, = struct.unpack(">h", data.read(2))
846 my_dict['MAJAXIS'] = a
848 elif subchunkname == "IMAG":
849 ###if DEBUG: print "---------- IMAG"
850 i, i_size = read_vx(data)
852 subchunklen -= i_size
853 elif subchunkname == "WRAP":
854 ###if DEBUG: print "---------- WRAP"
855 ww, wh = struct.unpack(">hh", data.read(4))
856 #reduce width and height to just 1 parameter for both
857 my_dict['WRAP'] = max([ww,wh])
858 #my_dict['WRAPWIDTH'] = ww
859 #my_dict['WRAPHEIGHT'] = wh
861 elif subchunkname == "WRPW":
862 ###if DEBUG: print "---------- WRPW"
863 w, = struct.unpack(">f", data.read(4))
865 envelope, env_size = read_vx(data)
866 subchunklen -= (env_size+4)
867 elif subchunkname == "WRPH":
868 ###if DEBUG: print "---------- WRPH"
869 w, = struct.unpack(">f", data.read(4))
871 envelope, env_size = read_vx(data)
872 subchunklen -= (env_size+4)
873 elif subchunkname == "VMAP":
874 ###if DEBUG: print "---------- VMAP"
875 vmp, i = read_name(data)
876 my_dict['VMAP'] = vmp
880 ###if DEBUG: print "---------- SURF: BLOK: %s: skipping" % subchunkname
883 discard = data.read(subchunklen)
884 #end loop on blok subchunks
885 return my_dict, my_uvname
888 # =====================
889 # === Read Surfaces ===
890 # =====================
891 def read_surfs(lwochunk, surf_list, tag_list):
893 data = cStringIO.StringIO(lwochunk.read())
894 surf_name, i = read_name(data)
895 parent_name, j = read_name(data)
897 if (surf_name == "") or not(surf_name in tag_list):
898 ###if DEBUG: print "Reading SURF: Actually empty surf name not allowed. Skipping"
900 if (parent_name != ""):
901 parent_index = [x['NAME'] for x in surf_list].count(parent_name)
903 my_dict = surf_list[parent_index-1]
904 my_dict['NAME'] = surf_name
905 ###if DEBUG: print "Surface data for TAG %s" % surf_name
906 while(i < lwochunk.chunksize):
907 subchunkname, = struct.unpack("4s", data.read(4))
908 subchunklen, = struct.unpack(">H", data.read(2))
909 i = i + 6 + subchunklen #6 bytes subchunk header
910 if subchunkname == "COLR": #color: mapped on color
911 ###if DEBUG: print "-------- COLR"
912 r, g, b = struct.unpack(">fff", data.read(12))
913 envelope, env_size = read_vx(data)
914 my_dict['COLR'] = [r, g, b]
915 subchunklen -= (12+env_size)
916 elif subchunkname == "DIFF": #diffusion: mapped on reflection (diffuse shader)
917 ###if DEBUG: print "-------- DIFF"
918 s, = struct.unpack(">f", data.read(4))
919 envelope, env_size = read_vx(data)
921 subchunklen -= (4+env_size)
922 elif subchunkname == "SPEC": #specularity: mapped to specularity (spec shader)
923 ###if DEBUG: print "-------- SPEC"
924 s, = struct.unpack(">f", data.read(4))
925 envelope, env_size = read_vx(data)
927 subchunklen -= (4+env_size)
928 elif subchunkname == "REFL": #reflection: mapped on raymirror
929 ###if DEBUG: print "-------- REFL"
930 s, = struct.unpack(">f", data.read(4))
931 envelope, env_size = read_vx(data)
933 subchunklen -= (4+env_size)
934 elif subchunkname == "TRNL": #translucency: mapped on same param
935 ###if DEBUG: print "-------- TRNL"
936 s, = struct.unpack(">f", data.read(4))
937 envelope, env_size = read_vx(data)
939 subchunklen -= (4+env_size)
940 elif subchunkname == "GLOS": #glossiness: mapped on specularity hardness (spec shader)
941 ###if DEBUG: print "-------- GLOS"
942 s, = struct.unpack(">f", data.read(4))
943 envelope, env_size = read_vx(data)
945 subchunklen -= (4+env_size)
946 elif subchunkname == "TRAN": #transparency: inverted and mapped on alpha channel
947 ###if DEBUG: print "-------- TRAN"
948 s, = struct.unpack(">f", data.read(4))
949 envelope, env_size = read_vx(data)
951 subchunklen -= (4+env_size)
952 elif subchunkname == "LUMI": #luminosity: mapped on emit channel
953 ###if DEBUG: print "-------- LUMI"
954 s, = struct.unpack(">f", data.read(4))
955 envelope, env_size = read_vx(data)
957 subchunklen -= (4+env_size)
958 elif subchunkname == "GVAL": #glow: mapped on add channel
959 ###if DEBUG: print "-------- GVAL"
960 s, = struct.unpack(">f", data.read(4))
961 envelope, env_size = read_vx(data)
963 subchunklen -= (4+env_size)
964 elif subchunkname == "SMAN": #smoothing angle
965 ###if DEBUG: print "-------- SMAN"
966 s, = struct.unpack(">f", data.read(4))
969 elif subchunkname == "SIDE": #double sided?
970 ###if DEBUG: print "-------- SIDE" #if 1 side do not define key
971 s, = struct.unpack(">H", data.read(2))
975 elif subchunkname == "RIND": #Refraction: mapped on IOR
976 ###if DEBUG: print "-------- RIND"
977 s, = struct.unpack(">f", data.read(4))
978 envelope, env_size = read_vx(data)
980 subchunklen -= (4+env_size)
981 elif subchunkname == "BLOK": #blocks
982 ###if DEBUG: print "-------- BLOK"
983 rr, uvname = read_surfblok(data.read(subchunklen))
984 #paranoia setting: preventing adding an empty dict
987 my_dict['BLOK'].append(rr)
989 my_dict['BLOK'] = [rr]
992 my_dict['UVNAME'] = uvname #theoretically there could be a number of them: only one used per surf
993 # all are dictionaries - so testing keys
994 if not('g_IMAG' in my_dict) and ('CHAN' in rr) and ('OPAC' in rr) and ('IMAG' in rr):
995 if (rr['CHAN'] == 'COLR') and (rr['OPAC'] == 0):
996 my_dict['g_IMAG'] = rr['IMAG'] #do not set anything, just save image object for later assignment
997 subchunklen = 0 #force ending
1000 ###if DEBUG: print "-------- SURF:%s: skipping" % subchunkname
1002 discard = data.read(subchunklen)
1003 #end loop on surf chunks
1004 try:#if my_dict.has_key('BLOK'):
1005 my_dict['BLOK'].reverse() #texture applied in reverse order with respect to reading from lwo
1009 #uncomment this if material pre-allocated by read_surf
1010 my_dict['g_MAT'] = bpy.data.materials.new(my_dict['NAME'])
1011 ###if DEBUG: print "-> Material pre-allocated."
1014 # =========================
1015 # === Recalculate Faces ===
1016 # =========================
1018 def get_uvface(complete_list, facenum):
1019 # extract from the complete list only vertexes of the desired polygon
1022 for elem in complete_list:
1023 if elem[0] == facenum:
1024 my_facelist.append(elem)
1027 return [elem for elem in complete_list if elem[0] == facenum]
1029 def get_newindex(polygon_list, vertnum):
1030 # extract from the polygon list the new index associated to a vertex
1031 if not polygon_list: # == []
1033 for elem in polygon_list:
1034 if elem[1] == vertnum:
1036 # ###if DEBUG: print "WARNING: expected vertex %s for polygon %s. Polygon_list dump follows" % (vertnum, polygon_list[0][0])
1037 # ###if DEBUG: print polygon_list
1040 def get_surf(surf_list, cur_tag):
1041 for elem in surf_list: # elem can be None
1042 if elem and elem['NAME'] == cur_tag:
1048 # ====================================
1049 # === Modified Create Blender Mesh ===
1050 # ====================================
1051 def my_create_mesh(clip_list, surf, objspec_list, current_facelist, objname, not_used_faces):
1052 #take the needed faces and update the not-used face list
1053 complete_vertlist = objspec_list[2]
1054 complete_facelist = objspec_list[3]
1055 uvcoords_dict = objspec_list[7]
1056 facesuv_dict = objspec_list[8]
1057 vertex_map = {} #implementation as dict
1059 cur_ptag_faces_indexes = []
1060 maxface = len(complete_facelist)
1061 for ff in current_facelist:
1063 ###if DEBUG: print "Non existent face addressed: Giving up with this object"
1064 return None, not_used_faces #return the created object
1065 cur_face = complete_facelist[ff]
1066 cur_ptag_faces_indexes.append(ff)
1067 if not_used_faces: # != []
1068 not_used_faces[ff] = -1
1069 for vv in cur_face: vertex_map[vv] = 1
1073 scn= bpy.data.scenes.active
1074 msh = bpy.data.meshes.new()
1075 obj = scn.objects.new(msh)
1079 msh.materials = [surf['g_MAT']]
1083 msh.mode |= Blender.Mesh.Modes.AUTOSMOOTH #smooth it anyway
1084 if 'SMAN' in surf: # has_key
1085 #not allowed mixed mode mesh (all the mesh is smoothed and all with the same angle)
1086 #only one smoothing angle will be active! => take the max one
1087 msh.degr = min(80, int(surf['SMAN']/3.1415926535897932384626433832795*180.0)) #lwo in radians - blender in degrees
1090 img= lookup_imag(clip_list, surf['g_IMAG'])['g_IMG']
1094 #uv_flag = ((surf.has_key('UVNAME')) and (uvcoords_dict.has_key(surf['UVNAME'])) and (img != None))
1095 uv_flag = (('UVNAME' in surf) and (surf['UVNAME'] in uvcoords_dict))
1097 ###if DEBUG: print "\n#===================================================================#"
1098 ###if DEBUG: print "Processing Object: %s" % objname
1099 ###if DEBUG: print "#===================================================================#"
1102 msh.verts.extend([(0.0,0.0,0.0),])
1107 def tmp_get_vert(k, i):
1108 vertex_map[k] = i+j # j is the dummy vert
1109 # ###if DEBUG: print complete_vertlist[i]
1110 return complete_vertlist[k]
1114 msh.verts.extend([tmp_get_vert(k, i) for i, k in enumerate(vertex_map.iterkeys())])
1115 msh.transform(TXMTX) # faster then applying while reading.
1116 #end sweep over vertexes
1119 FACE_TEX= Blender.Mesh.FaceModes.TEX
1120 FACE_ALPHA= Blender.Mesh.FaceTranspModes.ALPHA
1121 EDGE_DRAW_FLAG= Blender.Mesh.EdgeFlags.EDGEDRAW | Blender.Mesh.EdgeFlags.EDGERENDER
1125 face_data = [] # [(indicies, material, uvs, image), ]
1130 uvcoords_dict_context = uvcoords_dict[surf['UVNAME']]
1131 try: current_uvdict = facesuv_dict[surf['UVNAME']]
1132 except: current_uvdict = None
1134 default_uv = Blender.Mathutils.Vector(0,0)
1135 def tmp_get_face_uvs(cur_face, i):
1138 uvface = get_uvface(current_uvdict,i)
1140 ni = get_newindex(uvface, vi)
1141 if ni == -1: ni = vi
1144 uvs.append(uvcoords_dict_context[ ni ])
1146 ###if DEBUG: print '\tWarning, Corrupt UVs'
1147 uvs.append(default_uv)
1151 uvs.append(uvcoords_dict_context[ vi ])
1153 ###if DEBUG: print '\tWarning, Corrupt UVs'
1154 uvs.append(default_uv)
1158 for i in cur_ptag_faces_indexes:
1159 cur_face = complete_facelist[i]
1160 numfaceverts = len(cur_face)
1162 if numfaceverts == 2: edges.append((vertex_map[cur_face[0]], vertex_map[cur_face[1]]))
1163 elif numfaceverts == 3 or numfaceverts == 4:
1164 rev_face = [__i for __i in reversed(cur_face)]
1165 face_data.append( [vertex_map[j] for j in rev_face] )
1166 if uv_flag: face_uvs.append(tmp_get_face_uvs(rev_face, i))
1167 elif numfaceverts > 4:
1168 meta_faces= BPyMesh.ngon(complete_vertlist, cur_face, PREF_FIX_LOOPS= True)
1169 edge_face_count = {}
1170 for mf in meta_faces:
1171 # These will always be tri's since they are scanfill faces
1172 mf = cur_face[mf[2]], cur_face[mf[1]], cur_face[mf[0]]
1173 face_data.append( [vertex_map[j] for j in mf] )
1175 if uv_flag: face_uvs.append(tmp_get_face_uvs(mf, i))
1178 if len(meta_faces) > 1:
1179 mf = face_data[-1] # reuse mf
1187 edge_face_count[v1,v2]+= 1
1189 edge_face_count[v1,v2]= 0
1194 edges_fgon.extend( [vert_key for vert_key, count in edge_face_count.iteritems() if count] )
1197 msh.edges.extend(edges)
1199 face_mapping_removed = msh.faces.extend(face_data, indexList=True)
1200 if 'TRAN' in surf or (mat and mat.alpha<1.0): # incase mat is null
1207 msh_faces= msh.faces
1208 for i, uvs in enumerate(face_uvs):
1209 i_mapped = face_mapping_removed[i]
1210 if i_mapped != None:
1211 f = msh_faces[i_mapped]
1216 if transp_flag: f.transp |= FACE_ALPHA
1219 msh_edges = msh.edges
1220 FGON= Blender.Mesh.EdgeFlags.FGON
1221 edges_fgon = msh.findEdges( edges_fgon )
1222 if type(edges_fgon) != list: edges_fgon = [edges_fgon]
1223 for ed in edges_fgon:
1225 msh_edges[ed].flag |= FGON
1227 if not(uv_flag): #clear eventual UV data
1231 msh.verts.delete([0,])
1233 return obj, not_used_faces #return the created object
1236 # ============================================
1237 # === Set Subsurf attributes on given mesh ===
1238 # ============================================
1239 def set_subsurf(obj):
1240 mods = obj.modifiers # get the object's modifiers
1241 mod = mods.append(Blender.Modifier.Type.SUBSURF) # add a new subsurf modifier
1242 mod[Blender.Modifier.Settings.LEVELS] = 2 # set subsurf subdivision levels to 2
1243 mod[Blender.Modifier.Settings.RENDLEVELS] = 2 # set subsurf rendertime subdivision levels to 2
1244 obj.makeDisplayList()
1247 # =================================
1248 # === object size and dimension ===
1249 # =================================
1250 def obj_size_pos(obj):
1251 bbox = obj.getBoundBox()
1252 bbox_min = map(lambda *row: min(row), *bbox) #transpose & get min
1253 bbox_max = map(lambda *row: max(row), *bbox) #transpose & get max
1254 obj_size = (bbox_max[0]-bbox_min[0], bbox_max[1]-bbox_min[1], bbox_max[2]-bbox_min[2])
1255 obj_pos = ( (bbox_max[0]+bbox_min[0]) / 2, (bbox_max[1]+bbox_min[1]) / 2, (bbox_max[2]+bbox_min[2]) / 2)
1256 return (obj_size, obj_pos)
1259 # =========================
1260 # === Create the object ===
1261 # =========================
1262 def create_objects(clip_list, objspec_list, surf_list):
1263 nf = len(objspec_list[3])
1264 not_used_faces = range(nf)
1265 ptag_dict = objspec_list[5]
1266 obj_dict = {} #links tag names to object, used for material assignments
1268 obj_list = [] #have it handy for parent association
1271 if (objspec_list[6] == 1):
1272 middlechar = endchar = "#"
1273 for cur_tag, ptag_dict_val in ptag_dict.iteritems():
1274 if ptag_dict_val != []:
1275 cur_surf = get_surf(surf_list, cur_tag)
1276 cur_obj, not_used_faces= my_create_mesh(clip_list, cur_surf, objspec_list, ptag_dict_val, objspec_list[0][:9]+middlechar+cur_tag[:9], not_used_faces)
1277 # Works now with new modifiers
1278 if objspec_list[6] == 1:
1279 set_subsurf(cur_obj)
1280 if cur_obj: # != None
1281 obj_dict[cur_tag] = cur_obj
1282 obj_dim_dict[cur_tag] = obj_size_pos(cur_obj)
1283 obj_list.append(cur_obj)
1284 #end loop on current group
1285 #and what if some faces not used in any named PTAG? get rid of unused faces
1287 for tt in not_used_faces:
1288 if tt > -1: orphans.append(tt)
1289 #end sweep on unused face list
1290 not_used_faces = None
1292 cur_surf = get_surf(surf_list, "_Orphans")
1293 cur_obj, not_used_faces = my_create_mesh(clip_list, cur_surf, objspec_list, orphans, objspec_list[0][:9]+middlechar+"Orphans", [])
1294 if cur_obj: # != None
1295 if objspec_list[6] == 1:
1296 set_subsurf(cur_obj)
1297 obj_dict["_Orphans"] = cur_obj
1298 obj_dim_dict["_Orphans"] = obj_size_pos(cur_obj)
1299 obj_list.append(cur_obj)
1300 objspec_list[1]= obj_dict
1301 objspec_list[4]= obj_dim_dict
1307 # ===========================================
1308 # === Lookup for image index in clip_list ===
1309 # ===========================================
1310 def lookup_imag(clip_list, ima_id):
1311 for ii in clip_list:
1312 if ii and ii['ID'] == ima_id:
1313 if 'XREF' in ii: # has_key
1314 #cross reference - recursively look for images
1315 return lookup_imag(clip_list, ii['XREF'])
1321 # ===================================================
1322 # === Create and assign image mapping to material ===
1323 # ===================================================
1324 def create_blok(surf, mat, clip_list, obj_size, obj_pos):
1326 def output_size_ofs(size, pos, blok):
1327 #just automate repetitive task
1328 # 0 == X, 1 == Y, 2 == Z
1329 size_default = [1.0] * 3
1331 ofs_default = [0.0] * 3
1333 axis_default = [Blender.Texture.Proj.X, Blender.Texture.Proj.Y, Blender.Texture.Proj.Z]
1335 c_map_txt = [" X--", " -Y-", " --Z"]
1336 c_map = [0,1,2] # standard, good for Z axis projection
1337 if blok['MAJAXIS'] == 0:
1338 c_map = [1,2,0] # X axis projection
1339 if blok['MAJAXIS'] == 2:
1340 c_map = [0,2,1] # Y axis projection
1342 ###if DEBUG: print "!!!axis mapping:"
1343 #this is the smart way
1344 ###if DEBUG: for mp in c_map: print c_map_txt[mp]
1346 if blok['SIZE'][0] != 0.0: #paranoia controls
1347 size_default[0] = (size[0]/blok['SIZE'][0])
1348 ofs_default[0] = ((blok['CNTR'][0]-pos[0])/blok['SIZE'][0])
1349 if blok['SIZE'][1] != 0.0:
1350 size_default[2] = (size[2]/blok['SIZE'][1])
1351 ofs_default[2] = ((blok['CNTR'][1]-pos[2])/blok['SIZE'][1])
1352 if blok['SIZE'][2] != 0.0:
1353 size_default[1] = (size[1]/blok['SIZE'][2])
1354 ofs_default[1] = ((blok['CNTR'][2]-pos[1])/blok['SIZE'][2])
1356 for mp in xrange(3):
1357 axis[mp] = axis_default[c_map[mp]]
1358 size2[mp] = size_default[c_map[mp]]
1359 offset[mp] = ofs_default[c_map[mp]]
1360 if offset[mp]>10.0: offset[mp]-10.0
1361 if offset[mp]<-10.0: offset[mp]+10.0
1362 # size = [size_default[mp] for mp in c_map]
1364 ###if DEBUG: print "!!!texture size and offsets:"
1365 ###if DEBUG: print " sizeX = %.5f; sizeY = %.5f; sizeZ = %.5f" % (size[0],size[1],size[2])
1366 ###if DEBUG: print " ofsX = %.5f; ofsY = %.5f; ofsZ = %.5f" % (offset[0],offset[1],offset[2])
1367 return axis, size2, offset
1370 alphaflag = 0 #switched to 1 if some tex in this block is using alpha
1371 lastimag = 0 #experimental ....
1372 for blok in surf['BLOK']:
1373 ###if DEBUG: print "#...................................................................#"
1374 ###if DEBUG: print "# Processing texture block no.%s for surf %s" % (ti,surf['NAME'])
1375 ###if DEBUG: print "#...................................................................#"
1377 if ti > 9: break #only 8 channels 0..7 allowed for texture mapping
1378 #if not blok['ENAB']:
1379 # ###if DEBUG: print "***Image is not ENABled! Quitting this block"
1381 if not('IMAG' in blok): # has_key
1382 ###if DEBUG: print "***No IMAGE for this block? Quitting"
1383 break #extract out the image index within the clip_list
1384 if blok['IMAG'] == 0: blok['IMAG'] = lastimag #experimental ....
1385 ###if DEBUG: print "looking for image number %d" % blok['IMAG']
1386 ima = lookup_imag(clip_list, blok['IMAG'])
1388 ###if DEBUG: print "***Block index image not within CLIP list? Quitting Block"
1389 break #safety check (paranoia setting)
1391 lastimag = blok['IMAG'] #experimental ....
1393 ###if DEBUG: print "***Failed to pre-allocate image %s found: giving up" % ima['BASENAME']
1395 tname = str(ima['ID'])
1399 tname += "x" #let's signal when should not be enabled
1400 if 'CHAN' in blok: # has_key
1401 tname += blok['CHAN']
1402 newtex = bpy.data.textures.new(tname)
1403 newtex.setType('Image') # make it anu image texture
1405 #how does it extends beyond borders
1406 if 'WRAP' in blok: # has_key
1407 if (blok['WRAP'] == 3) or (blok['WRAP'] == 2):
1408 newtex.setExtend('Extend')
1409 elif (blok['WRAP'] == 1):
1410 newtex.setExtend('Repeat')
1411 elif (blok['WRAP'] == 0):
1412 newtex.setExtend('Clip')
1413 ###if DEBUG: print "generated texture %s" % tname
1415 #MapTo is determined by CHAN parameter
1416 #assign some defaults
1421 mapflag = Blender.Texture.MapTo.COL #default to color
1422 maptype = Blender.Texture.Mappings.FLAT
1423 if 'CHAN' in blok: # has_key
1424 if blok['CHAN'] == 'COLR' and 'OPACVAL' in blok: # has_key
1425 colfac = blok['OPACVAL']
1426 # Blender needs this to be clamped
1427 colfac = max(0.0, min(1.0, colfac))
1428 ###if DEBUG: print "!!!Set Texture -> MapTo -> Col = %.3f" % colfac
1429 if blok['CHAN'] == 'BUMP':
1430 mapflag = Blender.Texture.MapTo.NOR
1431 if 'OPACVAL' in blok: norfac = blok['OPACVAL'] # has_key
1432 ###if DEBUG: print "!!!Set Texture -> MapTo -> Nor = %.3f" % norfac
1433 if blok['CHAN'] == 'LUMI':
1434 mapflag = Blender.Texture.MapTo.EMIT
1435 if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key
1436 ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar
1437 if blok['CHAN'] == 'DIFF':
1438 mapflag = Blender.Texture.MapTo.REF
1439 if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key
1440 ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar
1441 if blok['CHAN'] == 'SPEC':
1442 mapflag = Blender.Texture.MapTo.SPEC
1443 if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key
1444 ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar
1445 if blok['CHAN'] == 'TRAN':
1446 mapflag = Blender.Texture.MapTo.ALPHA
1447 if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key
1448 ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar
1451 if 'NEGA' in blok: # has_key
1452 ###if DEBUG: print "!!!Watch-out: effect of this texture channel must be INVERTED!"
1455 blendmode_list = ['Mix',
1460 'Mix with calculated alpha layer and stencil flag',
1461 'Texture Displacement',
1463 set_blendmode = 7 #default additive
1464 if 'OPAC' in blok: # has_key
1465 set_blendmode = blok['OPAC']
1466 if set_blendmode == 5: #transparency
1467 newtex.imageFlags |= Blender.Texture.ImageFlags.CALCALPHA
1468 if nega: newtex.flags |= Blender.Texture.Flags.NEGALPHA
1469 ###if DEBUG: print "!!!Set Texture -> MapTo -> Blending Mode = %s" % blendmode_list[set_blendmode]
1471 #the TexCo flag is determined by PROJ parameter
1472 axis = [Blender.Texture.Proj.X, Blender.Texture.Proj.Y, Blender.Texture.Proj.Z]
1475 if 'PROJ' in blok: # has_key
1476 if blok['PROJ'] == 0: #0 - Planar
1477 ###if DEBUG: print "!!!Flat projection"
1478 coordflag = Blender.Texture.TexCo.ORCO
1479 maptype = Blender.Texture.Mappings.FLAT
1480 elif blok['PROJ'] == 1: #1 - Cylindrical
1481 ###if DEBUG: print "!!!Cylindrical projection"
1482 coordflag = Blender.Texture.TexCo.ORCO
1483 maptype = Blender.Texture.Mappings.TUBE
1484 elif blok['PROJ'] == 2: #2 - Spherical
1485 ###if DEBUG: print "!!!Spherical projection"
1486 coordflag = Blender.Texture.TexCo.ORCO
1487 maptype = Blender.Texture.Mappings.SPHERE
1488 elif blok['PROJ'] == 3: #3 - Cubic
1489 ###if DEBUG: print "!!!Cubic projection"
1490 coordflag = Blender.Texture.TexCo.ORCO
1491 maptype = Blender.Texture.Mappings.CUBE
1492 elif blok['PROJ'] == 4: #4 - Front Projection
1493 ###if DEBUG: print "!!!Front projection"
1494 coordflag = Blender.Texture.TexCo.ORCO
1495 maptype = Blender.Texture.Mappings.FLAT # ??? could it be a FLAT with some other TexCo type?
1496 elif blok['PROJ'] == 5: #5 - UV
1497 ###if DEBUG: print "UVMapped"
1498 coordflag = Blender.Texture.TexCo.UV
1499 maptype = Blender.Texture.Mappings.FLAT #in case of UV default to FLAT mapping => effectively not used
1500 if blok['PROJ'] != 5: #This holds for any projection map except UV
1501 axis, size, ofs = output_size_ofs(obj_size, obj_pos, blok)
1503 # Clamp ofs and size else blender will raise an error
1504 for ii in xrange(3):
1505 ofs[ii]= min(10.0, max(-10, ofs[ii]))
1506 size[ii]= min(100, max(-100, size[ii]))
1508 mat.setTexture(ti, newtex, coordflag, mapflag)
1509 current_mtex = mat.getTextures()[ti]
1510 current_mtex.mapping = maptype
1511 current_mtex.colfac = colfac
1512 current_mtex.dvar = dvar
1513 current_mtex.norfac = norfac
1514 current_mtex.neg = nega
1515 current_mtex.xproj = axis[0]
1516 current_mtex.yproj = axis[1]
1517 current_mtex.zproj = axis[2]
1518 current_mtex.size = tuple(size)
1519 current_mtex.ofs = tuple(ofs)
1520 if (set_blendmode == 5): #transparency
1521 current_mtex.stencil = not (nega)
1524 #end loop over bloks
1528 # ========================================
1529 # === Create and assign a new material ===
1530 # ========================================
1531 #def update_material(surf_list, ptag_dict, obj, clip_list, uv_dict, dir_part):
1532 def update_material(clip_list, objspec, surf_list):
1533 if (surf_list == []) or (objspec[5] == {}) or (objspec[1] == {}):
1534 ###if DEBUG: print "something getting wrong in update_material: dump follows ..."
1535 ###if DEBUG: print surf_list
1536 ###if DEBUG: print objspec[5]
1537 ###if DEBUG: print objspec[1]
1539 obj_dict = objspec[1]
1540 all_faces = objspec[3]
1541 obj_dim_dict = objspec[4]
1542 ptag_dict = objspec[5]
1543 uvcoords_dict = objspec[7]
1544 facesuv_dict = objspec[8]
1545 for surf in surf_list:
1546 if surf and surf['NAME'] in ptag_dict: # in ptag_dict.keys()
1547 ###if DEBUG: print "#-------------------------------------------------------------------#"
1548 ###if DEBUG: print "Processing surface (material): %s" % surf['NAME']
1549 ###if DEBUG: print "#-------------------------------------------------------------------#"
1551 facelist = ptag_dict[surf['NAME']]
1552 #bounding box and position
1553 cur_obj = obj_dict[surf['NAME']]
1554 obj_size = obj_dim_dict[surf['NAME']][0]
1555 obj_pos = obj_dim_dict[surf['NAME']][1]
1556 ###if DEBUG: print surf
1557 #uncomment this if material pre-allocated by read_surf
1560 ###if DEBUG: print "Sorry, no pre-allocated material to update. Giving up for %s." % surf['NAME']
1562 #mat = Blender.Material.New(surf['NAME'])
1563 #surf['g_MAT'] = mat
1564 if 'COLR' in surf: # has_key
1565 mat.rgbCol = surf['COLR']
1567 mat.setEmit(surf['LUMI'])
1568 if 'GVAL' in surf: # has_key
1569 mat.setAdd(surf['GVAL'])
1570 if 'SPEC' in surf: # has_key
1571 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]
1572 if 'DIFF' in surf: # has_key
1573 mat.setRef(surf['DIFF']) #lwo [0.0, 1.0] - blender [0.0, 1.0]
1574 if 'GLOS' in surf: # has_key #lwo [0.0, 1.0] - blender [0, 255]
1575 glo = int(371.67 * surf['GLOS'] - 42.334) #linear mapping - seems to work better than exp mapping
1576 if glo <32: glo = 32 #clamped to 32-255
1577 if glo >255: glo = 255
1578 mat.setHardness(glo)
1579 if 'TRNL' in surf: # has_key
1580 mat.setTranslucency(surf['TRNL']) #NOT SURE ABOUT THIS lwo [0.0, 1.0] - blender [0.0, 1.0]
1583 mm |= Blender.Material.Modes.TRANSPSHADOW
1584 if 'REFL' in surf: # has_key
1585 mat.setRayMirr(surf['REFL']) #lwo [0.0, 1.0] - blender [0.0, 1.0]
1586 mm |= Blender.Material.Modes.RAYMIRROR
1587 if 'TRAN' in surf: # has_key
1588 mat.setAlpha(1.0-surf['TRAN']) #lwo [0.0, 1.0] - blender [1.0, 0.0]
1589 mm |= Blender.Material.Modes.RAYTRANSP
1590 if 'RIND' in surf: # has_key
1594 mat.setIOR(s) #clipped to blender [1.0, 3.0]
1595 mm |= Blender.Material.Modes.RAYTRANSP
1596 if 'BLOK' in surf and surf['BLOK'] != []:
1597 #update the material according to texture.
1598 alphaflag = create_blok(surf, mat, clip_list, obj_size, obj_pos)
1600 mm |= Blender.Material.Modes.RAYTRANSP
1602 #finished setting up the material
1604 #end loop on materials (SURFs)
1608 # ======================
1609 # === Read Faces 6.0 ===
1610 # ======================
1611 def read_faces_6(lwochunk):
1612 data = cStringIO.StringIO(lwochunk.read())
1614 polygon_type = data.read(4)
1616 if polygon_type != "FACE" and polygon_type != "PTCH":
1617 ###if DEBUG: print "No FACE/PATCH Were Found. Polygon Type: %s" % polygon_type
1619 if polygon_type == 'PTCH': subsurf = 1
1621 while(i < lwochunk.chunksize-4):
1622 #if not i%1000 and my_meshtools.show_progress:
1623 # Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
1625 numfaceverts, = struct.unpack(">H", data.read(2))
1628 for j in xrange(numfaceverts):
1629 index, index_size = read_vx(data)
1633 ###if DEBUG: print "read %s faces; type of block %d (0=FACE; 1=PATCH)" % (len(faces), subsurf)
1634 return faces, subsurf
1638 Blender.Draw.PupMenu('This importer requires a full python install')
1641 Blender.Window.FileSelector(read, "Import LWO", '*.lwo')
1643 if __name__=='__main__':
1647 # Cams debugging lwo loader
1649 TIME= Blender.sys.time()
1651 print 'Searching for files'
1652 os.system('find /fe/lwo/Objects/ -follow -iname "*.lwo" > /tmp/templwo_list')
1653 # os.system('find /storage/ -iname "*.lwo" > /tmp/templwo_list')
1655 file= open('/tmp/templwo_list', 'r')
1656 lines= file.readlines()
1658 # sort by filesize for faster testing
1659 lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines]
1661 lines = [f[1] for f in lines_size]
1666 if v <= max(a,b) and v >= min(a,b):
1671 for i, _lwo in enumerate(lines):
1672 #if i==425: # SCANFILL
1674 #if i==520: # SCANFILL CRASH
1675 #if i==47: # SCANFILL CRASH
1676 #if between(i, 525, 550):
1678 #if i != 1519: # 730
1682 print 'Importing', _lwo, '\nNUMBER', i, 'of', len(lines)
1683 _lwo_file= _lwo.split('/')[-1].split('\\')[-1]
1684 newScn= bpy.data.scenes.new(_lwo_file)
1685 bpy.data.scenes.active = newScn
1686 size += ((os.path.getsize(_lwo)/1024.0))/ 1024.0
1688 # Remove objects to save memory?
1690 for ob in newScn.objects:
1692 me= ob.getData(mesh=1)
1696 print 'mb size so far', size
1698 print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME)