Scripts:
authorWillian Padovani Germano <wpgermano@gmail.com>
Tue, 11 Oct 2005 02:32:58 +0000 (02:32 +0000)
committerWillian Padovani Germano <wpgermano@gmail.com>
Tue, 11 Oct 2005 02:32:58 +0000 (02:32 +0000)
- updating some bundled scripts, thanks to authors Jean-Michel Soler, Campbell Barton and Anthony D'Agostino.

BPython:
- removing wrong fix from BGL.c's glDrawPixels.

note: applied guitargeek's setName patch to Blender.Key, but saw that he updated it with more functionality and assigned to stivs, so I won't commit this old version.

12 files changed:
release/scripts/console.py
release/scripts/disp_paint.py
release/scripts/hotkeys.py
release/scripts/lightwave_export.py
release/scripts/lightwave_import.py
release/scripts/obj_export.py
release/scripts/obj_import.py
release/scripts/skin.py
release/scripts/tex2uvbaker.py
release/scripts/uvpaint.py [moved from release/scripts/UVpaint05.py with 51% similarity]
release/scripts/wings_export.py
source/blender/python/api2_2x/BGL.c

index e95d6bc34b12bb7dcb1a3de8d5ec1d6097300fc0..4ce58e06e10ed387e6d263949507f6d17129d321 100644 (file)
@@ -18,11 +18,16 @@ that points to its official homepage, with news, downloads and documentation.
 Usage:<br>
   Type your code and hit "Enter" to get it executed.<br>
   - Right mouse click: Console Menu (Save output, etc);<br>
+  - Mousewheel: Scroll text
   - Arrow keys: command history and cursor;<br>
-  - Shift + arrow keys: jump words;<br>
+  - Shift + Backspace: Backspace whole word;<br>
+  - Shift + Arrow keys: jump words;<br>
+  - Ctrl + (+/- or mousewheel): Zoom text size;<br>
   - Ctrl + Tab: auto compleate based on variable names and modules loaded -- multiple choices popup a menu;<br>
   - Ctrl + Enter: multiline functions -- delays executing code until only Enter is pressed.
 """
+__author__ = "Campbell Barton AKA Ideasman"
+__url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
 
 import Blender
 from Blender import *
@@ -31,14 +36,18 @@ import StringIO
 import types
 
 # Constants
-__DELIMETERS__ = '. ,=+-*/%<>&~][{}():'
-__LINE_HISTORY__ = 200
+__DELIMETERS__ = '. ,=+-*/%<>&~][{}():\t'
+__VARIABLE_DELIMETERS__ = ' ,=+-*/%<>&~{}():\t'
+
+__LINE_HISTORY__ = 500
 
-global __LINE_HEIGHT__
-__LINE_HEIGHT__ = 14
 global __FONT_SIZE__
-__FONT_SIZE__ = "normal"
 
+__FONT_SIZES__ = ( ('tiny', 10), ('small', 12), ('normal', 14), ('large', 16) )
+__FONT_SIZE__ = 2 # index for the list above, normal default.
+
+global __CONSOLE_LINE_OFFSET__
+__CONSOLE_LINE_OFFSET__ = 0
 
 '''
 # Generic Blender functions
@@ -52,10 +61,93 @@ def getActScriptWinRect():
        return None
 '''
 
-class cmdLine:
-       # cmd: is the command string, or any other message
-       # type: 0:user input  1:program feedback  2:error message.  3:option feedback
-       # exe; 0- not yet executed   1:executed
+
+# menuText, # per group
+def PupMenuLess(menu, groupSize=35):
+       more = ['   more...']
+       less = ['   less...']
+       
+       menuList= menu.split('|')
+       
+       # No Less Needed, just call.
+       if len(menuList) < groupSize:
+               return Draw.PupMenu(menu)
+       
+       title = menuList[0].split('%t')[0]
+       
+       # Split the list into groups
+       menuGroups = [[]]
+       for li in menuList[1:]:
+               if len(menuGroups[-1]) < groupSize:
+                       menuGroups[-1].append(li)
+               else:
+                       menuGroups.append([li])
+       
+       # Stores teh current menu group we are looking at
+       groupIdx = 0
+       while 1:
+               # Give us a title with the menu number
+               numTitle = [ ' '.join([title, str(groupIdx + 1), 'of', str(len(menuGroups)), '%t'])]
+               if groupIdx == 0:
+                       menuString = '|'.join(numTitle + menuGroups[groupIdx] + more)
+               elif groupIdx == len(menuGroups)-1:
+                       menuString = '|'.join(numTitle + less + menuGroups[groupIdx])
+               else: # In the middle somewhere so Show a more and less
+                       menuString = '|'.join(numTitle + less + menuGroups[groupIdx] + more)
+               result = Draw.PupMenu(menuString)
+               # User Exit
+               if result == -1:
+                       return -1
+               
+               if groupIdx == 0: # First menu
+                       if result-1 < groupSize:
+                               return result
+                       else: # must be more
+                               groupIdx +=1
+               elif groupIdx == len(menuGroups): # Last Menu
+                       if result == 1: # Must be less
+                               groupIdx -= 1
+                       else: # Must be a choice
+                               return result + (groupIdx*groupSize)
+                       
+               else:   
+                       if result == 1: # Must be less
+                               groupIdx -= 1
+                       elif result-2 == groupSize:
+                               groupIdx +=1
+                       else:
+                               return result - 1 + (groupIdx*groupSize)
+                               
+
+
+def unzip(list):
+
+       """
+               unzip: inverse of zip - converts a list of tuples into a tuple of lists
+               
+               e.g.
+                a,b = unzip(zip(a,b))
+       
+               * note: all tuples in list have to have the same length, if not,
+                               this function will fail
+       """
+       
+       if len(list) == 0: return ()
+       l = []
+       for t in range(len(list[0])):
+               l.append(map( lambda x,t=t: x[t], list ))
+       return tuple(l)
+
+
+
+# Use newstyle classes, Im not bothering with inheretence
+# but slots are faster.
+class cmdLine(object):
+       __slots__ = [\
+       'cmd', # is the command string, or any other message
+       'type',# type: 0:user input  1:program feedback  2:error message.  3:option feedback
+       'exe' #  0- not yet executed   1:executed
+       ]
        def __init__(self, cmd, type, exe):
                self.cmd = cmd
                self.type = type
@@ -97,32 +189,68 @@ def insertCmdData(cmdBuffer):
 COLLECTED_VAR_NAMES = {} # a list of keys, each key has a list of absolute paths
 
 # Pain and simple recursice dir(), accepts a string
-def rdir(dirString):
+def rdir(dirString, depth=0):
+       
+       # MAX DEPTH SET HERE
+       if depth > 4:
+               print 'maxdepoth reached.'
+               return
+               
        global COLLECTED_VAR_NAMES
        dirStringSplit = dirString.split('.')
        
        exec('dirList = dir(%s)' % dirString) 
        for dirItem in dirList:
-               if not dirItem.startswith('_'):
-                       if dirItem not in COLLECTED_VAR_NAMES.keys():
-                               COLLECTED_VAR_NAMES[dirItem] = []
+               if dirItem.startswith('_'):
+                       continue
                        
-                       # Add the string
-                       splitD = dirString.split('"')[-2]
-                       if splitD not in COLLECTED_VAR_NAMES[dirItem]:
-                               COLLECTED_VAR_NAMES[dirItem].append(splitD)
-                       
-                       
-                               
-                       # Stops recursice stuff, overlooping
-                       if type(dirItem) == types.ClassType or \
-                                type(dirItem) == types.ModuleType:
-                               
-                               print dirString, splitD, dirItem
-                               
-                               # Dont loop up dirs for strings ints etc.
-                               if d not in dirStringSplit:
-                                       rdir( '%s.%s' % (dirString, d)) 
+               dirData = None
+               try:
+                       # Rare cases this can mess up, material.shader was a problem.
+                       exec('dirData = %s.%s' % (dirString, dirItem))
+                       #print dirData
+               except:
+                       # Dont bother with this data.
+                       continue
+               
+               if type(dirItem) != type('str'):
+                       print dirItem, type(dirItem)
+               
+               if dirItem not in COLLECTED_VAR_NAMES.keys():
+                       COLLECTED_VAR_NAMES[dirItem] = []
+               
+               # Add the string
+               splitD = dirString.split('"')[-2]
+               if splitD not in COLLECTED_VAR_NAMES[dirItem]:
+                       COLLECTED_VAR_NAMES[dirItem].append(splitD)
+               
+               
+               # Stops recursice stuff, overlooping
+               #print type(dirItem)
+               #if type(dirData) == types.ClassType or \
+               #        type(dirData) == types.ModuleType:
+               
+               if type(dirData) != types.StringType and\
+               type(dirData) != types.DictType and\
+               type(dirData) != types.DictionaryType and\
+               type(dirData) != types.FloatType and\
+               type(dirData) != types.IntType and\
+               type(dirData) != types.NoneType and\
+               type(dirData) != types.StringTypes and\
+               type(dirData) != types.TypeType and\
+               type(dirData) != types.TupleType and\
+               type(dirData) != types.BuiltinFunctionType:
+                       # print type(dirData), dirItem
+                       # Dont loop up dirs for strings ints etc.
+                       if dirItem not in dirStringSplit:
+                               rdir( '%s.%s' % (dirString, dirItem), depth+1)
+               '''
+               elif depth == 0: # Add local variables
+                       # print type(dirData), dirItem
+                       # Dont loop up dirs for strings ints etc.
+                       if dirItem not in dirStringSplit:
+                               rdir( '%s.%s' % (dirString, dirItem), depth+1)
+               '''
 
 def recursive_dir():
        global COLLECTED_VAR_NAMES
@@ -148,13 +276,17 @@ def runUserCode(__USER_CODE_STRING__):
        # Try and run the user entered line(s)
        try:
                # Load all variabls from global dict to local space.
-               for __TMP_VAR_NAME__ in __CONSOLE_VAR_DICT__.keys():
-                       exec('%s%s%s%s' % (__TMP_VAR_NAME__,'=__CONSOLE_VAR_DICT__["', __TMP_VAR_NAME__, '"]'))
+               for __TMP_VAR_NAME__, __TMP_VAR__ in __CONSOLE_VAR_DICT__.items():
+                       exec('%s%s' % (__TMP_VAR_NAME__,'=__TMP_VAR__'))
                del __TMP_VAR_NAME__
+               del __TMP_VAR__
                
                # Now all the vars are loaded, execute the code. # Newline thanks to phillip,
                exec(compile(__USER_CODE_STRING__, 'blender_cmd.py', 'single')) #exec(compile(__USER_CODE_STRING__, 'blender_cmd.py', 'exec'))
                
+               # Flush global dict, allow the user to remove items.
+               __CONSOLE_VAR_DICT__ = {}
+               
                # Write local veriables to global __CONSOLE_VAR_DICT__
                for __TMP_VAR_NAME__ in dir():
                        if      __TMP_VAR_NAME__ != '__FILE_LIKE_STRING__' and\
@@ -167,14 +299,16 @@ def runUserCode(__USER_CODE_STRING__):
                del __TMP_VAR_NAME__
        
        except: # Prints the REAL exception.
-               error = str(python_sys.exc_value)
+               error = '%s:  %s' % (python_sys.exc_type, python_sys.exc_value)         
                for errorLine in error.split('\n'):
                        cmdBuffer.append(cmdLine(errorLine, 2, None)) # new line to type into
        
        python_sys.stdout = __STD_OUTPUT__ # Go back to output to the normal blender console
        
        # Copy all new output to cmdBuffer
+       
        __FILE_LIKE_STRING__.seek(0) # the readline function requires that we go back to the start of the file.
+       
        for line in __FILE_LIKE_STRING__.readlines():
                cmdBuffer.append(cmdLine(line, 1, None))
                
@@ -203,34 +337,61 @@ def handle_event(evt, val):
        #------------------------------------------------------------------------------#
        def actionEnterKey():
                global histIndex, cursor, cmdBuffer
-               # Check for the neter kay hit
-               if Window.GetKeyQualifiers() & Window.Qual.CTRL: # HOLDING DOWN SHIFT, GO TO NEXT LINE.
-                       cmdBuffer.append(cmdLine(' ', 0, 0))
-               else:
+               
+               def getIndent(string):
+                       # Gather white space to add in the previous line
+                       # Ignore the last char since its padding.
+                       whiteSpace = ''
+                       #for i in range(len(cmdBuffer[-1].cmd)):
+                       for i in range(len(string)-1):
+                               if cmdBuffer[-1].cmd[i] == ' ' or cmdBuffer[-1].cmd[i] == '\t':
+                                       whiteSpace += string[i]
+                               else:
+                                       break
+                       return whiteSpace
+               
+               # Are we in the moddle of a multiline part or not?
+               # try be smart about it
+               if cmdBuffer[-1].cmd.split('#')[0].rstrip().endswith(':'):
+                       # : indicates an indent is needed
+                       cmdBuffer.append(cmdLine('\t%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
+                       print ': indicates an indent is needed'         
+               
+               elif cmdBuffer[-1].cmd[0] in [' ', '\t'] and len(cmdBuffer[-1].cmd) > 1 and cmdBuffer[-1].cmd.split():
+                       # white space at the start means he havnt finished the multiline.
+                       cmdBuffer.append(cmdLine('%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
+                       print 'white space at the start means he havnt finished the multiline.'
+               
+               elif Window.GetKeyQualifiers() & Window.Qual.CTRL:
+                       # Crtl forces multiline
+                       cmdBuffer.append(cmdLine('%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
+                       print 'Crtl forces multiline'                   
+               
+               else: # Execute multiline code block
+                       
                        # Multiline code will still run with 1 line,
-                       multiLineCode = ['if 1:'] 
-                       if cmdBuffer[-1].cmd != ' ':
-                               multiLineCode = ['%s%s' % (' ', cmdBuffer[-1].cmd)] # added space for fake if.
-                       else:
-                               cmdBuffer[-1].type = 1
-                               multiLineCode = []
-                       cmdBuffer[-1].exe = 1
-                       i = 2
+                       multiLineCode = ['if 1:'] # End of the multiline first.
+                       
+                       # Seek the start of the file multiline
+                       i = 1
                        while cmdBuffer[-i].exe == 0:
+                               i+=1
+                       
+                       while i > 1:
+                               i-=1
+                               
                                if cmdBuffer[-i].cmd == ' ':# Tag as an output type so its not used in the key history
                                        cmdBuffer[-i].type = 1
-                               else: # space added at the start for added if 1: statement
-                                       multiLineCode.append('%s%s' % (' ', cmdBuffer[-i].cmd) )
+                               else: # Tab added at the start for added if 1: statement
+                                       multiLineCode.append('\t%s' % cmdBuffer[-i].cmd )
                                
                                # Mark as executed
-                               cmdBuffer[-i].exe = 1
-                               i+=1
-                       
-                       # add if to the end, reverse will make it the start.
-                       multiLineCode.append('if 1:')
-                       multiLineCode.reverse()
-                       multiLineCode.append(' pass') # Now this is the end
+                               cmdBuffer[-i].exe = 1                           
+                               
+                       multiLineCode.append('\tpass') # reverse will make this the start.                      
                        
+                       # Dubug, print the code that is executed.
+                       #for m in multiLineCode: print m
                        
                        runUserCode('\n'.join(multiLineCode))
                        
@@ -262,9 +423,8 @@ def handle_event(evt, val):
        
        def actionRightMouse():
                global __FONT_SIZE__
-               global __LINE_HEIGHT__
-               choice = Draw.PupMenu('Console Menu%t|Write Input Data (white)|Write Output Data (blue)|Write Error Data (red)|Write All Text|%l|Insert Blender text|%l|Font Size|%l|Help|%l|Quit')
-               # print choice
+               choice = Draw.PupMenu('Console Menu%t|Write Input Data (white)|Write Output Data (blue)|Write Error Data (red)|Write All Text|%l|Insert Blender text|%l|Font Size|%l|Quit')
+               
                if choice == 1:
                        writeCmdData(cmdBuffer, 0) # type 0 user
                elif choice == 2:
@@ -274,55 +434,56 @@ def handle_event(evt, val):
                elif choice == 4:
                        writeCmdData(cmdBuffer, 3) # All
                elif choice == 6:
-                       insertCmdData(cmdBuffer) # All
+                       insertCmdData(cmdBuffer) # Insert text from Blender and run it.
                elif choice == 8:
                        # Fontsize.
                        font_choice = Draw.PupMenu('Font Size%t|Large|Normal|Small|Tiny')
                        if font_choice != -1:
                                if font_choice == 1:
-                                       __FONT_SIZE__ = 'large'
-                                       __LINE_HEIGHT__ = 16
+                                       __FONT_SIZE__ = 3
                                elif font_choice == 2:
-                                       __FONT_SIZE__ = 'normal'
-                                       __LINE_HEIGHT__ = 14
+                                       __FONT_SIZE__ = 2
                                elif font_choice == 3:
-                                       __FONT_SIZE__ = 'small'
-                                       __LINE_HEIGHT__ = 12
+                                       __FONT_SIZE__ = 1
                                elif font_choice == 4:
-                                       __FONT_SIZE__ = 'tiny'
-                                       __LINE_HEIGHT__ = 10
+                                       __FONT_SIZE__ = 0
                                Draw.Redraw()
-               elif choice == 10:
-                       Blender.ShowHelp('console.py')
-               elif choice == 12: # Exit
+                               
+               elif choice == 10: # Exit
                        Draw.Exit()
        
        
        # Auto compleating, quite complex- use recutsice dir for the moment.
        def actionAutoCompleate(): # Ctrl + Tab
+               if not cmdBuffer[-1].cmd[:cursor].split():
+                       return
+               
+               
                RECURSIVE_DIR = recursive_dir()
                
                # get last name of user input
                editVar = cmdBuffer[-1].cmd[:cursor]
-               
                # Split off spaces operators etc from the staryt of the command so we can use the startswith function.
-               for splitChar in __DELIMETERS__:
-                       editVar = editVar.split(splitChar)[-1]
+               for splitChar in __VARIABLE_DELIMETERS__:
+                       editVar = editVar[:-1].split(splitChar)[-1] + editVar[-1]
+               
                
                # Now we should have the var by its self
                if editVar:
-                       
                        possibilities = []
                        
-                       print editVar, 'editVar'
                        for __TMP_VAR_NAME__ in RECURSIVE_DIR.keys():
+                               #print '\t', __TMP_VAR_NAME__
                                if __TMP_VAR_NAME__ == editVar:
                                        # print 'ADITVAR IS A VAR'
-                                       continue
+                                       pass
+                               '''
                                elif __TMP_VAR_NAME__.startswith( editVar ):
+                                       print __TMP_VAR_NAME__, 'aaa'
                                        possibilities.append( __TMP_VAR_NAME__ )
-                                       
-                                       
+                               '''
+                               possibilities.append( __TMP_VAR_NAME__ )
+                       
                        if len(possibilities) == 1:
                                cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], possibilities[0], cmdBuffer[-1].cmd[cursor:]))    
                        
@@ -332,36 +493,50 @@ def handle_event(evt, val):
                                # Text choice
                                #cmdBuffer.insert(-1, cmdLine('options: %s' % ' '.join(possibilities), 3, None))
                                
-                               menuText = 'Choices (hold shift for whole name)%t|' 
-                               menuList = []
-                               menuListAbs = []
-                               possibilities.sort() # make nice :)
+                               menuList = [] # A lits of tuples- ABSOLUTE, RELATIVE
+                               
                                for __TMP_VAR_NAME__ in possibilities:
                                        for usage in RECURSIVE_DIR[__TMP_VAR_NAME__]:
                                                # Account for non absolute (variables for eg.)
                                                if usage: # not ''
-                                                       menuListAbs.append('%s.%s' % (usage, __TMP_VAR_NAME__)) # Used for names and can be entered when pressing shift.
-                                               else:
-                                                       menuListAbs.append(__TMP_VAR_NAME__) # Used for names and can be entered when pressing shift.
-                                                       
-                                               menuList.append(__TMP_VAR_NAME__) # Used for non Shift
+                                                       absName = '%s.%s' % (usage, __TMP_VAR_NAME__)
+                                                       if absName.find('.'+editVar) != -1 or\
+                                                       absName.startswith(editVar) or\
+                                                       __TMP_VAR_NAME__.startswith(editVar):
+                                                               #print editVar, 'found in', absName
+                                                               menuList.append( # Used for names and can be entered when pressing shift.
+                                                                 (absName, # Absolute name
+                                                                 __TMP_VAR_NAME__) # Relative name, non shift
+                                                                 )
+                                               #else:
+                                               #       if absName.find(editVar) != -1:
+                                               #               menuList.append((__TMP_VAR_NAME__, __TMP_VAR_NAME__)) # Used for names and can be entered when pressing shift.
+                               
+                               # No items to display? no menu
+                               if not menuList:
+                                       return
+                                       
+                               menuList.sort()
                                
+                               choice = PupMenuLess( # Menu for the user to choose the autocompleate
+                               'Choices (Shift for Whole name, Ctrl for Docs)%t|' + # Title Text
+                               '|'.join(['%s,  %s' % m for m in menuList])) # Use Absolute names m[0]
                                
-                               #choice = Draw.PupMenu('Select Variabe name%t|' + '|'.join(possibilities)  )
-                               choice = Draw.PupMenu(menuText + '|'.join(menuListAbs))
                                if choice != -1:
-                                       
-                                       if not Window.GetKeyQualifiers() & Window.Qual.SHIFT: # Only paste in the Short name
-                                               cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1], cmdBuffer[-1].cmd[cursor:]))    
-                                       else: # Put in the long name
-                                               cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuListAbs[choice-1], cmdBuffer[-1].cmd[cursor:]))    
+                                       if Window.GetKeyQualifiers() & Window.Qual.CTRL:  # Help
+                                               cmdBuffer[-1].cmd = ('help(%s%s) ' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1][0]))    
+                                       elif Window.GetKeyQualifiers() & Window.Qual.SHIFT:  # Put in the long name
+                                               cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1][0], cmdBuffer[-1].cmd[cursor:]))    
+                                       else: # Only paste in the Short name
+                                               cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1][1], cmdBuffer[-1].cmd[cursor:]))    
+                                               
                else:
                        # print 'NO EDITVAR'
                        return
                
-       # ------------------end------------------# 
-       
+       # ------------------end------------------ #
        
+       # Quit from menu only
        #if (evt == Draw.ESCKEY and not val):
        #       Draw.Exit()
        if evt == Draw.MOUSEX: # AVOID TOO MANY REDRAWS.
@@ -370,15 +545,20 @@ def handle_event(evt, val):
                return
        
        
+       
        global cursor
        global histIndex        
+       global __FONT_SIZE__
+       global __CONSOLE_LINE_OFFSET__
        
        ascii = Blender.event
        
+       resetScroll = True
+       
        #------------------------------------------------------------------------------#
        #                             key codes and key handling                       #
        #------------------------------------------------------------------------------#
-
+       
        # UP DOWN ARROW KEYS, TO TRAVERSE HISTORY
        if (evt == Draw.UPARROWKEY and val): actionUpKey()
        elif (evt == Draw.DOWNARROWKEY and val): actionDownKey()
@@ -436,23 +616,57 @@ def handle_event(evt, val):
                else:
                        insCh('\t')     
        
-       elif (evt == Draw.BACKSPACEKEY and val): cmdBuffer[-1].cmd = ('%s%s' % (cmdBuffer[-1].cmd[:cursor-1] , cmdBuffer[-1].cmd[cursor:]))
+       elif (evt == Draw.BACKSPACEKEY and val):
+               if Window.GetKeyQualifiers() & Window.Qual.SHIFT:
+                       i = -1
+                       for d in __DELIMETERS__:
+                               i = max(i, cmdBuffer[-1].cmd[:cursor-1].rfind(d))
+                       if i == -1:
+                               i=0
+                       cmdBuffer[-1].cmd = ('%s%s' % (cmdBuffer[-1].cmd[:i] , cmdBuffer[-1].cmd[cursor:]))
+                       
+               else:
+                       # Normal backspace.
+                       cmdBuffer[-1].cmd = ('%s%s' % (cmdBuffer[-1].cmd[:cursor-1] , cmdBuffer[-1].cmd[cursor:]))
+                       
        elif (evt == Draw.DELKEY and val) and cursor < -1:
                cmdBuffer[-1].cmd = cmdBuffer[-1].cmd[:cursor] + cmdBuffer[-1].cmd[cursor+1:]
                cursor +=1
                
        elif ((evt == Draw.RETKEY or evt == Draw.PADENTER) and val): actionEnterKey()
-       elif (evt == Draw.RIGHTMOUSE and not val):actionRightMouse(); return
+       elif (evt == Draw.RIGHTMOUSE and not val): actionRightMouse(); return
+       
+       elif (evt == Draw.PADPLUSKEY or evt == Draw.EQUALKEY or evt == Draw.WHEELUPMOUSE) and val and Window.GetKeyQualifiers() & Window.Qual.CTRL:
+               __FONT_SIZE__ += 1
+               __FONT_SIZE__ = min(len(__FONT_SIZES__)-1, __FONT_SIZE__)
+       elif (evt == Draw.PADMINUS or evt == Draw.MINUSKEY or evt == Draw.WHEELDOWNMOUSE) and val and Window.GetKeyQualifiers() & Window.Qual.CTRL:
+               __FONT_SIZE__ -=1
+               __FONT_SIZE__ = max(0, __FONT_SIZE__)
        
+       
+       elif evt == Draw.WHEELUPMOUSE and val:
+               __CONSOLE_LINE_OFFSET__ += 1
+               __CONSOLE_LINE_OFFSET__ = min(len(cmdBuffer)-2, __CONSOLE_LINE_OFFSET__)
+               resetScroll = False
+               
+       elif evt == Draw.WHEELDOWNMOUSE and val:
+               __CONSOLE_LINE_OFFSET__ -= 1
+               __CONSOLE_LINE_OFFSET__ = max(0, __CONSOLE_LINE_OFFSET__)
+               resetScroll = False
+       
+
        elif ascii:
                insCh(chr(ascii))
        else:
                return # dont redraw.
+       
+       # If the user types in anything then scroll to bottom.
+       if resetScroll:
+               __CONSOLE_LINE_OFFSET__ = 0
        Draw.Redraw()
 
 
 def draw_gui():
-       
        # Get the bounds from ObleGL directly
        __CONSOLE_RECT__ = BGL.Buffer(BGL.GL_FLOAT, 4)
        BGL.glGetFloatv(BGL.GL_SCISSOR_BOX, __CONSOLE_RECT__) 
@@ -462,20 +676,24 @@ def draw_gui():
        BGL.glClearColor(0.0, 0.0, 0.0, 1.0)
        BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)         # use it to clear the color buffer
        
+       
+       # Fixed margin. use a margin since 0 margin can be hard to seewhen close to a crt's edge.
+       margin = 4
+       
        # Draw cursor location colour
-       cmd2curWidth = Draw.GetStringWidth(cmdBuffer[-1].cmd[:cursor], __FONT_SIZE__)
-       BGL.glColor3f(0.8, 0.2, 0.2)
-       if cmd2curWidth == 0:
-               BGL.glRecti(0,2,2, __LINE_HEIGHT__+2)
-       else:
-               BGL.glRecti(cmd2curWidth-2,2,cmd2curWidth, __LINE_HEIGHT__+2)
+       if __CONSOLE_LINE_OFFSET__ == 0:
+               cmd2curWidth = Draw.GetStringWidth(cmdBuffer[-1].cmd[:cursor], __FONT_SIZES__[__FONT_SIZE__][0])
+               BGL.glColor3f(0.8, 0.2, 0.2)
+               if cmd2curWidth == 0:
+                       BGL.glRecti(margin,2,margin+2, __FONT_SIZES__[__FONT_SIZE__][1]+2)
+               else:
+                       BGL.glRecti(margin + cmd2curWidth-2,2, margin+cmd2curWidth, __FONT_SIZES__[__FONT_SIZE__][1]+2)
        
        BGL.glColor3f(1,1,1)
        # Draw the set of cammands to the buffer
-       
-       consoleLineIdx = 1
+       consoleLineIdx = __CONSOLE_LINE_OFFSET__ + 1
        wrapLineIndex = 0
-       while consoleLineIdx < len(cmdBuffer) and  __CONSOLE_RECT__[3] > consoleLineIdx*__LINE_HEIGHT__ :
+       while consoleLineIdx < len(cmdBuffer) and  __CONSOLE_RECT__[3] > (consoleLineIdx - __CONSOLE_LINE_OFFSET__) * __FONT_SIZES__[__FONT_SIZE__][1]:
                if cmdBuffer[-consoleLineIdx].type == 0:
                        BGL.glColor3f(1, 1, 1)
                elif cmdBuffer[-consoleLineIdx].type == 1:
@@ -484,21 +702,22 @@ def draw_gui():
                        BGL.glColor3f(1.0, 0, 0)
                elif cmdBuffer[-consoleLineIdx].type == 3:
                        BGL.glColor3f(0, 0.8, 0)
-               else:
+               else:  
                        BGL.glColor3f(1, 1, 0)
                
                if consoleLineIdx == 1: # NEVER WRAP THE USER INPUT
-                       BGL.glRasterPos2i(0, (__LINE_HEIGHT__*consoleLineIdx) - 8)
-                       Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZE__)
+                       BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1] * (consoleLineIdx-__CONSOLE_LINE_OFFSET__)) - 8)
+                       # BUG, LARGE TEXT DOSENT DISPLAY
+                       Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZES__[__FONT_SIZE__][0])
                        
                
                else: # WRAP?
                        # LINE WRAP
-                       if Draw.GetStringWidth(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZE__) >  __CONSOLE_RECT__[2]:
+                       if Draw.GetStringWidth(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZES__[__FONT_SIZE__][0]) >  __CONSOLE_RECT__[2]:
                                wrapLineList = []
                                copyCmd = [cmdBuffer[-consoleLineIdx].cmd, '']
                                while copyCmd != ['','']:
-                                       while Draw.GetStringWidth(copyCmd[0], __FONT_SIZE__) > __CONSOLE_RECT__[2]:
+                                       while margin + Draw.GetStringWidth(copyCmd[0], __FONT_SIZES__[__FONT_SIZE__][0]) > __CONSOLE_RECT__[2]:
                                                #print copyCmd
                                                copyCmd[1] = '%s%s'% (copyCmd[0][-1], copyCmd[1]) # Add the char on the end
                                                copyCmd[0] = copyCmd[0][:-1]# remove last chat
@@ -513,17 +732,16 @@ def draw_gui():
                                # Now we have a list of lines, draw them (OpenGLs reverse ordering requires this odd change)
                                wrapLineList.reverse()
                                for wline in wrapLineList:
-                                       BGL.glRasterPos2i(0, (__LINE_HEIGHT__*(consoleLineIdx + wrapLineIndex)) - 8)
-                                       Draw.Text(wline, __FONT_SIZE__)
+                                       BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1]*((consoleLineIdx-__CONSOLE_LINE_OFFSET__) + wrapLineIndex)) - 8)
+                                       Draw.Text(wline, __FONT_SIZES__[__FONT_SIZE__][0])
                                        wrapLineIndex += 1
                                wrapLineIndex-=1 # otherwise we get a silly extra line.
                                
                        else: # no wrapping.
                                
-                               BGL.glRasterPos2i(0, (__LINE_HEIGHT__*(consoleLineIdx+wrapLineIndex)) - 8)
-                               Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZE__)
-                       
-                       
+                               BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1] * ((consoleLineIdx-__CONSOLE_LINE_OFFSET__)+wrapLineIndex)) - 8)
+                               Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZES__[__FONT_SIZE__][0])
+               
                consoleLineIdx += 1
                        
 
@@ -536,42 +754,45 @@ def handle_button_event(evt):
 __CONSOLE_VAR_DICT__ = {} # Initialize var dict
 
 
-# Print Startup lines
-cmdBuffer = [cmdLine("Welcome to Ideasman's Blender Console", 1, None),\
-       cmdLine(' * Right Click:  Console Menu (Save output, etc.)', 1, None),\
-       cmdLine(' * Arrow Keys:  Command history and cursor', 1, None),\
-       cmdLine(' * Shift With Arrow Keys:  Jump words', 1, None),\
-       cmdLine(' * Ctrl + Tab:  Auto compleate based on variable names and modules loaded, multiple choices popup a menu', 1, None),\
-       cmdLine(' * Ctrl + Enter:  Multiline functions, delays executing code until only Enter is pressed.', 1, None)]
+# Print Startup lines, add __bpydoc__ to the console startup.
+cmdBuffer = []
+for l in __bpydoc__.split('<br>'):
+       cmdBuffer.append( cmdLine(l, 1, None) )
        
+
 histIndex = cursor = -1 # How far back from the first letter are we? - in current CMD line, history if for moving up and down lines.
 
 # Autoexec, startup code.
-console_autoexec  = '%s%s' % (Get('datadir'), '/console_autoexec.py')
+scriptDir = Get('scriptsdir')
+if not scriptDir.endswith(Blender.sys.sep):
+       scriptDir += Blender.sys.sep
+
+console_autoexec  = '%s%s' % (scriptDir, 'console_autoexec.py')
+
 if not sys.exists(console_autoexec):
        # touch the file
+       cmdBuffer.append(cmdLine('...console_autoexec.py not found, making new in scripts dir', 1, None))
        open(console_autoexec, 'w').close()
-       cmdBuffer.append(cmdLine('...console_autoexec.py not found, making new in scripts data dir', 1, None))
 else:
-       cmdBuffer.append(cmdLine('...Using existing console_autoexec.py in scripts data dir', 1, None))
+       cmdBuffer.append(cmdLine('...Using existing console_autoexec.py in scripts dir', 1, None))
 
 
 
 #-Autoexec---------------------------------------------------------------------#
 # Just use the function to jump into local naming mode.
 # This is so we can loop through all of the autoexec functions / vars and add them to the __CONSOLE_VAR_DICT__
-def autoexecToVarList():
+def include_console(includeFile):
        global __CONSOLE_VAR_DICT__ # write autoexec vars to this.
        
        # Execute an external py file as if local
-       exec(include(console_autoexec))
+       exec(include(includeFile))
        
        # Write local to global __CONSOLE_VAR_DICT__ for reuse,
        for __TMP_VAR_NAME__ in dir() + dir(Blender):
                # Execute the local > global coversion.
                exec('%s%s' % ('__CONSOLE_VAR_DICT__[__TMP_VAR_NAME__]=', __TMP_VAR_NAME__))
                
-autoexecToVarList() # pass the blender module
+include_console(console_autoexec) # pass the blender module
 #-end autoexec-----------------------------------------------------------------#
 
 
index e7628a00cae85851bf92dd7a8aa4712288671f7c..e5ce81f65f3f272045dee846a6cd8a76575ddcc0 100644 (file)
@@ -2,7 +2,7 @@
 
 """ Registration info for Blender menus: <- these words are ignored
 Name: 'Dispaint'
-Blender: 233
+Blender: 237
 Group: 'Mesh'
 Tip: 'use vertex paint color value to modify shape displacing vertices along normal'
 """
@@ -11,7 +11,7 @@ __author__ = "Jean-Michel Soler (jms)"
 __url__ = ("blender", "elysiun",
 "Script's homepage, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_displacementpainting.htm",
 "Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
-__version__ = "233i"
+__version__ = "237"
 
 __bpydoc__ = """\
 This script displaces mesh vertices according to vertex color values.
@@ -81,6 +81,7 @@ from Blender.Draw import *
 from Blender.BGL import *
 from Blender.Noise import *
 from Blender.Scene  import *
+from Blender.Window  import *
 sc=Scene.getCurrent()
 
 # niveau du deplacement
@@ -117,6 +118,7 @@ E_AXESELX  = 46
 E_AXESELY  = 47
 E_AXESELZ  = 48
 
+
 E_NOISEME  = 49
 E_NOISEH   = 50
 E_NOISELAC = 51
@@ -126,10 +128,28 @@ E_NOISEBAS = 54
 E_NOISEVAL=[E_NOISEH,E_NOISELAC,E_NOISEOCT,E_NOISEOFF,E_NOISEBAS]
 E_NOISEDIM = 55
 
+E_GETCOLORS = 56
+E_UVCOLORS =  57
+E_SAVECOLORS = 58
+B_SAVECOLORS = 0
+
+E_RESTCOLORS = 60
+V_RESTCOL=0
+F_RESTCOL=0
+
+BUF_COLORS=[]
+
+RVBA_VALUE=61
+RVBA_VERTICES=62
+RVBA_FACES=63
+
 ExitTIP="Exit from this script session "
 CreateTIP="Create a new copy of the selected shape"
 ActionTIP="Do the current selected actions"
 
+UVCOLORSTIP="Get colrs from first available UV image "
+GETCOLORSTIP="Get color from textures "
+REPEATTIP="Replay the same action with new values ."
 
 def copy_transform(ozero,Obis):
          Obis.setSize(ozero.getSize());
@@ -298,9 +318,23 @@ def DOCMat_list(TMATList):
               TMATList[0]=0
             return TMATList
  
-MOname = "MODE MENU %t|Normal %x1|Material %x2|Selected %x3"
+MOname = "MODE MENU %t|Normal %x1|Material %x2|Selected %x3| Find color %x4"
+MOname_doc=["",
+            "Displace all vertices",
+            "Displace vertices only on selected materials . ",
+            "Displace only selected vertices .",
+            "Try to find and set selected the vertices with this color."]
+
 ORname = "ORIENT MENU %t|From Normal %x1|Local Axes %x2| Noise %x3"
-NOname = "NOISE MENU %t|BLENDER %x1|STDPERLIN %x2|NEWPERLIN %x3|VORONOI_F1%x4|VORONOI_F2%x5|VORONOI_F3%x6|VORONOI_F4%x7|VORONOI_F2F1%x8|VORONOI_CRACKLE%x9|CELLNOISE%x10|HETEROTENOISE%x11"
+ORname_doc=["",
+            "Use normal orientation to calculate displacement",
+            "Use selected axes value to calculate displacement",
+            "Blend the color value with Nosie values to calculate the displacement"]
+
+NOname = "NOISE MENU %t|BLENDER %x1|STDPERLIN %x2|\
+NEWPERLIN %x3|VORONOI_F1%x4|VORONOI_F2%x5|\
+VORONOI_F3%x6|VORONOI_F4%x7|VORONOI_F2F1%x8|\
+VORONOI_CRACKLE%x9|CELLNOISE%x10|HETEROTENOISE%x11"
 
 MODEMenu = Create(1)
 ORIENTMenu = Create(1)
@@ -351,48 +385,190 @@ glCl3=glColor3f
 glCl4=glColor4f
 glRct=glRectf
 
-
-
+def triangle(a,b,c):
+  glBegin(GL_TRIANGLES);
+  glColor3f(a[2],a[3],a[4])
+  glVertex2f(a[0],a[1]);
+  glVertex2f(b[0],b[1]);
+  glVertex2f(c[0],c[1]);
+  glEnd();
+
+def triangleFcolor(a,b,c):
+  glBegin(GL_TRIANGLES);
+  glColor4f(a[2],a[3],a[4],a[5])
+  glVertex2f(a[0],a[1]);
+  glColor4f(b[2],b[3],b[4],a[5])
+  glVertex2f(b[0],b[1]);
+  glColor4f(c[2],c[3],c[4],a[5])
+  glVertex2f(c[0],c[1]);
+  glEnd();
+
+def Ltriangle(a,b,c,LC=0.5):
+  TL=[a,b,c,a]         
+  for v in [0,1,2] :
+    glBegin(GL_LINES);
+    glColor3f(LC,LC,LC)
+    glVertex2f(TL[v][0],TL[v][1]);
+    glVertex2f(TL[v+1][0],TL[v+1][1]);
+    glEnd();
+
+
+def carreFcolor(a,b,c,d):
+    triangleFcolor(a,b,c)
+    triangleFcolor(a,c,d)
+
+RVBA=[Create(255),Create(255),Create(255),Create(255),Create(0)]
+
+#         _*_ p1 
+#       _/   \_
+#     _/       \_
+#    /           \_
+# p0*_             /* p2
+#   | \_         _/ |
+#   |   \_     _/   |
+#   |     \_ _/     |
+#   |       * p3    |
+#   |       |       |
+#   *_      |      /* p4
+# p6  \_    |    _/ 
+#       \_  |  _/   
+#         \_|_/     
+#           * p5
+
+def flatcolorcube(r,g,b,a,m,x,y):
+    h0=60
+    v0=40
+    A=[x,      y,              (r-m)/255.0,g/255.0,b/255.0,a/255.0]     #p0 
+    B=[x+h0,y-v0,      r/255.0,g/255.0,b/255.0,a/255.0]         #p3
+    c=[x+h0*2,y,       r/255.0, g/255.0,  (b-m)/255.0,a/255.0]  #p2
+    d=[x+h0,y+v0,      (r-m)/255.0,g/255.0,(b-m)/255.0,a/255.0] #p1
+    carreFcolor(A,B,c,d)
+
+
+    A=[x,y,(r-m)/255.0,g/255.0,b/255.0,a/255.0]                                #p0
+    B=[x+h0,y-v0,r/255.0,g/255.0,b/255.0,a/255.0]                              #p3     
+    c=[x+h0,y-v0*2.5,  r/255.0, (g-m)/255.0,  b/255.0,a/255.0]    #p5
+    d=[x,y-v0*1.5,(r-m)/255.0,(g-m)/255.0,b/255.0,a/255.0]      #p6
+    carreFcolor(A,B,c,d)    
+
+    d=[x+h0,y-v0,r/255.0,g/255.0,b/255.0,a/255.0]                       #p3
+    A=[x+h0*2,y,r/255.0,g/255.0,(b-m)/255.0,a/255.0]                    #p2
+    B=[x+h0*2,y-v0*1.5,  r/255.0, (g-m)/255.0,(b-m)/255.0,a/255.0] #p4
+    c=[x+h0,y-v0*2.5,r/255.0,(g-m)/255.0,b/255.0,a/255.0]        #p5
+    carreFcolor(A,B,c,d)    
+
+def col_egal2col(col,RVBA):
+    eps=RVBA[4].val
+    if ( (RVBA[0].val-col[0]>=0 and RVBA[0].val-col[0]<=eps) and
+         (RVBA[1].val-col[1]>=0 and RVBA[1].val-col[1]<=eps) and
+         (RVBA[2].val-col[2]>=0 and RVBA[2].val-col[2]<=eps) and       
+              (RVBA[3].val-col[3]>=0 and RVBA[3].val-col[3]<=eps) ) :
+       #print 'ok',col, [RVBA[n].val-col[n] for n in 0,1,2,3]
+       return 1
+    else:
+       #print 'not',col, [RVBA[n].val-col[n] for n in 0,1,2,3]
+       return 0
+
+def select_bycolors(TYPE,RVBA):
+  global RVBA_VERTICES, RVBA_FACES
+  SEL = Blender.NMesh.FaceFlags['SELECT']
+  try: 
+    ME=Blender.Scene.getCurrent().getActiveObject().getData()
+    VC={}
+    for f in ME.faces:
+        for v in f.v:
+             try:
+                    VC[v].append(f)
+             except:
+                 VC[v]=[f]
+             #print '.', 
+    for C in VC.iteritems():
+        color=[0,0,0]
+        for f in C[1]:
+            col=f.col[f.v.index(C[0])]
+            col=[col.r,col.g,col.b,col.a]
+            if col_egal2col(col,RVBA):
+               if TYPE== RVBA_VERTICES: 
+                  C[0].sel=1
+               else:
+                  f.sel=1
+                  f.flag |= SEL
+        #VC[C[0]].append(color[:])
+    ME.update()                
+  except:
+         pass 
+       
 def draw():
     global MODEMenu, NSIZE, TDOCMat,TMATList, TAXEList
     global mat, ORIName, NEWName, ORIENTMenu 
-    global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu, NOISEDIMbout,NOISEDIM
+    global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu
+    global NOISEDIMbout,NOISEDIM, RVBA,RVB_VALUE, RVBA_VERTICES 
     global HBout,lacunarityBout,octavesBout,offsetBout,basisBout
-    global noiseTYPE, ExitTIP, CreateTIP, ActionTIP
-    
+    global noiseTYPE, ExitTIP, CreateTIP, ActionTIP, E_GETCOLORS
+    global E_UVCOLORS, UVCOLORSTIP, GETCOLORSTIP, REPEATTIP,RVBA_FACES
+    global E_SAVECOLORS, B_SAVECOLORS, E_RESTCOLORS, MOname_doc, ORname_doc
+
     size=Buffer(GL_FLOAT, 4)
     glGetFloatv(GL_SCISSOR_BOX, size)
     size= size.list
 
     for s in [0,1,2,3]: size[s]=int(size[s])
-    
+          
+    glClearColor(0.72,0.72,0.72,1.0)
     glClear(GL_COLOR_BUFFER_BIT)
 
-    glColor3f(0.0,0.0,0.0)
-    glRectf(4,size[3],534,size[3]-32 )
+    glColor3f(0.66,0.66,0.66)
+    glRectf(4,size[3]-4,404,size[3]-32 )
+
+    glColor3f(0.76,0.76,0.76)
+    glRectf(4,size[3]-32,404,size[3]-294 )
+
+    triangle([4+9,size[3],0.72,0.72,0.72],
+             [4,size[3],],
+             [4,size[3]-9])
+
+    triangle([404-9,size[3],0.72,0.72,0.72],
+             [404,size[3],],
+             [404,size[3]-9])
+
+    triangle([404,size[3]-294,.72,0.72,0.72],
+             [404,size[3]-294+9,],
+             [404-9,size[3]-294])
+
+    triangle([4,size[3]-294,.72,0.72,0.72],
+             [4,size[3]-294+9,],
+             [4+9,size[3]-294])
 
     glColor3f(1.0,1.0,1.0)
     glRasterPos2f(20, size[3]-15)
-    Text("Script Python de displacement paintingt")
+    Text("Script Python de displacement painting")
 
     glRasterPos2f(20, size[3]-28)
-    Text("Jean-michel Soler, juillet 2004")
+    Text("Jean-michel Soler, Aout 2005")
 
    
     n0=70
     n1=55
+    if MODEMenu.val<4 : 
+       Button("Create"                ,E_CREATE  ,5  ,size[3]-n0+11  ,60 ,20,CreateTIP)
+       Button("Action"                ,E_ACTION  ,5  ,size[3]-n0-11  ,60 ,20,ActionTIP)
+       NRepeat=Number("repeat"        ,E_REPEAT   ,5  ,size[3]-n0-56     ,75 ,20, NRepeat.val,1,10,REPEATTIP)    
+
+    Button("Exit"                  ,E_EXIT    ,5  ,size[3]-n0-32  ,60 ,20,ExitTIP)   
+    Button("Tex colors"            ,E_GETCOLORS   ,5  ,size[3]-n0-80  ,75 ,20,GETCOLORSTIP)
+    Button("UV colors"             ,E_UVCOLORS    ,5  ,size[3]-n0-102  ,75 ,20,UVCOLORSTIP)
+    if B_SAVECOLORS :
+         Button("Rest colors"             ,E_RESTCOLORS    ,5  ,size[3]-n0-146  ,75 ,20,UVCOLORSTIP)
+    else:
+         Button("Save colors"           ,E_SAVECOLORS   ,5  ,size[3]-n0-124  ,75 ,20,GETCOLORSTIP)
+
 
-    Button("Create"                ,E_CREATE  ,5  ,size[3]-n0+16  ,60 ,20,CreateTIP)
-    Button("Action"                ,E_ACTION  ,5  ,size[3]-n0-4  ,60 ,20,ActionTIP)
-    Button("Exit"                  ,E_EXIT   ,5  ,size[3]-n0-24  ,60 ,20,ExitTIP)
-    
-    NRepeat=Number("repeat"        ,E_REPEAT   ,5  ,size[3]-n0-50     ,75 ,20, NRepeat.val,1,10)    
     
     glColor3f(0.0,0.0,0.0)
     glRasterPos2f(80  ,size[3]-n0+24)
     Text("MODE")
 
-    MODEMenu= Menu(MOname,          E_MODE  ,80  ,size[3]-n0 ,100,20, MODEMenu.val, "MODE menu.")
+    MODEMenu= Menu(MOname,          E_MODE  ,80  ,size[3]-n0 ,100,20, MODEMenu.val, MOname_doc[MODEMenu.val])
 
     if MODEMenu.val==2:
        TDOCMat=Toggle("Doc Mat"     ,E_DOCMAT  ,180  ,size[3]-n0 ,60 ,20,TDOCMat.val)    
@@ -402,14 +578,22 @@ def draw():
                  glCl3(TMATList[1][t][0],
                        TMATList[1][t][1],
                        TMATList[1][t][2]) 
-                 glRct(80+t*40,
-                       size[3]-n0-60,
-                       80+t*40+40,
-                       size[3]-n0-60+40)
-                 TMATList[2][t]=Toggle("%s"%t , 32+t ,80+t*40+5  ,size[3]-n0-50  ,30 , 20,TMATList[2][t].val)
+                 
+                 if t<=7:
+                    glRct(80+t*40,
+                    size[3]-n0-60,
+                    80+t*40+40,
+                    size[3]-n0-60+40)
+                    TMATList[2][t]=Toggle("%s"%(t+1) , 32+t ,80+t*40+5  ,size[3]-n0-50  ,30 , 20,TMATList[2][t].val)
+                 else: 
+                    glRct(80+(t-8)*40,
+                    size[3]-n0-50-50,
+                    80+(t-8)*40+40,
+                    size[3]-n0-60)
+                    TMATList[2][t]=Toggle("%s"%(t+1) , 32+t ,80+(t-8)*40+5  ,size[3]-n0-45*2  ,30 , 20,TMATList[2][t].val)
                  
     glColor3f(1.0,0.3,0.0)
-    glRasterPos2f(80+40+5  ,size[3]-n0-80)
+    glRasterPos2f(80+40+5  ,size[3]-n0-110)
     if ERROR>1:
          Text('Last error : '+TextERROR)
     else:
@@ -417,35 +601,66 @@ def draw():
 
     glColor3f(0.0,0.0,0.0)
     glRasterPos2f(240  ,size[3]-n0+24)
-    Text("ORIENTATION")
-    ORIENTMenu= Menu(ORname,        E_ORIENT    ,240  ,size[3]-n0 ,100,20, ORIENTMenu.val, "ORIENT menu.")
 
-    if ORIENTMenu.val==2 :
-       for t in range(3):
-          TAXEList[1][t]=Toggle("%s"%TAXEList[0][t],
-                         E_AXESEL+t, 
-                         240+100+t*30 , size[3]-n0  ,30 , 20,
-                         TAXEList[1][t].val)
+    if MODEMenu.val<4:
+            Text("ORIENTATION")
+            ORIENTMenu= Menu(ORname,        E_ORIENT    ,240  ,size[3]-n0 ,100,20, ORIENTMenu.val, ORname_doc[ORIENTMenu.val])
+            if ORIENTMenu.val==2 :
+               for t in [0,1]:
+                  TAXEList[1][t]=Toggle("%s"%TAXEList[0][t],
+                                 E_AXESEL+t, 
+                                 240+100+t*30+2 , size[3]-n0+10  ,28 , 18,
+                                 TAXEList[1][t].val)
+                  TAXEList[1][2]=Toggle("%s"%TAXEList[0][2],
+                                 E_AXESEL+2, 
+                                 int(240+100+.5*30+2) , size[3]-n0-10  ,28 , 18,
+                                 TAXEList[1][2].val)
+            if ORIENTMenu.val==3 :
+               glRasterPos2f(240  ,size[3]-n0-120-4)
+               Text("NOISE")
+               NOISEMenu= Menu(NOname,         E_NOISEME    , 240  ,size[3]-n0-148 ,110,20, NOISEMenu.val, "NOISE menu.")
+               NOISEDIMbout=Number(" Dim: "     ,E_NOISEDIM   , 240  ,size[3]-n0-172 ,110,20, NOISEDIMbout.val, 1,100)
+               if NOISEMenu.val==11:
+                  basisBout=Slider(noiseTYPE[basisBout.val],   
+                                                      E_NOISEBAS  ,40  ,size[3]-n0-178 ,175,20, basisBout.val, 0,9,)
+                  HBout= Slider("H",                  E_NOISEH    ,40  ,size[3]-n0-198 ,175,20, HBout.val, -2.0,+2.0,0,)
+                  lacunarityBout=Slider("lacunarity", E_NOISELAC  ,40  ,size[3]-n0-218 ,175,20, lacunarityBout.val, -4.0,+4.0,0,)
+                  octavesBout=Slider("octave",        E_NOISEOCT  ,219  ,size[3]-n0-198 ,175,20, octavesBout.val, -10.0,+10.0,0,)
+                  offsetBout=Slider("offset",         E_NOISEOFF  ,219 ,size[3]-n0-218 ,175,20, offsetBout.val, -5.0,+5.0,0,)
+            NSIZE= Slider("Disp Size",      E_NSIZE  ,80  ,size[3]-n0-20 ,260,20, NSIZE.val, -4.0,+4.0,0,"SIZE.")
 
-    
-    if ORIENTMenu.val==3 :
-       glRasterPos2f(240  ,size[3]-n0-90-4)
-       Text("NOISE")
-       NOISEMenu= Menu(NOname,         E_NOISEME    , 240  ,size[3]-n0-118 ,110,20, NOISEMenu.val, "NOISE menu.")
-       NOISEDIMbout=Number(" Dim: "     ,E_NOISEDIM   , 240  ,size[3]-n0-138 ,110,20, NOISEDIMbout.val, 1,100)
-
-       if NOISEMenu.val==11:
-          basisBout=Slider(noiseTYPE[basisBout.val],   
-                                              E_NOISEBAS  ,40  ,size[3]-n0-118 ,175,20, basisBout.val, 0,9,)
-          HBout= Slider("H",                  E_NOISEH  ,40  ,size[3]-n0-138 ,175,20, HBout.val, -2.0,+2.0,0,)
-          lacunarityBout=Slider("lacunarity", E_NOISELAC ,40  ,size[3]-n0-158 ,175,20, lacunarityBout.val, -4.0,+4.0,0,)
-          octavesBout=Slider("octave",        E_NOISEOCT  ,40  ,size[3]-n0-178 ,175,20, octavesBout.val, -10.0,+10.0,0,)
-          offsetBout=Slider("offset",         E_NOISEOFF  ,40  ,size[3]-n0-198 ,175,20, offsetBout.val, -5.0,+5.0,0,)
-
-    NSIZE= Slider("Disp Size",      E_NSIZE  ,80  ,size[3]-n0-20 ,260,20, NSIZE.val, -4.0,+4.0,0,"SIZE.")
 
-
-                 
+    else:
+        # degrades de couleurs
+        glShadeModel(GL_SMOOTH)
+        #transparence  
+        glEnable(GL_BLEND)
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
+
+        RVBA[0]=Slider("Red  :",      RVBA_VALUE , 105  ,size[3]-n0-25 ,280,20, RVBA[0].val, 0,255,0,"")
+        RVBA[1]=Slider("Green  :",    RVBA_VALUE , 105  ,size[3]-n0-47 ,280,20, RVBA[1].val, 0,255,0,"")
+        RVBA[2]=Slider("Blue  :",     RVBA_VALUE , 105  ,size[3]-n0-69 ,280,20, RVBA[2].val, 0,255,0,"")
+        RVBA[3]=Slider("Alpha  :",    RVBA_VALUE , 105  ,size[3]-n0-91 ,150,20, RVBA[3].val, 0,255,0,"")
+        RVBA[4]=Slider("margin  :",   RVBA_VALUE , 105  ,size[3]-n0-113 ,150,20, RVBA[4].val, 0,255,0,"")
+        flatcolorcube(RVBA[0].val,
+                      RVBA[1].val,
+                      RVBA[2].val,
+                      RVBA[3].val,
+                      RVBA[4].val,
+                      270,size[3]-n0-120)
+        
+        Button("Vertex"                ,RVBA_VERTICES  ,5  ,size[3]-n0-148  ,75 ,20,CreateTIP)
+        Button("Faces"                 ,RVBA_FACES  ,5  ,size[3]-n0-169  ,75 ,20,ActionTIP)
+
+        
+def on_MESH():
+    Me=Object.GetSelected()
+    if Me!=[] and Me[0].getType()=='Mesh':                 
+       editmode = Window.EditMode()   
+       if editmode: Window.EditMode(0)
+       return 1,Me[0].getData()
+    else: 
+          return 0, None
 
 def event(evt, val):    
     if (evt== QKEY and not val): Exit()
@@ -455,21 +670,20 @@ def bevent(evt):
     global mat, ORIENTMenu, NRepeat, TAXEList 
     global ERROR,TextERROR, NOISE, NOISEMenu, NOISEDIMbout,NOISEDIM
     global HBout,lacunarityBout,octavesBout,offsetBout,basisBout
-    global H,lacunarity,octaves,offset,basis
-    
+    global H,lacunarity,octaves,offset,basis, E_RESTCOLORS, RVBA_VERTICES
+    global E_GETCOLORS, E_UVCOLORS, E_SAVECOLORS, B_SAVECOLORS
+    global V_RESTCOLORS, F_RESTCOLORS, BUF_COLORS, RVBA, RVBA_FACES
+
     if   (evt== E_EXIT):
         Exit()
-
-
     elif   (evt== E_ACTION):
        for n in range(NRepeat.val):
           paint()
-
     elif   (evt== E_NSIZE):
        ng=NSIZE.val
-
     elif   (evt== E_DOCMAT) or (evt in E_MATVAL):
       Me=Object.GetSelected()
+
       if Me!=[]:
          if Me[0].getType()=='Mesh':   
             TMATList=DOCMat_list(TMATList)
@@ -484,29 +698,68 @@ def bevent(evt):
       else:
           ERROR=1
           TextERROR='No Selected Object.'  
-      
-
     elif   (evt== E_CREATE):
-         
            NEWMEcreation(Blender.Object.GetSelected()[0])
            Blender.Draw.Redraw()
-         
            ERROR=1
            TextERROR='No Selected Object.'
-
     elif   (evt== E_NOISEME):
        NOISE=NOISEMenu.val-1
-
     elif   (evt in E_NOISEVAL):
        H=HBout.val
        lacunarity=lacunarityBout.val
        octaves=octavesBout.val
        offset=offsetBout.val
        basis=basisBout.val
-
     elif (evt== E_NOISEDIM):
            NOISEDIM=NOISEDIMbout.val
-
+    elif (evt == E_GETCOLORS):
+            OK,MESH=on_MESH()
+            if OK: MESH.update(1,0,1)
+    elif (evt == E_UVCOLORS):
+       OK,MESH=on_MESH()
+       if OK and MESH.hasFaceUV():
+          for f in MESH.faces:
+              if f.image:
+                 im=Blender.Image.Get(f.image.name)
+                 break
+          imX,imY = im.getMaxXY()
+          for f in MESH.faces:  
+              for uv in  f.uv:
+                 color=[int(c*255.0) for c in im.getPixelF(abs(uv[0]*imX%imX), abs(uv[1]*imY%imY))]
+                 f.col[f.uv.index(uv)].r=color[0]
+                 f.col[f.uv.index(uv)].g=color[1]
+                 f.col[f.uv.index(uv)].b=color[2]
+                 f.col[f.uv.index(uv)].a=color[3]
+          MESH.update()
+    elif (evt == E_SAVECOLORS):
+          OK,MESH=on_MESH()
+          print OK, MESH
+          if OK and (MESH.hasFaceUV() or MESH.hasVertexColours()):
+             F_RESTCOLORS=1
+             for f in MESH.faces:
+               b=[MESH.faces.index(f)]  
+               for c in  f.col:
+                   b.append([c.r,c.g,c.b,c.a])
+               BUF_COLORS.append(b) 
+             B_SAVECOLORS  =  1
+          else: 
+             B_SAVECOLORS  =  0
+    elif (evt == E_RESTCOLORS):
+          OK,MESH=on_MESH()
+          print  F_RESTCOLORS, len(BUF_COLORS),len(MESH.faces)
+          if  OK and F_RESTCOLORS==1 and len(BUF_COLORS)==len(MESH.faces):
+             for b in  BUF_COLORS:
+                ncol=0
+                for c in  MESH.faces[b[0]].col :
+                    print b[ncol+1]
+                    c.r,c.g,c.b,c.a= b[ncol+1]
+                    ncol+=1
+             F_RESTCOLORS=0
+             B_SAVECOLORS = 0
+             BUF_COLORS=[]
+             MESH.update()
+    elif (evt == RVBA_VERTICES or evt == RVBA_FACES):
+        select_bycolors(evt,RVBA)
     Blender.Draw.Redraw()
 Register(draw, event, bevent)
index ca2ef1689cbda650ea678e8aca42e708c4e7b290..d56951959c3073a1d6e93a00f9f86c973b50b5e3 100644 (file)
@@ -2,7 +2,7 @@
 
 """ Registration info for Blender menus:
 Name: 'HotKey and MouseAction Reference'
-Blender: 232
+Blender: 237
 Group: 'Help'
 Tip: 'All the hotkeys/short keys'
 """ 
@@ -11,7 +11,7 @@ __author__ = "Jean-Michel Soler (jms)"
 __url__ = ("blender", "elysiun",
 "Script's homepage, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_hotkeyscript.htm",
 "Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
-__version__ = "10/2004"
+__version__ = "10/04/2005"
 
 __bpydoc__ = """\
 This script is a reference about all hotkeys and mouse actions in Blender.
@@ -22,6 +22,8 @@ Open the script from the Help menu and select group of keys to browse.
 
 Notes:<br>
     Additional entries in the database (c) 2004 by Bart.
+    Additional entries in the database for blender 2.37 (c) 2005 by jms.
+    
 """
 
 # $Id$
@@ -67,11 +69,12 @@ from Blender.Draw import *
 from Blender.BGL import *
 
 hotkeys={
+'Search ':[['', '']],
 'Specials 1 ':[
 [',', 'Set Bounding Box rotation scaling pivot'],
 ['Ctrl-,', 'Set Median Point rotation scaling pivot'],
 ['.', 'Set 3D cursor as rotation scaling pivot'],
-['Ctrl-.', 'Set Individual Object Centers as rotation scaling pivot'] ,
+['Ctrl-.', 'Set Individual Object Centers as rotation scaling pivot'],
 ['~', 'Display all layers (German keys: ö)'],
 ['Shift-~', 'Display all/previous layers (German keys: Shift-ö)'],
 ['Space', 'Popup menu'],
@@ -82,9 +85,10 @@ hotkeys={
 ['TAB', 'IPO: Edit selected'],
 ['Ctrl-TAB', 'Enter/exit Pose Mode'],
 ['Shift-TAB', 'Enter Object Mode'],
-['Ctrl-Open menu/', ''],
+['Ctrl-Open menu /', ''],
 ['Ctrl-Load Image', 'Opens a thumbnail browser instead of file browser for images']
 ],
+
 'Mouse ':[
 ['Actions:', ''],
 ['LMB', '3D View: Set 3D Cursor'],
@@ -111,6 +115,7 @@ hotkeys={
 ['MMB', 'Toggle optional transform feature'],
 ['RMB', 'Abort transformation']
 ],
+
 'F-Keys ':[
 ['F1', 'Open File'],
 ['F2', 'Save File'],
@@ -133,13 +138,18 @@ hotkeys={
 ['Shift-F7', 'Buttons Window'],
 ['Shift-F8', 'Video Sequencer Window'],
 ['Shift-F9', 'OOP Window'],
+['Alt-Shift-F9', 'OutLiner Window'],
 ['Shift-F10', 'UV Image Editor'],
 ['Shift-F11', 'Text Editor'],
-['Shift-F12', 'Action Editor']
+['Shift-F12', 'Action Editor'],
+['Ctrl-F2', 'Save/export in vrml 1.0 format' ],
+['Ctrl-F3', 'Save image : dump 3d view'],
+['Ctrl-Shift-F3', 'Save image : dump screen']
 ],
 
 'Numbers ':[
 ['1..2..0-=', 'Show layer 1..2..12'],
+['1..2..0-=', 'Edit Mode with Size, Grab, rotate tools : enter value'],
 ['Alt-1..2..0', 'Show layer 11..12..20'],
 ['Shift-1..2..0-=', 'Toggle layer 1..2..12'],
 ['Shift-ALT-...', 'Toggle layer 11..12..20']
@@ -150,10 +160,12 @@ hotkeys={
 ['Numpad /', 'Local view on object (hide others)'],
 ['Numpad *', 'Rotate view to objects local axes'],
 ['Numpad +', 'Zoom in (works everywhere)'],
-['Numpad +', 'Proportional vertex Edit Mode: Increase range of influence'],
+['Numpad +', 'In OutLiner window, Collapse one level of the  hierarchy'],
+['Alt-Numpad +', 'Proportional vertex Edit Mode: Increase range of influence'],
 ['Ctrl-Numpad +', 'Edit Mode: Select More vertices'],
 ['Numpad -', 'Zoom out (works everywhere)'],
-['Numpad -', 'Proportional vertex Edit Mode: Decrease range of influence'],
+['Numpad +', 'In OutLiner window, Expand one level of the  hierarchy'],
+['Alt-Numpad -', 'Proportional vertex Edit Mode: Decrease range of influence'],
 ['Ctrl-Numpad +', 'Edit Mode: Select Less vertices'],
 ['Numpad INS', 'Set Camera view'],
 ['Ctrl-Numpad INS', 'Set active object as camera'],
@@ -174,10 +186,19 @@ hotkeys={
 
 'Arrows ':[
 ['Home/Pos1', 'View all'],
+['Home', 'In OutLiner Windows, Show hierarchy'],
+['PgUp', 'Edit Mode and Proportionnal Editing Tools, increase influence'],
+['PgUp', 'Strip Editor, Move Down'],
+['PgUn', 'TimeLine: Jump to next marker'],
 ['PgUp', 'IPO: Select next keyframe'],
 ['Ctrl-PgUp', 'IPO: Select and jump to next keyframe'],
+['Ctrl-PgUn', 'TimeLine: Jump to next key'],   
+['PgDn', 'Edit Mode and Proportionnal Editing Tools, decrease influence'],
+['PgDn', 'Strip Editor, Move Up'],
+['PgDn', 'TimeLine: Jump to prev marker'],
 ['PgDn', 'IPO: Select previous keyframe'],
 ['Ctrl-PgDn', 'IPO: Select and jump to previous keyframe'],
+['Ctrl-PgDn', 'TimeLine: Jump to prev key'],           
 ['Left', 'One frame backwards'],
 ['Right', 'One frame forwards'],
 ['Down', '10 frames backwards'],
@@ -191,7 +212,9 @@ hotkeys={
 ['Shift-Arrow', 'Toggle first frame/ last frame']
 ],
 
-'Letters ':[ {"A":[ 
+'Letters ':[ 
+{
+"A":[ 
 ['A', 'Select all/Deselect all'],
 ['Alt-A', 'Play animation in current window'],
 ['Ctrl-A', 'Apply objects size/rotation to object data'],
@@ -199,7 +222,8 @@ hotkeys={
 ['Shift-A', 'Sequencer: Add menu'],
 ['Shift-A', '3D-View: Add menu'],
 ['Shift-ALT-A', 'Play animation in all windows'],
-['Shift-CTRL-A', 'Apply lattice / Make dupliverts real']
+['Shift-CTRL-A', 'Apply lattice / Make dupliverts real'],
+['Shift-CTRL-A', 'Apply Deform ']
 ],
 
 "B":[ 
@@ -214,6 +238,7 @@ hotkeys={
 ['C', 'UV Image Editor: Active Face Select toggle'],
 ['C', 'Sequencer: Change images'],
 ['C', 'IPO: Snap current frame to selected key'],
+['C', 'TimeLine: Center View'],        
 ['Alt-C', 'Object Mode: Convert menu'],
 ['Alt-C', 'Text Editor: Copy selection to clipboard'],
 ['Ctrl-C', 'Copy menu (Copy properties of active to selected objects)'],
@@ -232,6 +257,7 @@ hotkeys={
 "E":[ 
 ['E', 'Edit Mode: Extrude'],
 ['E', 'UV Image Editor: LSCM Unwrap'],
+['E', 'TimeLine: Set End'],    
 ['ER', 'Edit Mode: Extrude Rotate'],
 ['ES', 'Edit Mode: Extrude Scale'],
 ['ESX', 'Edit Mode: Extrude Scale X axis'],
@@ -267,10 +293,13 @@ hotkeys={
 "H":[ 
 ['H', 'Hide selected vertices/faces'],
 ['H', 'Curves: Set handle type'],
+['H', 'Action editor: Handle type aligned'],
+['H', 'Action editor: Handle type free'],              
 ['Alt-H', 'Show Hidden vertices/faces'],
-['Ctrl-H', 'Curves: Automatic handle calculation'],
-['Shift-H', 'Hide deselected  vertices/faces'],
-['Shift-H', 'Curves: Set handle type']
+['Shift-H', 'Curves: Automatic handle calculation'],
+['Shift-H', 'Action editor: Handle type auto'],        
+['Shift-H', 'Edit Mode, Hide deselected  vertices/faces'],
+['Ctrl-H', 'Edit Mode, Add a hook on selected points or show the hook menu .']
 ],
 
 "I":[ 
@@ -279,12 +308,11 @@ hotkeys={
 
 "J":[ 
 ['J', 'IPO: Join menu'],
-['J', 'Mesh: Join all adjacent triangles to quads'],
+['J', 'Mesh: Join all adjacent triangles to quads'],   
 ['J', 'Render Window: Swap render buffer'],
 ['Ctrl-J', 'Join selected objects'],
 ['Ctrl-J', 'Nurbs: Add segment'],
 ['Ctrl-J', 'IPO: Join keyframes menu'],
-['Alt-J', 'Edit Mode: convert quads to triangles']
 ],
 
 "K":[  
@@ -304,7 +332,7 @@ hotkeys={
 ['L', 'Edit mode: Select linked vertices (near mouse pointer)'],
 ['L', 'OOPS window: Select linked objects'],
 ['L', 'UV Face Select: Select linked faces'],
-['Ctrl-L', 'Make links menu'],
+['Ctrl-L', 'Make links menu (for instance : to scene...)'],
 ['Shift-L', 'Select links menu']
 ],
 
@@ -312,8 +340,12 @@ hotkeys={
 ['M', 'Move object to different layer'],
 ['M', 'Sequencer: Make meta strip (group) from selected strips'],
 ['M', 'Edit Mode: Mirros Axis menu'],
+['M', 'Video Sequence Editor : Make Meta strip...'],           
+['M', 'TimeLine: Add marker'],
 ['Alt-M', 'Edit Mode: Merge vertices menu'],
-['Ctrl-M', 'Object Mode: Mirros Axis menu']
+['Alt-M', 'Video Sequence Editor : Separate Meta strip...'],   
+['Ctrl-M', 'Object Mode: Mirros Axis menu'],
+['Ctrl-M', 'TimeLine: Name marker']    
 ],
 
 "N":[ 
@@ -360,6 +392,7 @@ hotkeys={
 
 "S":[ 
 ['S', 'Scale'] ,
+['S', 'TimeLine: Set Start'],
 ['SX', 'Flip around X axis'] ,
 ['SY', 'Flip around Y axis'] ,
 ['SZ', 'Flip around Z axis'] ,
@@ -376,14 +409,15 @@ hotkeys={
 ['T', 'Adjust texture space'] ,
 ['T', 'Edit mode: Flip 3d curve'] ,
 ['T', 'IPO: Change IPO type'] ,
+['T', 'TimeLine: Show second'],        
 ['Alt-T', 'Clear tracking of object'] ,
 ['Ctrl-T', 'Make selected object track active object'] ,
 ['Ctrl-T', 'Edit Mode: Convert to triangles'] ,
 ['Ctrl-ALT-T', 'Benchmark'] ],
 
 "U":[ 
-['U', 'Make single user menu'] ,
-['U', '3D View: Global undo'] ,
+['U', 'Make single user menu (for import completly linked object to another scene  for instance) '] ,
+['U', '3D View: Make Single user Menu'] ,
 ['U', 'Edit Mode: Reload object data from before entering Edit Mode'] ,
 ['U', 'UV Face Select: Automatic UV calculation menu'] ,
 ['U', 'Vertex-/Weightpaint mode: Undo'] ,
@@ -395,9 +429,11 @@ hotkeys={
 ['V', 'Curves/Nurbs: Vector handle'],
 ['V', 'Vertexpaint mode'],
 ['V', 'UV Image Editor: Stitch UVs'],
+['V', 'Action editor: Vector'],        
 ['Alt-V', "Scale object to match image texture's aspect ratio"],
 ['Shift-V', 'Edit mode: Align view to selected vertices'],
 ['Shift-V', 'UV Image Editor: Limited Stitch UVs popup'],
+       
 ],
 
 "W":[ 
@@ -408,11 +444,16 @@ hotkeys={
 ['WY', 'UV Image Editor: Weld/Align Y axis'],
 ['Ctrl-W', 'Save current file'] ,
 ['Ctrl-W', 'Nurbs: Switch direction'] ,
-['Shift-W', 'Warp/bend selected vertices around cursor'] ],
+['Shift-W', 'Warp/bend selected vertices around cursor'],
+['alt-W', 'Export in videoscape format']
+ ],
 
 "X":[ 
 ['X', 'Delete menu'] ,
-['Ctrl-X', 'Restore default state (Erase all)'] ],
+['X', 'TimeLine: Remove marker'],
+['Ctrl-X', 'Restore default state (Erase all)']
+       
+ ],
 
 "Y":[ 
 ['Y', 'Mesh: Split selected vertices/faces from the rest'] ],
@@ -429,6 +470,12 @@ hotkeys={
 up=128
 down=129
 UP=0
+SEARCH=131
+OLDSEARCHLINE=''
+SEARCHLINE=Create('')
+LINE=130
+FINDED=[]
+LEN=0
 
 for k in hotkeys.keys():
    hotkeys[k].append(Create(0))
@@ -442,6 +489,24 @@ hotL.sort()
 hot=hotkeys.keys()
 hot.sort()
 
+def searchfor(SEARCHLINE):
+       global hotkeys, hot
+       FINDLIST=[]
+       for k in hot:
+               if k not in ['Letters ', 'Search '] :
+                       for l in hotkeys[k][:-1]:
+                               #print 'k, l : ', k,  l, l[1] 
+                               if  l[1].upper().find(SEARCHLINE.upper())!=-1:
+                                       FINDLIST.append(l)
+               elif k == 'Letters ':
+                       for l in hotL :
+                               for l0 in hotkeys['Letters '][0][l][:-1]:
+                                       #print 'k, l : ',l,  k,  l0
+                                       if l0[1].upper().find(SEARCHLINE.upper())!=-1:
+                                               FINDLIST.append(l0)
+       return FINDLIST                 
+                       
+       
 glCr=glRasterPos2d
 glCl3=glColor3f
 glCl4=glColor4f
@@ -462,8 +527,8 @@ def trace_rectangle3(r,c,c1):
     glCl3(c1[0],c1[1],c1[2])
 
 def draw():
-    global r,c,c1,hotkeys, hot, hotL, up, down, UP
-
+    global r,c,c1,hotkeys, hot, hotL, up, down, UP, SEARCH, SEARCHLINE,LINE
+    global OLDSEARCHLINE, FINDED, SCROLL, LEN
     size=Buffer(GL_FLOAT, 4)
     glGetFloatv(GL_SCISSOR_BOX, size)
     size= size.list
@@ -490,7 +555,7 @@ def draw():
     glRasterPos2f(42, size[3]-25)
 
     Text("HotKey and MouseAction Reference")
-
+   
     l=0
     listed=0
     Llisted=0
@@ -499,14 +564,12 @@ def draw():
     for k in hot:             
        #hotkeys[k][-1]=Toggle(k, hot.index(k)+10, 4+(20*26)/6*hot.index(k), size[3]-(42), len(k)*8, 20, hotkeys[k][-1].val )
        hotkeys[k][-1]=Toggle(k, hot.index(k)+10, 78*hot.index(k), size[3]-(47), 78, 24, hotkeys[k][-1].val )
-       
        l+=len(k)
-
        if hotkeys[k][-1].val==1.0:
            listed=hot.index(k)
     l=0
     size[3]=size[3]-4
-    if hot[listed]!='Letters ':
+    if hot[listed]!='Letters ' and hot[listed]!='Search ' :
        size[3]=size[3]-8
        SCROLL=size[3]/21
        END=-1
@@ -520,9 +583,7 @@ def draw():
              UP=len(hotkeys[hot[listed]][:-1])-SCROLL         
        else :
          UP=0
-
        for n in  hotkeys[hot[listed]][:-1][UP:END]:
-          
           if l%2==0:
              r=[0,size[3]-(21*l+66),
                      size[2], size[3]-(21*l+43)]
@@ -533,20 +594,50 @@ def draw():
           glRasterPos2f(4+8*15, size[3]-(58+21*l))
           Text('  : '+n[1]) 
           l+=1
+    elif hot[listed]=='Search ' :
+       r=[0,size[3]-70,
+          size[2], size[3]-44]
+       trace_rectangle4(r,c2)
+       SEARCHLINE=String(' ', LINE, 42, size[3]-68,200,18,SEARCHLINE.val, 256,'')
+       if len(FINDED)>0:
+         LEN=len(FINDED)          
+         size[3]=size[3]-8
+         SCROLL=size[3]/21
+         END=-1
+         if SCROLL < len(FINDED):
+            Button('/\\',up,4,size[3]+8,20,14,'Scroll up') 
+            Button('\\/',down,4,size[3]-8,20,14,'Scroll down')            
+            if (SCROLL+UP)<len(FINDED):
+               END=(UP+SCROLL-1)
+            else:
+               END=-1
+               #UP=len(FINDED)-SCROLL
+         else:
+              UP=0         
+         for n in FINDED[UP:END]:
+             if l%2==0:
+                 r=[0,size[3]-(21*l+66+24),
+                     size[2], size[3]-(21*l+43+24)]
+                 trace_rectangle4(r,c2)
+             glColor3f(0,0,0)
+             glRasterPos2f(4+8, size[3]-(58+24+21*l))
+             Text(n[0])
+             glRasterPos2f(4+8*15, size[3]-(58+24+21*l))
+             Text('  : '+n[1]) 
+             l+=1
     else:
        for k in hotL:
             pos=hotL.index(k)
             hotkeys['Letters '][0][k][-1]=Toggle(k,pos+20,hotL.index(k)*21, size[3]-(52+18), 21, 18, hotkeys['Letters '][0][k][-1].val )
             if hotkeys['Letters '][0][k][-1].val==1.0:
                Llisted=pos
-
        size[3]=size[3]-8
-
        SCROLL=(size[3]-88)/21
        END=-1
        if SCROLL < len(hotkeys['Letters '][0][hotL[Llisted]]):
-          Button('/\\',up,4,size[3]+8,20,14,'Scroll up') 
-          Button('\\/',down,4,size[3]-8,20,14,'Scroll down')            
+          LEN=len(hotkeys['Letters '][0][hotL[Llisted]])
+          Button('/\\',up,4,size[3]+8,20,14,'Scroll up, you can use arrow or page keys too ') 
+          Button('\\/',down,4,size[3]-8,20,14,'Scroll down,  you can use arrow or page keys too ')            
           if (UP+SCROLL)<len(hotkeys['Letters '][0][hotL[Llisted]]):
              END=(UP+SCROLL)
           else:
@@ -569,13 +660,27 @@ def draw():
           l+=1
 
 def event(evt, val):
-    global hotkeys, UP     
-    if ((evt== QKEY or evt== ESCKEY) and not val): 
+    global hotkeys, UP,  SCROLL  , LEN   
+    if (evt== QKEY or evt== ESCKEY): 
         Exit()
-
+    elif val:
+      if (evt== PAGEUPKEY):
+         if (UP+SCROLL)<LEN-5: 
+             UP+=5 
+      elif (evt== PAGEDOWNKEY):
+          if UP>4: 
+             UP-=5 
+      elif (evt== UPARROWKEY):
+          if (UP+SCROLL)<LEN-1: 
+             UP+=1
+      elif (evt== DOWNARROWKEY):
+          if UP>0: 
+              UP-=1
+      Redraw()
 
 def bevent(evt):
-    global hotkeysmhot, hotL, up,down,UP
+    global hotkeysmhot, hotL, up,down,UP, FINDED
+    global SEARCH, SEARCHLINE,LINE, OLDSEARCHLINE
 
     if   (evt== 1):
         Exit()
@@ -602,4 +707,11 @@ def bevent(evt):
        if UP>0: UP-=1
        Blender.Window.Redraw()
 
+    elif (evt==LINE):
+       if SEARCHLINE.val!='' and SEARCHLINE.val!=OLDSEARCHLINE:
+          OLDSEARCHLINE=SEARCHLINE.val 
+          FINDED=searchfor(OLDSEARCHLINE)
+          Blender.Window.Redraw()
+
+
 Register(draw, event, bevent)
index dc14d036503ab9e566cc817dc994742cb60f8ac2..a109acc02c15e0dab5bb2d6901c5139f8ac9bd7d 100644 (file)
@@ -26,7 +26,7 @@ Usage:<br>
 Supported:<br>
        UV Coordinates, Meshes, Materials, Material Indices, Specular
 Highlights, and Vertex Colors. For added functionality, each object is
-placed on its own layer.
+placed on its own layer. Someone added the CLIP chunk and imagename support.
 
 Missing:<br>
        Not too much, I hope! :).
@@ -95,6 +95,7 @@ def write(filename):
                bbox = generate_bbox(mesh)
                pols = generate_pols(mesh)
                ptag = generate_ptag(mesh, material_names)
+               clip = generate_clip(mesh, material_names)
 
                if mesh.hasFaceUV():
                        vmad_uv = generate_vmad_uv(mesh)  # per face
@@ -111,10 +112,6 @@ def write(filename):
                write_chunk(meshdata, "POLS", pols); chunks.append(pols)
                write_chunk(meshdata, "PTAG", ptag); chunks.append(ptag)
 
-               if mesh.hasFaceUV():
-                       write_chunk(meshdata, "VMAD", vmad_uv)
-                       chunks.append(vmad_uv)
-
                if meshtools.has_vertex_colors(mesh):
                        if meshtools.average_vcols:
                                write_chunk(meshdata, "VMAP", vmap_vc)
@@ -122,6 +119,13 @@ def write(filename):
                        else:
                                write_chunk(meshdata, "VMAD", vmad_vc)
                                chunks.append(vmad_vc)
+
+               if mesh.hasFaceUV():
+                       write_chunk(meshdata, "VMAD", vmad_uv)
+                       chunks.append(vmad_uv)
+                       write_chunk(meshdata, "CLIP", clip)
+                       chunks.append(clip)
+               
                layer_index += 1
 
        for surf in surfs:
@@ -135,6 +139,7 @@ def write(filename):
        file.write(meshdata.getvalue()); meshdata.close()
        for surf in surfs:
                write_chunk(file, "SURF", surf)
+       write_chunk(file, "DATE", "August 19, 2005")
 
        Blender.Window.DrawProgressBar(1.0, "")    # clear progressbar
        file.close()
@@ -456,6 +461,46 @@ def generate_surf(material_name):
        comment = generate_nstring(comment)
        data.write(struct.pack(">H", len(comment)))
        data.write(comment)
+
+       # Check if the material contains any image maps
+       mtextures = material.getTextures()                                                                      # Get a list of textures linked to the material
+       for mtex in mtextures:
+               if (mtex) and (mtex.tex.type == Blender.Texture.Types.IMAGE):   # Check if the texture is of type "IMAGE"
+                       data.write("BLOK")                  # Surface BLOK header
+                       data.write(struct.pack(">H", 104))  # Hardcoded and ugly! Will only handle 1 image per material
+
+                       # IMAP subchunk (image map sub header)
+                       data.write("IMAP")                  
+                       data_tmp = cStringIO.StringIO()
+                       data_tmp.write(struct.pack(">H", 0))  # Hardcoded - not sure what it represents
+                       data_tmp.write("CHAN")
+                       data_tmp.write(struct.pack(">H", 4))
+                       data_tmp.write("COLR")
+                       data_tmp.write("OPAC")                # Hardcoded texture layer opacity
+                       data_tmp.write(struct.pack(">H", 8))
+                       data_tmp.write(struct.pack(">H", 0))
+                       data_tmp.write(struct.pack(">f", 1.0))
+                       data_tmp.write(struct.pack(">H", 0))
+                       data_tmp.write("ENAB")
+                       data_tmp.write(struct.pack(">HH", 2, 1))  # 1 = texture layer enabled
+                       data_tmp.write("NEGA")
+                       data_tmp.write(struct.pack(">HH", 2, 0))  # Disable negative image (1 = invert RGB values)
+                       data_tmp.write("AXIS")
+                       data_tmp.write(struct.pack(">HH", 2, 1))
+                       data.write(struct.pack(">H", len(data_tmp.getvalue())))
+                       data.write(data_tmp.getvalue())
+
+                       # IMAG subchunk
+                       data.write("IMAG")
+                       data.write(struct.pack(">HH", 2, 1))
+                       data.write("PROJ")
+                       data.write(struct.pack(">HH", 2, 5)) # UV projection
+
+                       data.write("VMAP")
+                       uvname = generate_nstring("Blender's UV Coordinates")
+                       data.write(struct.pack(">H", len(uvname)))
+                       data.write(uvname)
+
        return data.getvalue()
 
 # =============================================
@@ -536,6 +581,27 @@ def generate_icon():
        #print len(icon_data)
        return data.getvalue()
 
+# ===============================================
+# === Generate CLIP chunk with STIL subchunks ===
+# ===============================================
+def generate_clip(mesh, material_names):
+       data = cStringIO.StringIO()
+       clipid = 1
+       for i in range(len(mesh.materials)):                                                                    # Run through list of materials used by mesh
+               material = Blender.Material.Get(mesh.materials[i].name)
+               mtextures = material.getTextures()                                                                      # Get a list of textures linked to the material
+               for mtex in mtextures:
+                       if (mtex) and (mtex.tex.type == Blender.Texture.Types.IMAGE):   # Check if the texture is of type "IMAGE"
+                               pathname = mtex.tex.image.filename                                                      # If full path is needed use filename in place of name
+                               pathname = pathname[0:2] + pathname.replace("\\", "/")[3:]  # Convert to Modo standard path
+                               imagename = generate_nstring(pathname)
+                               data.write(struct.pack(">L", clipid))                       # CLIP sequence/id
+                               data.write("STIL")                                          # STIL image
+                               data.write(struct.pack(">H", len(imagename)))               # Size of image name
+                               data.write(imagename)
+                               clipid += 1
+       return data.getvalue()
+
 # ===================
 # === Write Chunk ===
 # ===================
index 7e479536678189642667363568d744f5c99c85e1..8680890b0ced49a5bd3175b24b8b745d9ee7424c 100644 (file)
@@ -1,46 +1,22 @@
 #!BPY
-
 """
-Name: 'LightWave (.lwo)...'
-Blender: 232
+Name: 'LightWave + Materials (.lwo)...'
+Blender: 237
 Group: 'Import'
 Tooltip: 'Import LightWave Object File Format (.lwo)'
 """
 
-__author__ = "Anthony D'Agostino (Scorpius)"
+__author__ = "Alessandro Pirovano, Anthony D'Agostino (Scorpius)"
 __url__ = ("blender", "elysiun",
-"Author's homepage, http://www.redrival.com/scorpius")
-__version__ = "Part of IOSuite 0.5"
-
-__bpydoc__ = """\
-This script imports LightWave files to Blender.
-
-LightWave is a full-featured commercial modeling and rendering
-application. The lwo file format is composed of 'chunks,' is well
-defined, and easy to read and write. It is similar in structure to the
-trueSpace cob format.
-
-Usage:<br>
-       Execute this script from the "File->Import" menu and choose a LightWave
-file to open.
-
-Supported:<br>
-       Meshes only.
-
-Missing:<br>
-    Materials, UV Coordinates, and Vertex Color info will be ignored.
-
-Known issues:<br>
-       Triangulation of convex polygons works fine, and uses a very simple
-fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
-require a different algorithm, and will be triagulated incorrectly.
-
-Notes:<br>
-       Also reads lwo files in the old LW v5.5 format.
-"""
+"Author's homepage, http://www.redrival.com/scorpius", "Author's homepage, http://uaraus.altervista.org")
 
-# $Id$
-#
+importername = "lwo_import 0.1.16"
+# +---------------------------------------------------------+
+# | Save your work before and after use.                    |
+# | Please report any useful comment to:                    |
+# | uaraus-dem@yahoo.it                                     |
+# | Thanks                                                  |
+# +---------------------------------------------------------+
 # +---------------------------------------------------------+
 # | Copyright (c) 2002 Anthony D'Agostino                   |
 # | http://www.redrival.com/scorpius                        |
@@ -51,156 +27,1578 @@ Notes:<br>
 # +---------------------------------------------------------+
 # | Read and write LightWave Object File Format (*.lwo)     |
 # +---------------------------------------------------------+
+# +---------------------------------------------------------+
+# | Alessandro Pirovano tweaked starting on March 2005      |
+# | http://uaraus.altervista.org                            |
+# +---------------------------------------------------------+
+# +---------------------------------------------------------+
+# | Release log:                                            |
+# | 0.1.16: fixed (try 2) texture offset calculations       |
+# |         added hint on axis mapping                      |
+# |         added hint on texture blending mode             |
+# |         added hint on texture transparency setting      |
+# |         search images in original directory first       |
+# |         fixed texture order application                 |
+# | 0.1.15: added release log                               |
+# |         fixed texture offset calculations (non-UV)      |
+# |         fixed reverting vertex order in face generation |
+# |         associate texture on game-engine settings       |
+# |         vector math definitely based on mathutils       |
+# |         search images in "Images" and "../Images" dir   |
+# |         revised logging facility                        |
+# |         fixed subsurf texture and material mappings     |
+# | 0.1.14: patched missing mod_vector (not definitive)     |
+# | 0.1.13: first public release                            |
+# +---------------------------------------------------------+
+
+#blender related import
+import Blender
+
+#iosuite related import
+try: #new naming
+    import meshtools as my_meshtools
+except ImportError: #fallback to the old one
+    print "using old mod_meshtools"
+    import mod_meshtools as my_meshtools
+
+#python specific modules import
+import struct, chunk, os, cStringIO, time, operator, copy
 
-import Blender, meshtools
-import struct, chunk, os, cStringIO, time, operator
+# ===========================================================
+# === Utility Preamble ======================================
+# ===========================================================
+
+textname = "lwo_log"
+#uncomment the following line to disable logging facility
+#textname = None                      1
+
+# ===========================================================
+
+class dotext:
+
+    _NO = 0    #use internal to class only
+    LOG = 1    #write only to LOG
+    CON = 2    #write to both LOG and CONSOLE
+
+    def __init__(self, tname, where=LOG):
+        self.dwhere = where #defaults on console only
+        if (tname==None):
+            print "*** not using text object to log script"
+            self.txtobj = None
+            return
+        tlist = Blender.Text.get()
+        for i in range(len(tlist)):
+            if (tlist[i].getName()==tname):
+                tlist[i].clear()
+                #print tname, " text object found and cleared!"
+                self.txtobj = tlist[i]
+                return
+        #print tname, " text object not found and created!"
+        self.txtobj = Blender.Text.New(tname)
+    # end def __init__
+
+    def write(self, wstring, maxlen=100):
+        if (self.txtobj==None): return
+        while (1):
+            ll = len(wstring)
+            if (ll>maxlen):
+                self.txtobj.write((wstring[:maxlen]))
+                self.txtobj.write("\n")
+                wstring = (wstring[maxlen:])
+            else:
+                self.txtobj.write(wstring)
+                break
+    # end def write
+
+    def pstring(self, ppstring, where = _NO):
+        if where == dotext._NO: where = self.dwhere
+        if where == dotext.CON:
+            print ppstring
+        self.write(ppstring)
+        self.write("\n")
+    # end def pstring
+
+    def plist(self, pplist, where = _NO):
+        self.pprint ("list:[")
+        for pp in range(len(pplist)):
+            self.pprint ("[%d] -> %s" % (pp, pplist[pp]), where)
+        self.pprint ("]")
+    # end def plist
+
+    def pdict(self, pdict, where = _NO):
+        self.pprint ("dict:{", where)
+        for pp in pdict.keys():
+            self.pprint ("[%s] -> %s" % (pp, pdict[pp]), where)
+        self.pprint ("}")
+    # end def pdict
+
+    def pprint(self, parg, where = _NO):
+        if parg == None:
+            self.pstring("_None_", where)
+        elif type(parg) == type ([]):
+            self.plist(parg, where)
+        elif type(parg) == type ({}):
+            self.pdict(parg, where)
+        else:
+            self.pstring(parg, where)
+    # end def pprint
+
+    def logcon(self, parg):
+        self.pprint(parg, dotext.CON)
+    # end def logcon
+# endclass dotext
+
+tobj=dotext(textname)
+#uncomment the following line to log all messages on both console and logfile
+#tobj=dotext(textname,dotext.CON)
+
+
+# ===========================================================
+# === Main read functions ===================================
+# ===========================================================
 
 # =============================
 # === Read LightWave Format ===
 # =============================
 def read(filename):
-       start = time.clock()
-       file = open(filename, "rb")
-
-       # === LWO header ===
-       form_id, form_size, form_type = struct.unpack(">4s1L4s",  file.read(12))
-       if (form_type != "LWOB") and (form_type != "LWO2"):
-               print "Can't read a file with the form_type:", form_type
-               return
-
-       objname = os.path.splitext(os.path.basename(filename))[0]
-
-       while 1:
-               try:
-                       lwochunk = chunk.Chunk(file)
-               except EOFError:
-                       break
-               if lwochunk.chunkname == "LAYR":
-                       objname = read_layr(lwochunk)
-               elif lwochunk.chunkname == "PNTS":                         # Verts
-                       verts = read_verts(lwochunk)
-               elif lwochunk.chunkname == "POLS" and form_type == "LWO2": # Faces v6.0
-                       faces = read_faces_6(lwochunk)
-                       meshtools.create_mesh(verts, faces, objname)
-               elif lwochunk.chunkname == "POLS" and form_type == "LWOB": # Faces v5.5
-                       faces = read_faces_5(lwochunk)
-                       meshtools.create_mesh(verts, faces, objname)
-               else:                                                                                                      # Misc Chunks
-                       lwochunk.skip()
-
-       Blender.Window.DrawProgressBar(1.0, "")    # clear progressbar
-       file.close()
-       end = time.clock()
-       seconds = " in %.2f %s" % (end-start, "seconds")
-       if form_type == "LWO2": fmt = " (v6.0 Format)"
-       if form_type == "LWOB": fmt = " (v5.5 Format)"
-       message = "Successfully imported " + os.path.basename(filename) + fmt + seconds
-       meshtools.print_boxed(message)
+    global tobj
+
+    tobj.logcon ("#####################################################################")
+    tobj.logcon ("This is: %s" % importername)
+    tobj.logcon ("Importing file:")
+    tobj.logcon (filename)
+    tobj.pprint ("#####################################################################")
+
+    start = time.clock()
+    file = open(filename, "rb")
+
+    # === LWO header ===
+    form_id, form_size, form_type = struct.unpack(">4s1L4s",  file.read(12))
+    if (form_type == "LWOB"):
+        read_lwob(file, filename)
+    elif (form_type == "LWO2"):
+        read_lwo2(file, filename)
+    else:
+        tobj.logcon ("Can't read a file with the form_type: %s" %form_type)
+        return
+
+    Blender.Window.DrawProgressBar(1.0, "")    # clear progressbar
+    file.close()
+    end = time.clock()
+    seconds = " in %.2f %s" % (end-start, "seconds")
+    if form_type == "LWO2": fmt = " (v6.0 Format)"
+    if form_type == "LWOB": fmt = " (v5.5 Format)"
+    message = "Successfully imported " + os.path.basename(filename) + fmt + seconds
+    #my_meshtools.print_boxed(message)
+    tobj.pprint ("#####################################################################")
+    tobj.logcon (message)
+    tobj.logcon ("#####################################################################")
 
+# enddef read
+
+
+# =================================
+# === Read LightWave 5.5 format ===
+# =================================
+def read_lwob(file, filename):
+    global tobj
+
+    tobj.logcon("LightWave 5.5 format")
+    objname = os.path.splitext(os.path.basename(filename))[0]
+
+    while 1:
+        try:
+            lwochunk = chunk.Chunk(file)
+        except EOFError:
+            break
+        if lwochunk.chunkname == "LAYR":
+            objname = read_layr(lwochunk)
+        elif lwochunk.chunkname == "PNTS":                         # Verts
+            verts = read_verts(lwochunk)
+        elif lwochunk.chunkname == "POLS": # Faces v5.5
+            faces = read_faces_5(lwochunk)
+            my_meshtools.create_mesh(verts, faces, objname)
+        else:                                                       # Misc Chunks
+            lwochunk.skip()
+    return
+# enddef read_lwob
+
+
+# =============================
+# === Read LightWave Format ===
+# =============================
+def read_lwo2(file, filename, typ="LWO2"):
+    global tobj
+
+    tobj.logcon("LightWave 6 (and above) format")
+
+    dir_part = Blender.sys.dirname(filename)
+    fname_part = Blender.sys.basename(filename)
+
+    #first initialization of data structures
+    defaultname = os.path.splitext(fname_part)[0]
+    tag_list = []              #tag list: global for the whole file?
+    surf_list = []             #surf list: global for the whole file?
+    clip_list = []             #clip list: global for the whole file?
+    object_index = 0
+    object_list = None
+    # init value is: object_list = [[None, {}, [], [], {}, {}, 0, {}, {}]]
+    #0 - objname                    #original name
+    #1 - obj_dict = {TAG}           #objects created
+    #2 - verts = []                 #object vertexes
+    #3 - faces = []                 #object faces (associations poly -> vertexes)
+    #4 - obj_dim_dict = {TAG}       #tuples size and pos in local object coords - used for NON-UV mappings
+    #5 - polytag_dict = {TAG}       #tag to polygon mapping
+    #6 - patch_flag                 #0 = surf; 1 = patch (subdivision surface) - it was the image list
+    #7 - uvcoords_dict = {name}     #uvmap coordinates (mixed mode per face/per vertex)
+    #8 - facesuv_dict = {name}      #uvmap coordinates associations poly -> uv tuples
+
+    while 1:
+        try:
+            lwochunk = chunk.Chunk(file)
+        except EOFError:
+            break
+        tobj.pprint(" ")
+        if lwochunk.chunkname == "LAYR":
+            tobj.pprint("---- LAYR")
+            objname = read_layr(lwochunk)
+            tobj.pprint(objname)
+            if object_list == None:
+                object_list = [[objname, {}, [], [], {}, {}, 0, {}, {}]]
+            else:
+                object_list.append([objname, {}, [], [], {}, {}, 0, {}, {}])
+                object_index += 1
+        elif lwochunk.chunkname == "PNTS":                         # Verts
+            tobj.pprint("---- PNTS")
+            verts = read_verts(lwochunk)
+            object_list[object_index][2] = verts
+        elif lwochunk.chunkname == "VMAP":                         # MAPS (UV)
+            tobj.pprint("---- VMAP")
+            object_list[object_index][7], object_list[object_index][8] = read_vmap(object_list[object_index][7], object_list[object_index][8], object_list[object_index][3], len(object_list[object_index][2]), lwochunk)
+        elif lwochunk.chunkname == "VMAD":                         # MAPS (UV) per-face
+            tobj.pprint("---- VMAD")
+            object_list[object_index][7], object_list[object_index][8] = read_vmad(object_list[object_index][7], object_list[object_index][8], object_list[object_index][3], len(object_list[object_index][2]), lwochunk)
+        elif lwochunk.chunkname == "POLS": # Faces v6.0
+            tobj.pprint("-------- POLS(6)")
+            faces, flag = read_faces_6(lwochunk)
+            #flag is 0 for regular polygon, 1 for patches (= subsurf), 2 for anything else to be ignored
+            if flag<2:
+                if object_list[object_index][3] != []:
+                    object_list.append([object_list[object_index][0],                  #update name
+                                        {},                                            #init
+                                        copy.deepcopy(object_list[object_index][2]),   #same vertexes
+                                        [],                                            #no faces
+                                        {},                                            #no need to copy - filled at runtime
+                                        {},                                            #polygon tagging will follow
+                                        flag,                                          #patch flag
+                                        copy.deepcopy(object_list[object_index][7]),   #same uvcoords
+                                        {}])                                           #no uv mapping
+                    object_index += 1
+                #end if already has a face list
+                #update uv coords mapping if VMAP already encountered
+                for uvname in object_list[object_index][7]:
+                    tobj.pprint("updating uv to face mapping for %s" % uvname)
+                    object_list[object_index][8][uvname] = copy.deepcopy(faces)
+                object_list[object_index][3] = faces
+                objname = object_list[object_index][0]
+                if objname == None:
+                    objname = defaultname
+            #end if processing a valid poly type
+        elif lwochunk.chunkname == "TAGS":                         # Tags
+            tobj.pprint("---- TAGS")
+            tag_list.extend(read_tags(lwochunk))
+        elif lwochunk.chunkname == "PTAG":                         # PTags
+            tobj.pprint("---- PTAG")
+            polytag_dict = read_ptags(lwochunk, tag_list)
+            for kk in polytag_dict.keys(): object_list[object_index][5][kk] = polytag_dict[kk]
+        elif lwochunk.chunkname == "SURF":                         # surfaces
+            tobj.pprint("---- SURF")
+            surf_list.append(read_surfs(lwochunk, surf_list, tag_list))
+        elif lwochunk.chunkname == "CLIP":                         # texture images
+            tobj.pprint("---- CLIP")
+            clip_list.append(read_clip(lwochunk))
+            tobj.pprint("read total %s clips" % len(clip_list))
+        else:                                                       # Misc Chunks
+            tobj.pprint("---- %s: skipping" % lwochunk.chunkname)
+            lwochunk.skip()
+        #uncomment here to log data structure as it is built
+        #tobj.pprint(object_list)
+
+    tobj.pprint ("\n#####################################################################")
+    tobj.pprint("Found %d objects:" % len(object_list))
+    tobj.pprint ("#####################################################################")
+    for objspec_list in object_list:
+        tobj.pprint ("\n#===================================================================#")
+        tobj.pprint("Processing Object: %s" % objspec_list[0])
+        tobj.pprint ("#===================================================================#")
+        objspec_list[3], objspec_list[5], objspec_list[8] = recalc_faces(objspec_list[2], objspec_list[3], objspec_list[5], objspec_list[8]) #recalculate faces, polytag_dict and uv_mapping get rid of faces fanning
+
+        create_objects(objspec_list)
+
+        if surf_list != []:
+            create_material(clip_list, surf_list, objspec_list, dir_part) #give it all the object
+    return
+# enddef read_lwo2
+
+
+
+
+
+
+# ===========================================================
+# === File reading routines =================================
+# ===========================================================
 # ==================
 # === Read Verts ===
 # ==================
 def read_verts(lwochunk):
-       data = cStringIO.StringIO(lwochunk.read())
-       numverts = lwochunk.chunksize/12
-       #$verts = []
-       verts = [None] * numverts
-       for i in range(numverts):
-               if not i%100 and meshtools.show_progress:
-                       Blender.Window.DrawProgressBar(float(i)/numverts, "Reading Verts")
-               x, y, z = struct.unpack(">fff", data.read(12))
-               #$verts.append((x, z, y))
-               verts[i] = (x, z, y)
-       return verts
+    global tobj
+
+    data = cStringIO.StringIO(lwochunk.read())
+    numverts = lwochunk.chunksize/12
+    #$verts = []
+    verts = [None] * numverts
+    for i in range(numverts):
+        if not i%100 and my_meshtools.show_progress:
+            Blender.Window.DrawProgressBar(float(i)/numverts, "Reading Verts")
+        x, y, z = struct.unpack(">fff", data.read(12))
+        verts[i] = (x, z, y)
+    tobj.pprint("read %d vertexes" % (i+1))
+    return verts
+# enddef read_verts
+
 
 # =================
 # === Read Name ===
 # =================
+# modified to deal with odd lenght strings
 def read_name(file):
-       name = ""
-       while 1:
-               char = file.read(1)
-               if char == "\0": break
-               else: name += char
-       return name
+    name = ""
+    while 1:
+        char = file.read(1)
+        if char == "\0": break
+        else: name += char
+    len_name = len(name) + 1 #count the trailing zero
+    if len_name%2==1:
+        char = file.read(1) #remove zero padding to even lenght
+        len_name += 1
+    return name, len_name
+
 
 # ==================
 # === Read Layer ===
 # ==================
 def read_layr(lwochunk):
-       data = cStringIO.StringIO(lwochunk.read())
-       idx, flags = struct.unpack(">hh", data.read(4))
-       pivot = struct.unpack(">fff", data.read(12))
-       layer_name = read_name(data)
-       if not layer_name: layer_name = "No Name"
-       return layer_name
+    data = cStringIO.StringIO(lwochunk.read())
+    idx, flags = struct.unpack(">hh", data.read(4))
+    pivot = struct.unpack(">fff", data.read(12))
+    layer_name, discard = read_name(data)
+    if not layer_name: layer_name = "NoName"
+    return layer_name
+# enddef read_layr
+
 
 # ======================
 # === Read Faces 5.5 ===
 # ======================
 def read_faces_5(lwochunk):
-       data = cStringIO.StringIO(lwochunk.read())
-       faces = []
-       i = 0
-       while i < lwochunk.chunksize:
-               if not i%100 and meshtools.show_progress:
-                  Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
-               facev = []
-               numfaceverts, = struct.unpack(">H", data.read(2))
-               for j in range(numfaceverts):
-                       index, = struct.unpack(">H", data.read(2))
-                       facev.append(index)
-               facev.reverse()
-               faces.append(facev)
-               surfaceindex, = struct.unpack(">H", data.read(2))
-               if surfaceindex < 0:
-                       print "detail polygons follow, error."
-                       return
-               i += (4+numfaceverts*2)
-       return faces
+    data = cStringIO.StringIO(lwochunk.read())
+    faces = []
+    i = 0
+    while i < lwochunk.chunksize:
+        if not i%100 and my_meshtools.show_progress:
+           Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
+        facev = []
+        numfaceverts, = struct.unpack(">H", data.read(2))
+        for j in range(numfaceverts):
+            index, = struct.unpack(">H", data.read(2))
+            facev.append(index)
+        facev.reverse()
+        faces.append(facev)
+        surfaceindex, = struct.unpack(">H", data.read(2))
+        if surfaceindex < 0:
+            tobj.logcon ("***Error. Referencing uncorrect surface index")
+            return
+        i += (4+numfaceverts*2)
+    return faces
+
 
 # ==================================
 # === Read Variable-Length Index ===
 # ==================================
 def read_vx(data):
-       byte1, = struct.unpack(">B", data.read(1))
-       if byte1 != 0xFF:       # 2-byte index
-               byte2, = struct.unpack(">B", data.read(1))
-               index = byte1*256 + byte2
-               index_size = 2
-       else:                           # 4-byte index
-               byte2, byte3, byte4 = struct.unpack(">3B", data.read(3))
-               index = byte2*65536 + byte3*256 + byte4
-               index_size = 4
-       return index, index_size
+    byte1, = struct.unpack(">B", data.read(1))
+    if byte1 != 0xFF:    # 2-byte index
+        byte2, = struct.unpack(">B", data.read(1))
+        index = byte1*256 + byte2
+        index_size = 2
+    else:                # 4-byte index
+        byte2, byte3, byte4 = struct.unpack(">3B", data.read(3))
+        index = byte2*65536 + byte3*256 + byte4
+        index_size = 4
+    return index, index_size
+
+
+# ======================
+# === Read uvmapping ===
+# ======================
+def read_vmap(uvcoords_dict, facesuv_dict, faces, maxvertnum, lwochunk):
+    if maxvertnum == 0:
+        tobj.pprint ("Found VMAP but no vertexes to map!")
+        return uvcoords_dict, facesuv_dict
+    data = cStringIO.StringIO(lwochunk.read())
+    map_type = data.read(4)
+    if map_type != "TXUV":
+        tobj.pprint ("Reading VMAP: No Texture UV map Were Found. Map Type: %s" % map_type)
+        return uvcoords_dict, facesuv_dict
+    dimension, = struct.unpack(">H", data.read(2))
+    name, i = read_name(data) #i initialized with string lenght + zeros
+    tobj.pprint ("TXUV %d %s" % (dimension, name))
+    #my_uv_list = [None] * maxvertnum
+    my_uv_list = [(0.0, 0.0)] * maxvertnum         #more safe to have some default coordinates to associate in any case?
+    while (i < lwochunk.chunksize - 6):            #4+2 header bytes already read
+        vertnum, vnum_size = read_vx(data)
+        u, v = struct.unpack(">ff", data.read(8))
+        if vertnum >= maxvertnum:
+            tobj.pprint ("Hem: more uvmap than vertexes? ignoring uv data for vertex %d" % vertnum)
+        else:
+            my_uv_list[vertnum] = (u, v)
+        i += 8 + vnum_size
+    #end loop on uv pairs
+    uvcoords_dict[name] = my_uv_list
+    #this is a per-vertex mapping AND the uv tuple is vertex-ordered, so faces_uv is the same as faces
+    if faces == []:
+        tobj.pprint ("no faces read yet! delaying uv to face assignments")
+        facesuv_dict[name] = []
+    else:
+        #deepcopy so we could modify it without actually modify faces
+        tobj.pprint ("faces already present: proceeding with assignments")
+        facesuv_dict[name] = copy.deepcopy(faces)
+    return uvcoords_dict, facesuv_dict
+
+
+# ========================
+# === Read uvmapping 2 ===
+# ========================
+def read_vmad(uvcoords_dict, facesuv_dict, faces, maxvertnum, lwochunk):
+    maxfacenum = len(faces)
+    if maxvertnum == 0 or maxfacenum == 0:
+        tobj.pprint ("Found VMAD but no vertexes to map!")
+        return uvcoords_dict, facesuv_dict
+    data = cStringIO.StringIO(lwochunk.read())
+    map_type = data.read(4)
+    if map_type != "TXUV":
+        tobj.pprint ("Reading VMAD: No Texture UV map Were Found. Map Type: %s" % map_type)
+        return uvcoords_dict, facesuv_dict
+    dimension, = struct.unpack(">H", data.read(2))
+    name, i = read_name(data) #i initialized with string lenght + zeros
+    tobj.pprint ("TXUV %d %s" % (dimension, name))
+    if uvcoords_dict.has_key(name):
+        my_uv_list = uvcoords_dict[name]          #update existing
+        my_facesuv_list = facesuv_dict[name]
+    else:
+        my_uv_list = [(0.0, 0.0)] * maxvertnum    #start a brand new: this could be made more smart
+        my_facesuv_list = copy.deepcopy(faces)
+    #end variable initialization
+    lastindex = len(my_uv_list) - 1
+    while (i < lwochunk.chunksize - 6):  #4+2 header bytes already read
+        vertnum, vnum_size = read_vx(data)
+        i += vnum_size
+        polynum, vnum_size = read_vx(data)
+        i += vnum_size
+        u, v = struct.unpack(">ff", data.read(8))
+        if polynum >= maxfacenum or vertnum >= maxvertnum:
+            tobj.pprint ("Hem: more uvmap than vertexes? ignorig uv data for vertex %d" % vertnum)
+        else:
+            my_uv_list.append( (u,v) )
+            newindex = len(my_uv_list) - 1
+            for vi in range(len(my_facesuv_list[polynum])): #polynum starting from 1 or from 0?
+                if my_facesuv_list[polynum][vi] == vertnum:
+                    my_facesuv_list[polynum][vi] = newindex
+            #end loop on current face vertexes
+        i += 8
+    #end loop on uv pairs
+    uvcoords_dict[name] = my_uv_list
+    facesuv_dict[name] = my_facesuv_list
+    tobj.pprint ("updated %d vertexes data" % (newindex-lastindex))
+    return uvcoords_dict, facesuv_dict
+
+
+# =================
+# === Read tags ===
+# =================
+def read_tags(lwochunk):
+    data = cStringIO.StringIO(lwochunk.read())
+    tag_list = []
+    current_tag = ""
+    i = 0
+    while i < lwochunk.chunksize:
+        char = data.read(1)
+        if char == "\0":
+            tag_list.append(current_tag)
+            if (len(current_tag) % 2 == 0): char = data.read(1)
+            current_tag = ""
+        else:
+            current_tag += char
+        i += 1
+    tobj.pprint("read %d tags, list follows:" % len(tag_list))
+    tobj.pprint( tag_list)
+    return tag_list
+
+
+# ==================
+# === Read Ptags ===
+# ==================
+def read_ptags(lwochunk, tag_list):
+    data = cStringIO.StringIO(lwochunk.read())
+    polygon_type = data.read(4)
+    if polygon_type != "SURF":
+        tobj.pprint ("No Surf Were Found. Polygon Type: %s" % polygon_type)
+        return {}
+    ptag_dict = {}
+    i = 0
+    while(i < lwochunk.chunksize-4): #4 bytes polygon type already read
+        if not i%100 and my_meshtools.show_progress:
+           Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading PTAGS")
+        poln, poln_size = read_vx(data)
+        i += poln_size
+        tag_index, = struct.unpack(">H", data.read(2))
+        if tag_index > (len(tag_list)):
+            tobj.pprint ("Reading PTAG: Surf belonging to undefined TAG: %d. Skipping" % tag_index)
+            return {}
+        i += 2
+        tag_key = tag_list[tag_index]
+        if not(ptag_dict.has_key(tag_key)):
+            ptag_dict[tag_list[tag_index]] = [poln]
+        else:
+            ptag_dict[tag_list[tag_index]].append(poln)
+    for i in ptag_dict.keys():
+        tobj.pprint ("read %d polygons belonging to TAG %s" % (len(ptag_dict[i]), i))
+    return ptag_dict
+
+
+
+# ==================
+# === Read Clips ===
+# ==================
+def read_clip(lwochunk):
+    clip_dict = {}
+    data = cStringIO.StringIO(lwochunk.read())
+    image_index, = struct.unpack(">L", data.read(4))
+    clip_dict['ID'] = image_index
+    i = 4
+    while(i < lwochunk.chunksize):
+        subchunkname, = struct.unpack("4s", data.read(4))
+        subchunklen, = struct.unpack(">H", data.read(2))
+        if subchunkname == "STIL":
+            tobj.pprint("-------- STIL")
+            clip_name, k = read_name(data)
+            #now split text independently from platform
+            #depend on the system where image was saved. NOT the one where the script is run
+            no_sep = "\\"
+            if Blender.sys.sep == no_sep: no_sep ="/"
+            if (no_sep in clip_name):
+                clip_name = clip_name.replace(no_sep, Blender.sys.sep)
+            short_name = Blender.sys.basename(clip_name)
+            if (clip_name == "") or (short_name == ""):
+                tobj.pprint ("Reading CLIP: Empty clip name not allowed. Skipping")
+                discard = data.read(subchunklen-k)
+            clip_dict['NAME'] = clip_name
+            clip_dict['BASENAME'] = short_name
+        elif subchunkname == "XREF":                           #cross reference another image
+            tobj.pprint("-------- XREF")
+            image_index, = struct.unpack(">L", data.read(4))
+            clip_name, k = read_name(data)
+            clip_dict['NAME'] = clip_name
+            clip_dict['XREF'] = image_index
+        elif subchunkname == "NEGA":                           #negate texture effect
+            tobj.pprint("-------- NEGA")
+            n, = struct.unpack(">H", data.read(2))
+            clip_dict['NEGA'] = n
+        else:                                                       # Misc Chunks
+            tobj.pprint("-------- SURF:%s: skipping" % subchunkname)
+            discard = data.read(subchunklen)
+        i = i + 6 + subchunklen
+    #end loop on surf chunks
+    tobj.pprint("read image:%s" % clip_dict)
+    return clip_dict
+
+
+# ===========================
+# === Read Surfaces Block ===
+# ===========================
+def read_surfblok(subchunkdata):
+    lenght = len(subchunkdata)
+    my_dict = {}
+    my_uvname = ""
+    data = cStringIO.StringIO(subchunkdata)
+    ##############################################################
+    # blok header sub-chunk
+    ##############################################################
+    subchunkname, = struct.unpack("4s", data.read(4))
+    subchunklen, = struct.unpack(">h", data.read(2))
+    accumulate_i = subchunklen + 6
+    if subchunkname != 'IMAP':
+        tobj.pprint("---------- SURF: BLOK: %s: block aborting" % subchunkname)
+        return {}, ""
+    tobj.pprint ("---------- IMAP")
+    ordinal, i = read_name(data)
+    my_dict['ORD'] = ordinal
+    my_dict['g_ORD'] = -1
+    my_dict['ENAB'] = True
+    while(i < subchunklen): # ---------left 6------------------------- loop on header parameters
+        sub2chunkname, = struct.unpack("4s", data.read(4))
+        sub2chunklen, = struct.unpack(">h", data.read(2))
+        i = i + 6 + sub2chunklen
+        if sub2chunkname == "CHAN":
+            tobj.pprint("------------ CHAN")
+            sub2chunkname, = struct.unpack("4s", data.read(4))
+            my_dict['CHAN'] = sub2chunkname
+            sub2chunklen -= 4
+        elif sub2chunkname == "ENAB":                             #only present if is to be disabled
+            tobj.pprint("------------ ENAB")
+            ena, = struct.unpack(">h", data.read(2))
+            my_dict['ENAB'] = ena
+            sub2chunklen -= 2
+        elif sub2chunkname == "NEGA":                             #only present if is to be enabled
+            tobj.pprint("------------ NEGA")
+            ena, = struct.unpack(">h", data.read(2))
+            if ena == 1:
+                my_dict['NEGA'] = ena
+            sub2chunklen -= 2
+        elif sub2chunkname == "OPAC":                             #only present if is to be disabled
+            tobj.pprint("------------ OPAC")
+            opa, = struct.unpack(">h", data.read(2))
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['OPAC'] = opa
+            my_dict['OPACVAL'] = s
+            sub2chunklen -= 6
+        elif sub2chunkname == "AXIS":
+            tobj.pprint("------------ AXIS")
+            ena, = struct.unpack(">h", data.read(2))
+            my_dict['DISPLAXIS'] = ena
+            sub2chunklen -= 2
+        else:                                                       # Misc Chunks
+            tobj.pprint("------------ SURF: BLOK: IMAP: %s: skipping" % sub2chunkname)
+            discard = data.read(sub2chunklen)
+    #end loop on blok header subchunks
+    ##############################################################
+    # blok attributes sub-chunk
+    ##############################################################
+    subchunkname, = struct.unpack("4s", data.read(4))
+    subchunklen, = struct.unpack(">h", data.read(2))
+    accumulate_i += subchunklen + 6
+    if subchunkname != 'TMAP':
+        tobj.pprint("---------- SURF: BLOK: %s: block aborting" % subchunkname)
+        return {}, ""
+    tobj.pprint ("---------- TMAP")
+    i = 0
+    while(i < subchunklen): # -----------left 6----------------------- loop on header parameters
+        sub2chunkname, = struct.unpack("4s", data.read(4))
+        sub2chunklen, = struct.unpack(">h", data.read(2))
+        i = i + 6 + sub2chunklen
+        if sub2chunkname == "CNTR":
+            tobj.pprint("------------ CNTR")
+            x, y, z = struct.unpack(">fff", data.read(12))
+            envelope, env_size = read_vx(data)
+            my_dict['CNTR'] = [x, y, z]
+            sub2chunklen -= (12+env_size)
+        elif sub2chunkname == "SIZE":
+            tobj.pprint("------------ SIZE")
+            x, y, z = struct.unpack(">fff", data.read(12))
+            envelope, env_size = read_vx(data)
+            my_dict['SIZE'] = [x, y, z]
+            sub2chunklen -= (12+env_size)
+        elif sub2chunkname == "ROTA":
+            tobj.pprint("------------ ROTA")
+            x, y, z = struct.unpack(">fff", data.read(12))
+            envelope, env_size = read_vx(data)
+            my_dict['ROTA'] = [x, y, z]
+            sub2chunklen -= (12+env_size)
+        elif sub2chunkname == "CSYS":
+            tobj.pprint("------------ CSYS")
+            ena, = struct.unpack(">h", data.read(2))
+            my_dict['CSYS'] = ena
+            sub2chunklen -= 2
+        else:                                                       # Misc Chunks
+            tobj.pprint("------------ SURF: BLOK: TMAP: %s: skipping" % sub2chunkname)
+        if  sub2chunklen > 0:
+            discard = data.read(sub2chunklen)
+    #end loop on blok attributes subchunks
+    ##############################################################
+    # ok, now other attributes without sub_chunks
+    ##############################################################
+    while(accumulate_i < lenght): # ---------------------------------- loop on header parameters: lenght has already stripped the 6 bypes header
+        subchunkname, = struct.unpack("4s", data.read(4))
+        subchunklen, = struct.unpack(">H", data.read(2))
+        accumulate_i = accumulate_i + 6 + subchunklen
+        if subchunkname == "PROJ":
+            tobj.pprint("---------- PROJ")
+            p, = struct.unpack(">h", data.read(2))
+            my_dict['PROJ'] = p
+            subchunklen -= 2
+        elif subchunkname == "AXIS":
+            tobj.pprint("---------- AXIS")
+            a, = struct.unpack(">h", data.read(2))
+            my_dict['MAJAXIS'] = a
+            subchunklen -= 2
+        elif subchunkname == "IMAG":
+            tobj.pprint("---------- IMAG")
+            i, i_size = read_vx(data)
+            my_dict['IMAG'] = i
+            subchunklen -= i_size
+        elif subchunkname == "WRAP":
+            tobj.pprint("---------- WRAP")
+            ww, wh = struct.unpack(">hh", data.read(4))
+            #reduce width and height to just 1 parameter for both
+            my_dict['WRAP'] = max([ww,wh])
+            #my_dict['WRAPWIDTH'] = ww
+            #my_dict['WRAPHEIGHT'] = wh
+            subchunklen -= 4
+        elif subchunkname == "WRPW":
+            tobj.pprint("---------- WRPW")
+            w, = struct.unpack(">f", data.read(4))
+            my_dict['WRPW'] = w
+            envelope, env_size = read_vx(data)
+            subchunklen -= (env_size+4)
+        elif subchunkname == "WRPH":
+            tobj.pprint("---------- WRPH")
+            w, = struct.unpack(">f", data.read(4))
+            my_dict['WRPH'] = w
+            envelope, env_size = read_vx(data)
+            subchunklen -= (env_size+4)
+        elif subchunkname == "VMAP":
+            tobj.pprint("---------- VMAP")
+            vmp, i = read_name(data)
+            my_dict['VMAP'] = vmp
+            my_uvname = vmp
+            subchunklen -= i
+        else:                                                    # Misc Chunks
+            tobj.pprint("---------- SURF: BLOK: %s: skipping" % subchunkname)
+        if  subchunklen > 0:
+            discard = data.read(subchunklen)
+    #end loop on blok subchunks
+    return my_dict, my_uvname
+
+
+# =====================
+# === Read Surfaces ===
+# =====================
+def read_surfs(lwochunk, surf_list, tag_list):
+    my_dict = {}
+    data = cStringIO.StringIO(lwochunk.read())
+    surf_name, i = read_name(data)
+    parent_name, j = read_name(data)
+    i += j
+    if (surf_name == "") or not(surf_name in tag_list):
+        tobj.pprint ("Reading SURF: Actually empty surf name not allowed. Skipping")
+        return {}
+    if (parent_name != ""):
+        parent_index = [x['NAME'] for x in surf_list].count(parent_name)
+        if parent_index >0:
+            my_dict = surf_list[parent_index-1]
+    my_dict['NAME'] = surf_name
+    tobj.pprint ("Surface data for TAG %s" % surf_name)
+    while(i < lwochunk.chunksize):
+        subchunkname, = struct.unpack("4s", data.read(4))
+        subchunklen, = struct.unpack(">H", data.read(2))
+        i = i + 6 + subchunklen #6 bytes subchunk header
+        if subchunkname == "COLR":                             #color: mapped on color
+            tobj.pprint("-------- COLR")
+            r, g, b = struct.unpack(">fff", data.read(12))
+            envelope, env_size = read_vx(data)
+            my_dict['COLR'] = [r, g, b]
+            subchunklen -= (12+env_size)
+        elif subchunkname == "DIFF":                           #diffusion: mapped on reflection (diffuse shader)
+            tobj.pprint("-------- DIFF")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['DIFF'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "SPEC":                           #specularity: mapped to specularity (spec shader)
+            tobj.pprint("-------- SPEC")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['SPEC'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "REFL":                           #reflection: mapped on raymirror
+            tobj.pprint("-------- REFL")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['REFL'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "TRNL":                           #translucency: mapped on same param
+            tobj.pprint("-------- TRNL")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['TRNL'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "GLOS":                           #glossiness: mapped on specularity hardness (spec shader)
+            tobj.pprint("-------- GLOS")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['GLOS'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "TRAN":                           #transparency: inverted and mapped on alpha channel
+            tobj.pprint("-------- TRAN")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['TRAN'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "LUMI":                           #luminosity: mapped on emit channel
+            tobj.pprint("-------- LUMI")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['LUMI'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "GVAL":                           #glow: mapped on add channel
+            tobj.pprint("-------- GVAL")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['GVAL'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "SMAN":                           #smoothing angle
+            tobj.pprint("-------- SMAN")
+            s, = struct.unpack(">f", data.read(4))
+            my_dict['SMAN'] = s
+            subchunklen -= 4
+        elif subchunkname == "SIDE":                           #double sided?
+            tobj.pprint("-------- SIDE")                             #if 1 side do not define key
+            s, = struct.unpack(">H", data.read(2))
+            if s == 3:
+                my_dict['SIDE'] = s
+            subchunklen -= 2
+        elif subchunkname == "RIND":                           #Refraction: mapped on IOR
+            tobj.pprint("-------- RIND")
+            s, = struct.unpack(">f", data.read(4))
+            envelope, env_size = read_vx(data)
+            my_dict['RIND'] = s
+            subchunklen -= (4+env_size)
+        elif subchunkname == "BLOK":                           #blocks
+            tobj.pprint("-------- BLOK")
+            rr, uvname = read_surfblok(data.read(subchunklen))
+            #paranoia setting: preventing adding an empty dict
+            if rr != {}:
+                if not(my_dict.has_key('BLOK')):
+                    my_dict['BLOK'] = [rr]
+                else:
+                    my_dict['BLOK'].append(rr)
+            if uvname != "":
+                my_dict['UVNAME'] = uvname                            #theoretically there could be a number of them: only one used per surf
+            subchunklen = 0 #force ending
+        else:                                                       # Misc Chunks
+            tobj.pprint("-------- SURF:%s: skipping" % subchunkname)
+        if  subchunklen > 0:
+            discard = data.read(subchunklen)
+    #end loop on surf chunks
+    if my_dict.has_key('BLOK'):
+       my_dict['BLOK'].reverse()
+    return my_dict
+
+
+
+
+
+
+# ===========================================================
+# === Generation Routines ===================================
+# ===========================================================
+# ==================================================
+# === Compute vector distance between two points ===
+# ==================================================
+def dist_vector (head, tail): #vector from head to tail
+    return Blender.Mathutils.Vector([head[0] - tail[0], head[1] - tail[1], head[2] - tail[2]])
+
+
+# ================
+# === Find Ear ===
+# ================
+def find_ear(normal, list_dict, verts, face):
+    nv = len(list_dict['MF'])
+    #looping through vertexes trying to find an ear
+    #most likely in case of panic
+    mlc = 0
+    mla = 1
+    mlb = 2
+
+    for c in range(nv):
+        a = (c+1) % nv; b = (a+1) % nv
+
+        if list_dict['P'][a] > 0.0: #we have to start from a convex vertex
+        #if (list_dict['P'][a] > 0.0) and (list_dict['P'][b] <= 0.0): #we have to start from a convex vertex
+            mlc = c
+            mla = a
+            mlb = b
+            #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]))
+            #tobj.pprint ("   ok, this one passed")
+            concave = 0
+            concave_inside = 0
+            for xx in range(nv): #looking for concave vertex
+                if (list_dict['P'][xx] <= 0.0) and (xx != b) and (xx != c): #cannot be a: it's convex
+                    #ok, found concave vertex
+                    concave = 1
+                    #a, b, c, xx are all meta-meta vertex indexes
+                    mva = list_dict['MF'][a] #meta-vertex-index
+                    mvb = list_dict['MF'][b]
+                    mvc = list_dict['MF'][c]
+                    mvxx = list_dict['MF'][xx]
+                    va = face[mva] #vertex
+                    vb = face[mvb]
+                    vc = face[mvc]
+                    vxx = face[mvxx]
+
+                    #Distances
+                    d_ac_v = list_dict['D'][c]
+                    d_ba_v = list_dict['D'][a]
+                    d_cb_v = dist_vector(verts[vc], verts[vb])
+
+                    #distance from triangle points
+                    d_xxa_v = dist_vector(verts[vxx], verts[va])
+                    d_xxb_v = dist_vector(verts[vxx], verts[vb])
+                    d_xxc_v = dist_vector(verts[vxx], verts[vc])
+
+                    #normals
+                    n_xxa_v = Blender.Mathutils.CrossVecs(d_ba_v, d_xxa_v)
+                    n_xxb_v = Blender.Mathutils.CrossVecs(d_cb_v, d_xxb_v)
+                    n_xxc_v = Blender.Mathutils.CrossVecs(d_ac_v, d_xxc_v)
+
+                    #how are oriented the normals?
+                    p_xxa_v = Blender.Mathutils.DotVecs(normal, n_xxa_v)
+                    p_xxb_v = Blender.Mathutils.DotVecs(normal, n_xxb_v)
+                    p_xxc_v = Blender.Mathutils.DotVecs(normal, n_xxc_v)
+
+                    #if normals are oriented all to same directions - so it is insida
+                    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)):
+                        #print "vertex %d: concave inside" % xx
+                        concave_inside = 1
+                        break
+                #endif found a concave vertex
+            #end loop looking for concave vertexes
+            if (concave == 0) or (concave_inside == 0):
+                #no concave vertexes in polygon (should not be): return immediately
+                #looped all concave vertexes and no one inside found
+                return [c, a, b]
+        #no convex vertex, try another one
+    #end loop to find a suitable base vertex for ear
+    #looped all candidate ears and find no-one suitable
+    tobj.pprint ("Reducing face: no valid ear found to reduce!")
+    return [mlc, mla, mlb] #uses most likely
+
+
+
+
+# ====================
+# === Reduce Faces ===
+# ====================
+# http://www-cgrl.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/cutting_ears.html per l'import
+def reduce_face(verts, face):
+    nv = len (face)
+    if nv == 3: return [[0,1,2]] #trivial decomposition list
+    list_dict = {}
+    #meta-vertex indexes
+    list_dict['MF'] = range(nv) # these are meta-vertex-indexes
+    list_dict['D'] = [None] * nv
+    list_dict['X'] = [None] * nv
+    list_dict['P'] = [None] * nv
+    #list of distances
+    for mvi in list_dict['MF']:
+        #vector between two vertexes
+        mvi_hiend = (mvi+1) % nv      #last-to-first
+        vi_hiend = face[mvi_hiend] #vertex
+        vi = face[mvi]
+        list_dict['D'][mvi] = dist_vector(verts[vi_hiend], verts[vi])
+    #list of cross products - normals evaluated into vertexes
+    for vi in range(nv):
+        list_dict['X'][vi] = Blender.Mathutils.CrossVecs(list_dict['D'][vi], list_dict['D'][vi-1])
+    my_face_normal = Blender.Mathutils.Vector([list_dict['X'][0][0], list_dict['X'][0][1], list_dict['X'][0][2]])
+    #list of dot products
+    list_dict['P'][0] = 1.0
+    for vi in range(1, nv):
+        list_dict['P'][vi] = Blender.Mathutils.DotVecs(my_face_normal, list_dict['X'][vi])
+    #is there at least one concave vertex?
+    #one_concave = reduce(lambda x, y: (x) or (y<=0.0), list_dict['P'], 0)
+    one_concave = reduce(lambda x, y: (x) + (y<0.0), list_dict['P'], 0)
+    decomposition_list = []
+
+    while 1:
+        if nv == 3: break
+        if one_concave:
+            #look for triangle
+            ct = find_ear(my_face_normal, list_dict, verts, face)
+            mv0 = list_dict['MF'][ct[0]] #meta-vertex-index
+            mv1 = list_dict['MF'][ct[1]]
+            mv2 = list_dict['MF'][ct[2]]
+            #add the triangle to output list
+            decomposition_list.append([mv0, mv1, mv2])
+            #update data structures removing remove middle vertex from list
+            #distances
+            v0 = face[mv0] #vertex
+            v1 = face[mv1]
+            v2 = face[mv2]
+            list_dict['D'][ct[0]] = dist_vector(verts[v2], verts[v0])
+            #cross products
+            list_dict['X'][ct[0]] = Blender.Mathutils.CrossVecs(list_dict['D'][ct[0]], list_dict['D'][ct[0]-1])
+            list_dict['X'][ct[2]] = Blender.Mathutils.CrossVecs(list_dict['D'][ct[2]], list_dict['D'][ct[0]])
+            #list of dot products
+            list_dict['P'][ct[0]] = Blender.Mathutils.DotVecs(my_face_normal, list_dict['X'][ct[0]])
+            list_dict['P'][ct[2]] = Blender.Mathutils.DotVecs(my_face_normal, list_dict['X'][ct[2]])
+            #physical removal
+            list_dict['MF'].pop(ct[1])
+            list_dict['D'].pop(ct[1])
+            list_dict['X'].pop(ct[1])
+            list_dict['P'].pop(ct[1])
+            one_concave = reduce(lambda x, y: (x) or (y<0.0), list_dict['P'], 0)
+            nv -=1
+        else: #here if no more concave vertexes
+            if nv == 4: break  #quads only if no concave vertexes
+            decomposition_list.append([list_dict['MF'][0], list_dict['MF'][1], list_dict['MF'][2]])
+            #physical removal
+            list_dict['MF'].pop(1)
+            nv -=1
+    #end while there are more my_face to triangulate
+    decomposition_list.append(list_dict['MF'])
+    return decomposition_list
+
+
+# =========================
+# === Recalculate Faces ===
+# =========================
+# --------- this do the standard face + ptag_dict + uv-map recalc
+def recalc_faces(verts, faces, polytag_dict, facesuv_dict):
+    # init local face list
+    my_faces = []
+    # init local uvface dict
+    my_facesuv = {}
+    for uvname in facesuv_dict:
+        my_facesuv[uvname] = []
+    replaced_faces_dict = {}
+    j = 0
+    if len(faces)==0:
+        return faces, polytag_dict, facesuv_dict
+    for i in range(len(faces)):
+        # i = index that spans on original faces
+        # j = index that spans on new faces
+        if not i%100 and my_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/len(faces), "Recalculating faces")
+        numfaceverts=len(faces[i])
+        if numfaceverts < 4:                #This face is a triangle or quad: more strict - it has to be a triangle
+            my_faces.append(faces[i])       #ok, leave it alone ....
+            for uvname in facesuv_dict:
+                my_facesuv[uvname].append(facesuv_dict[uvname][i])
+            replaced_faces_dict[i] = [j]     #.... but change the nuber order of the face
+            j += 1
+        else:                                                # Reduce n-sided convex polygon.
+            meta_faces = reduce_face(verts, faces[i])   # Indices of triangles.
+            this_faces = []                                  # list of triangles poly replacing original face
+            this_faces_index = []
+            for mf in meta_faces:
+                ll = len(mf)
+                if ll == 3: #triangle
+                    this_faces.append([faces[i][mf[0]], faces[i][mf[1]], faces[i][mf[2]]])
+                else:        #quads
+                    this_faces.append([faces[i][mf[0]], faces[i][mf[1]], faces[i][mf[2]], faces[i][mf[3]]])
+                for uvname in facesuv_dict:
+                    if ll == 3:  #triangle
+                        my_facesuv[uvname].append([facesuv_dict[uvname][i][mf[0]], facesuv_dict[uvname][i][mf[1]], facesuv_dict[uvname][i][mf[2]]])
+                    else:        #quads
+                        my_facesuv[uvname].append([facesuv_dict[uvname][i][mf[0]], facesuv_dict[uvname][i][mf[1]], facesuv_dict[uvname][i][mf[2]], facesuv_dict[uvname][i][mf[3]]])
+                this_faces_index.append(j)
+                j +=1
+            my_faces.extend(this_faces)
+            replaced_faces_dict[i] = this_faces_index   #face i substituted by this list of faces
+        #endif on face vertex number
+    #end loop on every face
+    #now we have the new faces list and a dictionary replacement.
+    #going for polygon tagging
+    my_ptag_dict = {}
+    for tag in polytag_dict:                                      #for every tag group
+        my_ptag_dict[tag] = []                                    #rebuild a new entry
+        for poly in polytag_dict[tag]:                            #take every element of old face list
+            my_ptag_dict[tag].extend(replaced_faces_dict[poly])   #substitutes the element of new face list
+    return my_faces, my_ptag_dict, my_facesuv
+
+
+# ========================================
+# === Revert list keeping first vertex ===
+# ========================================
+def revert (llist):
+    #different flavors: the reverse one is the one that works better
+    #rhead = [llist[0]]
+    #rtail = llist[1:]
+    #rhead.extend(rtail)
+    #return rhead
+    #--------------
+    rhead=copy.deepcopy(llist)
+    rhead.reverse()
+    return rhead
+    #--------------
+    #return llist
+
+# ====================================
+# === Modified Create Blender Mesh ===
+# ====================================
+def my_create_mesh(complete_vertlist, complete_facelist, current_facelist, objname, not_used_faces):
+    #take the needed faces and update the not-used face list
+    vertex_map = [-1] * len(complete_vertlist)
+    cur_ptag_faces = []
+    for ff in current_facelist:
+        cur_face = complete_facelist[ff]
+        cur_ptag_faces.append(cur_face)
+        if not_used_faces != []: not_used_faces[ff] = -1
+        for vv in cur_face:
+            vertex_map[vv] = 1
+        #end loop on vertex on this face
+    #end loop on faces
+
+    mesh = Blender.NMesh.GetRaw()
+
+    #append vertexes
+    jj = 0
+    for i in range(len(complete_vertlist)):
+        if vertex_map[i] == 1:
+            if not i%100 and my_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/len(complete_vertlist), "Generating Verts")
+            x, y, z = complete_vertlist[i]
+            mesh.verts.append(Blender.NMesh.Vert(x, y, z))
+            vertex_map[i] = jj
+            jj += 1
+    #end sweep over vertexes
+
+    #append faces
+    for i in range(len(cur_ptag_faces)):
+        if not i%100 and my_meshtools.show_progress: Blender.Window.DrawProgressBar(float(i)/len(cur_ptag_faces), "Generating Faces")
+        face = Blender.NMesh.Face()
+        rev_face = revert(cur_ptag_faces[i])
+        for vi in rev_face:
+        #for vi in cur_ptag_faces[i]:
+            index = vertex_map[vi]
+            face.v.append(mesh.verts[index])
+        #end sweep over vertexes
+        mesh.faces.append(face)
+    #end sweep over faces
+
+    if not my_meshtools.overwrite_mesh_name:
+        objname = my_meshtools.versioned_name(objname)
+    Blender.NMesh.PutRaw(mesh, objname)    # Name the Mesh
+    obj = Blender.Object.GetSelected()[0]
+    obj.name=objname        # Name the Object
+    Blender.Redraw()
+    return obj, not_used_faces              #return the created object
+
+
+# ============================================
+# === Set Subsurf attributes on given mesh ===
+# ============================================
+def set_subsurf(obj):
+    msh = obj.getData()
+    msh.setSubDivLevels([2, 2])
+    msh.mode |= Blender.NMesh.Modes.SUBSURF
+    msh.update(1)
+    obj.makeDisplayList()
+    return
+
+
+# =================================
+# === object size and dimension ===
+# =================================
+def obj_size_pos(obj):
+    bbox = obj.getBoundBox()
+    bbox_min = map(lambda *row: min(row), *bbox) #transpose & get min
+    bbox_max = map(lambda *row: max(row), *bbox) #transpose & get max
+    obj_size = (bbox_max[0]-bbox_min[0], bbox_max[1]-bbox_min[1], bbox_max[2]-bbox_min[2])
+    obj_pos = ( (bbox_max[0]+bbox_min[0]) / 2, (bbox_max[1]+bbox_min[1]) / 2, (bbox_max[2]+bbox_min[2]) / 2)
+    return (obj_size, obj_pos)
+
+
+# =========================
+# === Create the object ===
+# =========================
+def create_objects(objspec_list):
+    nf = len(objspec_list[3])
+    not_used_faces = range(nf)
+    ptag_dict = objspec_list[5]
+    obj_dict = {}  #links tag names to object, used for material assignments
+    obj_dim_dict = {}
+    obj_list = []  #have it handy for parent association
+    middlechar = "+"
+    endchar = ""
+    if (objspec_list[6] == 1):
+        middlechar = endchar = "#"
+    for cur_tag in ptag_dict.keys():
+        if ptag_dict[cur_tag] != []:
+            cur_obj, not_used_faces= my_create_mesh(objspec_list[2], objspec_list[3], ptag_dict[cur_tag], objspec_list[0][:9]+middlechar+cur_tag[:9], not_used_faces)
+            if objspec_list[6] == 1:
+                set_subsurf(cur_obj)
+            obj_dict[cur_tag] = cur_obj
+            obj_dim_dict[cur_tag] = obj_size_pos(cur_obj)
+            obj_list.append(cur_obj)
+    #end loop on current group
+    #and what if some faces not used in any named PTAG? get rid of unused faces
+    for ff in range(nf):
+        tt = nf-1-ff #reverse order
+        if not_used_faces[tt] == -1:
+            not_used_faces.pop(tt)
+    #end sweep on unused face list
+    if not_used_faces != []:
+        cur_obj, not_used_faces = my_create_mesh(objspec_list[2], objspec_list[3], not_used_faces, objspec_list[0][:9]+middlechar+"lone", [])
+        #my_meshtools.create_mesh(objspec_list[2], not_used_faces, "_unk") #vert, faces, name
+        #cur_obj = Blender.Object.GetSelected()[0]
+        if objspec_list[6] == 1:
+            set_subsurf(cur_obj)
+        obj_dict["lone"] = cur_obj
+        obj_dim_dict["lone"] = obj_size_pos(cur_obj)
+        obj_list.append(cur_obj)
+    objspec_list[1] = obj_dict
+    objspec_list[4] = obj_dim_dict
+    scene = Blender.Scene.getCurrent ()                   # get the current scene
+    ob = Blender.Object.New ('Empty', objspec_list[0]+endchar)    # make empty object
+    scene.link (ob)                                       # link the object into the scene
+    ob.makeParent(obj_list, 1, 0)                         # set the root for created objects (no inverse, update scene hyerarchy (slow))
+    Blender.Redraw()
+    return
+
+
+# =====================
+# === Load an image ===
+# =====================
+#extensively search for image name
+def load_image(dir_part, name):
+    img = None
+    nname = Blender.sys.splitext(name)
+    lname = [c.lower() for c in nname]
+    ext_list = []
+    if lname[1] != nname[1]:
+        ext_list.append(lname[1])
+    ext_list.extend(['.tga', '.png', '.jpg', '.gif', '.bmp'])  #order from best to worst (personal judgement) bmp last cause of nasty bug
+    #first round: original "case"
+    current = Blender.sys.join(dir_part, name)
+    name_list = [current]
+    name_list.extend([Blender.sys.makename(current, ext) for ext in ext_list])
+    #second round: lower "case"
+    if lname[0] != nname[0]:
+        current = Blender.sys.join(dir_part, lname[0])
+        name_list.extend([Blender.sys.makename(current, ext) for ext in ext_list])
+    for nn in name_list:
+        if Blender.sys.exists(nn) == 1:
+            break
+    try:
+        img = Blender.Image.Load(nn)
+        return img
+    except IOError:
+        return None
+
+
+# ===========================================
+# === Lookup for image index in clip_list ===
+# ===========================================
+def lookup_imag(clip_list,ima_id):
+    for ii in clip_list:
+        if ii['ID'] == ima_id:
+            if ii.has_key('XREF'):
+                #cross reference - recursively look for images
+                return lookup_imag(clip_list, ii['XREF'])
+            else:
+                return ii
+    return None
+
+
+# ===================================================
+# === Create and assign image mapping to material ===
+# ===================================================
+def create_blok(surf, mat, clip_list, dir_part, obj_size, obj_pos):
+
+    def output_size_ofs(size, pos, blok):
+        #just automate repetitive task
+        c_map = [0,1,2]
+        c_map_txt = ["    X--", "    -Y-", "    --Z"]
+        if blok['MAJAXIS'] == 0:
+            c_map = [1,2,0]
+        if blok['MAJAXIS'] == 2:
+            c_map = [0,2,1]
+        tobj.pprint ("!!!axis mapping:")
+        for mp in c_map: tobj.pprint (c_map_txt[mp])
+
+        s = ["1.0 (Forced)"] * 3
+        o = ["0.0 (Forced)"] * 3
+        if blok['SIZE'][0] > 0.0:          #paranoia controls
+            s[0] = "%.5f" % (size[0]/blok['SIZE'][0])
+            o[0] = "%.5f" % ((blok['CNTR'][0]-pos[0])/blok['SIZE'][0])
+        if blok['SIZE'][1] > 0.0:
+            s[2] = "%.5f" % (size[2]/blok['SIZE'][1])
+            o[2] = "%.5f" % ((blok['CNTR'][1]-pos[2])/blok['SIZE'][1])
+        if blok['SIZE'][2] > 0.0:
+            s[1] = "%.5f" % (size[1]/blok['SIZE'][2])
+            o[1] = "%.5f" % ((blok['CNTR'][2]-pos[1])/blok['SIZE'][2])
+        tobj.pprint ("!!!texture size and offsets:")
+        tobj.pprint ("    sizeX = %s; sizeY = %s; sizeZ = %s" % (s[c_map[0]], s[c_map[1]], s[c_map[2]]))
+        tobj.pprint ("    ofsX = %s; ofsY = %s; ofsZ = %s" % (o[c_map[0]], o[c_map[1]], o[c_map[2]]))
+        return
+
+    ti = 0
+    for blok in surf['BLOK']:
+        tobj.pprint ("#...................................................................#")
+        tobj.pprint ("# Processing texture block no.%s for surf %s" % (ti,surf['NAME']))
+        tobj.pprint ("#...................................................................#")
+        tobj.pdict (blok)
+        if ti > 9: break                                    #only 8 channels 0..7 allowed for texture mapping
+        if not blok['ENAB']:
+            tobj.pprint (  "***Image is not ENABled! Quitting this block")
+            break
+        if not(blok.has_key('IMAG')):
+            tobj.pprint (  "***No IMAGe for this block? Quitting")
+            break                 #extract out the image index within the clip_list
+        tobj.pprint ("looking for image number %d" % blok['IMAG'])
+        ima = lookup_imag(clip_list, blok['IMAG'])
+        if ima == None:
+            tobj.pprint (  "***Block index image not within CLIP list? Quitting Block")
+            break                              #safety check (paranoia setting)
+        #look for images
+        img = load_image("",ima['NAME'])
+        if img == None:
+            tobj.pprint (  "***No image %s found: trying LWO file subdir" % ima['NAME'])
+            img = load_image(dir_part,ima['BASENAME'])
+        if img == None:
+            tobj.pprint (  "***No image %s found in directory %s: trying Images subdir" % (ima['BASENAME'], dir_part))
+            img = load_image(dir_part+Blender.sys.sep+"Images",ima['BASENAME'])
+        if img == None:
+            tobj.pprint (  "***No image %s found: trying alternate Images subdir" % ima['BASENAME'])
+            img = load_image(dir_part+Blender.sys.sep+".."+Blender.sys.sep+"Images",ima['BASENAME'])
+        if img == None:
+            tobj.pprint (  "***No image %s found: giving up" % ima['BASENAME'])
+            break
+        #lucky we are: we have an image
+        tname = str(ima['ID'])
+        if blok.has_key('CHAN'):
+            tname = tname + "+" + blok['CHAN']
+        newtex = Blender.Texture.New(tname)
+        newtex.setType('Image')                 # make it an image texture
+        newtex.image = img
+        #how does it extends beyond borders
+        if blok.has_key('WRAP'):
+            if (blok['WRAP'] == 3) or (blok['WRAP'] == 2):
+                newtex.setExtend('Extend')
+            elif (blok['WRAP'] == 1):
+                newtex.setExtend('Repeat')
+            elif (blok['WRAP'] == 0):
+                newtex.setExtend('Clip')
+        tobj.pprint ("generated texture %s" % tname)
+
+        blendmode_list = ['Mix',
+                         'Subtractive',
+                         'Difference',
+                         'Multiply',
+                         'Divide',
+                         'Mix (CalcAlpha already set; try setting Stencil!',
+                         'Texture Displacement',
+                         'Additive']
+        set_blendmode = 7 #default additive
+        if blok.has_key('OPAC'):
+            set_blendmode = blok['OPAC']
+        if set_blendmode == 5: #transparency
+            newtex.imageFlags |= Blender.Texture.ImageFlags.CALCALPHA
+        tobj.pprint ("!!!Set Texture -> MapTo -> Blending Mode = %s" % blendmode_list[set_blendmode])
+
+        set_dvar = 1.0
+        if blok.has_key('OPACVAL'):
+            set_dvar = blok['OPACVAL']
+
+        #MapTo is determined by CHAN parameter
+        mapflag = Blender.Texture.MapTo.COL  #default to color
+        if blok.has_key('CHAN'):
+            if blok['CHAN'] == 'COLR':
+                tobj.pprint ("!!!Set Texture -> MapTo -> Col = %.3f" % set_dvar)
+                if set_blendmode == 0:
+                    surf['g_IM'] = img                 #do not set anything, just save image object for later assignment
+            if blok['CHAN'] == 'BUMP':
+                mapflag = Blender.Texture.MapTo.NOR
+                tobj.pprint ("!!!Set Texture -> MapTo -> Nor = %.3f" % set_dvar)
+            if blok['CHAN'] == 'LUMI':
+                mapflag = Blender.Texture.MapTo.EMIT
+                tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
+            if blok['CHAN'] == 'DIFF':
+                mapflag = Blender.Texture.MapTo.REF
+                tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
+            if blok['CHAN'] == 'SPEC':
+                mapflag = Blender.Texture.MapTo.SPEC
+                tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
+            if blok['CHAN'] == 'TRAN':
+                mapflag = Blender.Texture.MapTo.ALPHA
+                tobj.pprint ("!!!Set Texture -> MapTo -> DVar = %.3f" % set_dvar)
+        if blok.has_key('NEGA'):
+            tobj.pprint ("!!!Watch-out: effect of this texture channel must be INVERTED!")
+
+        #the TexCo flag is determined by PROJ parameter
+        if blok.has_key('PROJ'):
+            if blok['PROJ'] == 0: #0 - Planar
+               tobj.pprint ("!!!Flat projection")
+               coordflag = Blender.Texture.TexCo.ORCO
+               output_size_ofs(obj_size, obj_pos, blok)
+            elif blok['PROJ'] == 1: #1 - Cylindrical
+               tobj.pprint ("!!!Cylindrical projection")
+               coordflag = Blender.Texture.TexCo.ORCO
+               output_size_ofs(obj_size, obj_pos, blok)
+            elif blok['PROJ'] == 2: #2 - Spherical
+               tobj.pprint ("!!!Spherical projection")
+               coordflag = Blender.Texture.TexCo.ORCO
+               output_size_ofs(obj_size, obj_pos, blok)
+            elif blok['PROJ'] == 3: #3 - Cubic
+               tobj.pprint ("!!!Cubic projection")
+               coordflag = Blender.Texture.TexCo.ORCO
+               output_size_ofs(obj_size, obj_pos, blok)
+            elif blok['PROJ'] == 4: #4 - Front Projection
+               tobj.pprint ("!!!Front projection")
+               coordflag = Blender.Texture.TexCo.ORCO
+               output_size_ofs(obj_size, obj_pos, blok)
+            elif blok['PROJ'] == 5: #5 - UV
+               tobj.pprint ("UVMapped")
+               coordflag = Blender.Texture.TexCo.UV
+        mat.setTexture(ti, newtex, coordflag, mapflag)
+        ti += 1
+    #end loop over bloks
+    return
+
+
+
+
+# ========================================
+# === Create and assign a new material ===
+# ========================================
+#def create_material(surf_list, ptag_dict, obj, clip_list, uv_dict, dir_part):
+def create_material(clip_list, surf_list, objspec, dir_part):
+    if (surf_list == []) or (objspec[5] == {}) or (objspec[1] == {}):
+        tobj.pprint( surf_list)
+        tobj.pprint( objspec[5])
+        tobj.pprint( objspec[1])
+        tobj.pprint( "something getting wrong in create_material ...")
+        return
+    obj_dict = objspec[1]
+    obj_dim_dict = objspec[4]
+    ptag_dict = objspec[5]
+    uvcoords_dict = objspec[7]
+    facesuv_dict = objspec[8]
+    for surf in surf_list:
+        if (surf['NAME'] in ptag_dict.keys()):
+            tobj.pprint ("#-------------------------------------------------------------------#")
+            tobj.pprint ("Processing surface (material): %s" % surf['NAME'])
+            tobj.pprint ("#-------------------------------------------------------------------#")
+            #material set up
+            facelist = ptag_dict[surf['NAME']]
+            #bounding box and position
+            cur_obj = obj_dict[surf['NAME']]
+            obj_size = obj_dim_dict[surf['NAME']][0]
+            obj_pos = obj_dim_dict[surf['NAME']][1]
+            tobj.pprint(surf)
+            mat = Blender.Material.New(surf['NAME'])
+            if surf.has_key('COLR'):
+                mat.rgbCol = surf['COLR']
+            if surf.has_key('LUMI'):
+                mat.setEmit(surf['LUMI'])
+            if surf.has_key('GVAL'):
+                mat.setAdd(surf['GVAL'])
+            if surf.has_key('SPEC'):
+                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]
+            if surf.has_key('DIFF'):
+                mat.setRef(surf['DIFF'])                         #lwo [0.0, 1.0] - blender [0.0, 1.0]
+            if surf.has_key('REFL'):
+                mat.setRayMirr(surf['REFL'])                     #lwo [0.0, 1.0] - blender [0.0, 1.0]
+                #mat.setMode('RAYMIRROR')
+                mat.mode |= Blender.Material.Modes.RAYMIRROR
+            #WARNING translucency not implemented yet check 2.36 API
+            #if surf.has_key('TRNL'):
+            #
+            if surf.has_key('GLOS'):                             #lwo [0.0, 1.0] - blender [0, 255]
+                glo = int(371.67 * surf['GLOS'] - 42.334)        #linear mapping - seems to work better than exp mapping
+                if glo <32:  glo = 32                            #clamped to 32-255
+                if glo >255: glo = 255
+                mat.setHardness(glo)
+            if surf.has_key('TRAN'):
+                mat.setAlpha(1.0-surf['TRAN'])                                        #lwo [0.0, 1.0] - blender [1.0, 0.0]
+                mat.mode |= Blender.Material.Modes.RAYTRANSP
+            if surf.has_key('RIND'):
+                s = surf['RIND']
+                if s < 1.0: s = 1.0
+                if s > 3.0: s = 3.0
+                mat.setIOR(s)                                                         #clipped to blender [1.0, 3.0]
+                mat.mode |= Blender.Material.Modes.RAYTRANSP
+            if surf.has_key('BLOK') and surf['BLOK'] != []:
+                #update the material according to texture.
+                create_blok(surf, mat, clip_list, dir_part, obj_size, obj_pos)
+            #finished setting up the material
+            #associate material to mesh
+            msh = cur_obj.getData()
+            mat_index = len(msh.getMaterials(1))
+            msh.addMaterial(mat)
+            msh.mode |= Blender.NMesh.Modes.AUTOSMOOTH                                #smooth it anyway
+            msh.update(1)
+            for f in range(len(msh.faces)):
+                msh.faces[f].materialIndex = mat_index
+                msh.faces[f].smooth = 1 #smooth it anyway
+                msh.faces[f].mode |= Blender.NMesh.FaceModes.TWOSIDE                  #set it anyway
+                msh.faces[f].transp = Blender.NMesh.FaceTranspModes['SOLID']
+                msh.faces[f].flag = Blender.NMesh.FaceTranspModes['SOLID']
+                if surf.has_key('SMAN'):
+                    #not allowed mixed mode mesh (all the mesh is smoothed and all with the same angle)
+                    #only one smoothing angle will be active! => take the max one
+                    s = int(surf['SMAN']/3.1415926535897932384626433832795*180.0)     #lwo in radians - blender in degrees
+                    if msh.getMaxSmoothAngle() < s: msh.setMaxSmoothAngle(s)
+                #if surf.has_key('SIDE'):
+                #    msh.faces[f].mode |= Blender.NMesh.FaceModes.TWOSIDE             #set it anyway
+                if surf.has_key('TRAN') and mat.getAlpha()<1.0:
+                    msh.faces[f].transp = Blender.NMesh.FaceTranspModes['ALPHA']
+                if surf.has_key('UVNAME') and facesuv_dict.has_key(surf['UVNAME']):
+                    #assign uv-data
+                    msh.hasFaceUV(1)
+                    #WARNING every block could have its own uvmap set of coordinate. take only the first one
+                    facesuv_list = facesuv_dict[surf['UVNAME']]
+                    #print "facesuv_list: ",f , facelist[f]
+                    rev_face = revert(facesuv_list[facelist[f]])
+                    for vi in rev_face:
+                        msh.faces[f].uv.append(uvcoords_dict[surf['UVNAME']][vi])
+                    if surf.has_key('g_IM'):
+                        msh.faces[f].mode |= Blender.NMesh.FaceModes['TEX']
+                        msh.faces[f].image = surf['g_IM']
+            #end loop over faces
+            msh.update(1)
+            mat_index += 1
+        #end if exist faces ib this object belonging to surf
+    #end loop on surfaces
+    return
+
 
 # ======================
 # === Read Faces 6.0 ===
 # ======================
 def read_faces_6(lwochunk):
-       data = cStringIO.StringIO(lwochunk.read())
-       faces = []
-       polygon_type = data.read(4)
-       if polygon_type != "FACE":
-               print "No Faces Were Found. Polygon Type:", polygon_type
-               return ""
-       i = 0
-       while(i < lwochunk.chunksize-4):
-               if not i%100 and meshtools.show_progress:
-                  Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
-               facev = []
-               numfaceverts, = struct.unpack(">H", data.read(2))
-               i += 2
-
-               for j in range(numfaceverts):
-                       index, index_size = read_vx(data)
-                       i += index_size
-                       facev.append(index)
-               facev.reverse()
-               faces.append(facev)
-       return faces
+    data = cStringIO.StringIO(lwochunk.read())
+    faces = []
+    polygon_type = data.read(4)
+    subsurf = 0
+    if polygon_type != "FACE" and polygon_type != "PTCH":
+        tobj.pprint("No FACE/PATCH Were Found. Polygon Type: %s" % polygon_type)
+        return "", 2
+    if polygon_type == 'PTCH': subsurf = 1
+    i = 0
+    while(i < lwochunk.chunksize-4):
+        if not i%100 and my_meshtools.show_progress:
+            Blender.Window.DrawProgressBar(float(i)/lwochunk.chunksize, "Reading Faces")
+        facev = []
+        numfaceverts, = struct.unpack(">H", data.read(2))
+        i += 2
+
+        for j in range(numfaceverts):
+            index, index_size = read_vx(data)
+            i += index_size
+            facev.append(index)
+        faces.append(facev)
+    tobj.pprint("read %s faces; type of block %d (0=FACE; 1=PATCH)" % (len(faces), subsurf))
+    return faces, subsurf
+
+
+
+# ===========================================================
+# === Start the show and main callback ======================
+# ===========================================================
 
 def fs_callback(filename):
-       read(filename)
+    read(filename)
 
 Blender.Window.FileSelector(fs_callback, "Import LWO")
index 205a280f878d6ba0230565f4550c1948584d801e..c220d66b70916465075b225d72fab5422bb6fee2 100644 (file)
@@ -9,7 +9,7 @@ Tooltip: 'Save a Wavefront OBJ File'
 
 __author__ = "Campbell Barton, Jiri Hnidek"
 __url__ = ["blender", "elysiun"]
-__version__ = "0.9"
+__version__ = "1.0"
 
 __bpydoc__ = """\
 This script is an exporter to OBJ file format.
@@ -49,134 +49,287 @@ def newFName(ext):
        return Get('filename')[: -len(Get('filename').split('.', -1)[-1]) ] + ext
 
 
+def fixName(name):
+       if name == None:
+               return 'None'
+       else:
+               return name.replace(' ', '_')
+
+
+
+
 from Blender import *
 
-NULL_MAT = '(null)'
-NULL_IMG = '(null)' # from docs at http://astronomy.swin.edu.au/~pbourke/geomformats/obj/ also could be 'off'
+global MTL_DICT
+
+# A Dict of Materials
+# (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
+MTL_DICT = {} 
 
 def save_mtl(filename):
+       global MTL_DICT
+       
+       world = World.GetCurrent()
+       if world:
+               worldAmb = world.getAmb()
+       else:
+               worldAmb = (0,0,0) # Default value
+       
        file = open(filename, "w")
-       file.write('# Blender MTL File: %s\n' % (Get('filename')))
-       for mat in Material.Get():
-               file.write('newmtl %s\n' % (mat.getName())) # Define a new material
-               file.write('Ns %s\n' % ((mat.getHardness()-1) * 1.9607843137254901 ) ) # Hardness, convert blenders 1-511 to MTL's 
-               file.write('Kd %.6f %.6f %.6f\n' % tuple(mat.getRGBCol())) # Diffuse
-               file.write('Ka %.6f %.6f %.6f\n' % tuple(mat.getMirCol())) # Ambient, uses mirror colour,
-               file.write('Ks %.6f %.6f %.6f\n' % tuple(mat.getSpecCol())) # Specular
-               file.write('Ni %.6f\n' % mat.getIOR()) # Refraction index
-               file.write('d %.6f\n' % mat.getAlpha()) # Alpha (obj uses 'd' for dissolve)
-               
-               # illum, 0 to disable lightng, 2 is normal.
-               if mat.getMode() & Material.Modes['SHADELESS']:
-                       file.write('illum 0\n\n') # ignore lighting
+       file.write('# Blender MTL File: %s\n' % Get('filename').split('\\')[-1].split('/')[-1])
+       file.write('# Material Count: %i\n' % len(MTL_DICT))
+       # Write material/image combinations we have used.
+       for key, mtl_mat_name in MTL_DICT.iteritems():
+               
+               # Get the Blender data for the material and the image.
+               # Having an image named None will make a bug, dont do it :)
+               
+               file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname
+               
+               if key[0] == None:
+                       #write a dummy material here?
+                       file.write('Ns 0\n')
+                       file.write('Ka %s %s %s\n' %  tuple([round(c, 6) for c in worldAmb])  ) # Ambient, uses mirror colour,
+                       file.write('Kd 0.8 0.8 0.8\n')
+                       file.write('Ks 0.8 0.8 0.8\n')
+                       file.write('d 1\n') # No alpha
+                       file.write('illum 2\n') # light normaly 
+                       
                else:
-                       file.write('illum 2\n\n') # light normaly               
+                       mat = Material.Get(key[0])
+                       file.write('Ns %s\n' % round((mat.getHardness()-1) * 1.9607843137254901 ) ) # Hardness, convert blenders 1-511 to MTL's 
+                       file.write('Ka %s %s %s\n' %  tuple([round(c*mat.getAmb(), 6) for c in worldAmb])  ) # Ambient, uses mirror colour,
+                       file.write('Kd %s %s %s\n' % tuple([round(c*mat.getRef(), 6) for c in mat.getRGBCol()]) ) # Diffuse
+                       file.write('Ks %s %s %s\n' % tuple([round(c*mat.getSpec(), 6) for c in mat.getSpecCol()]) ) # Specular
+                       file.write('Ni %s\n' % round(mat.getIOR(), 6)) # Refraction index
+                       file.write('d %s\n' % round(mat.getAlpha(), 6)) # Alpha (obj uses 'd' for dissolve)
+                       
+                       # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting.
+                       if mat.getMode() & Material.Modes['SHADELESS']:
+                               file.write('illum 0\n') # ignore lighting
+                       elif mat.getSpec() == 0:
+                               file.write('illum 1\n') # no specular.
+                       else:
+                               file.write('illum 2\n') # light normaly 
+               
+               
+               # Write images!
+               if key[1] != None:  # We have an image on the face!
+                       img = Image.Get(key[1])
+                       file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image                 
+               
+               elif key[0] != None: # No face image. if we havea material search for MTex image.
+                       for mtex in mat.getTextures():
+                               if mtex and mtex.tex.type == Texture.Types.IMAGE:
+                                       try:
+                                               filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1]
+                                               file.write('map_Kd %s\n' % filename) # Diffuse mapping image
+                                               break
+                                       except:
+                                               # Texture has no image though its an image type, best ignore.
+                                               pass
+               
+               file.write('\n\n')
+       
        file.close()
 
+
+
 def save_obj(filename):
+       global MTL_DICT
+       
        time1 = sys.time()
        scn = Scene.GetCurrent()
-       # First output all material
-       mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1])
-       save_mtl(mtlfilename)
 
        file = open(filename, "w")
        
        # Write Header
-       file.write('# Blender OBJ File: %s\n' % (Get('filename')))
+       file.write('# Blender OBJ File: %s\n' % (Get('filename').split('/')[-1].split('\\')[-1] ))
        file.write('# www.blender.org\n')
 
        # Tell the obj file what material file to use.
+       mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1])
        file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] ))
 
        # Initialize totals, these are updated each object
-       totverts = totuvco = 0
+       totverts = totuvco = totno = 1
        
        globalUVCoords = {}
+       globalNormals = {}
        
        # Get all meshs
        for ob in scn.getChildren():
-               if ob.getType() != 'Mesh':
+               #for ob in Object.GetSelected():
+               try:
+                       # Will work for non meshes now! :)
+                       m = NMesh.GetRawFromObject(ob.name)
+               except:
                        continue
-               m = NMesh.GetRawFromObject(ob.name)
+               
+               faces = [ f for f in m.faces if len(f) > 2 ]
+               
+               if not faces: # Make sure there is somthing to write
+                       continue # dont bother with this mesh.
+               
                m.transform(ob.matrix)
                
-               if not m.faces: # Make sure there is somthing to write
-                       continue #dont bother with this mesh.
+               # # Crash Blender
+               #materials = m.getMaterials(1) # 1 == will return None in the list.
+               materials = m.getMaterials()
                
-               faces = [ f for f in m.faces if len(f) > 2 ]
-               materials = m.materials
                
-               # Sort by Material so we dont over context switch in the obj file.
-               if len(materials) > 1:
-                       faces.sort(lambda a,b: cmp(a.mat, b.mat))
+               if materials:
+                       materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken.    
+               else:
+                       materialNames = []
+               
+               # Possible there null materials, will mess up indicies
+               # but at least it will export, wait until Blender gets fixed.
+               materialNames.extend((16-len(materialNames)) * [None])
                
-               # Set the default mat
-               currentMatName = NULL_MAT
-               currentImgName = NULL_IMG
                
-               file.write('o %s_%s\n' % (ob.getName(), m.name)) # Write Object name
+               # Sort by Material, then images
+               # so we dont over context switch in the obj file.
+               faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth)))
+               
+               
+               # Set the default mat to no material and no image.
+               contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get.
+               contextSmooth = None # Will either be true or false,  set bad to force initialization switch.
+               
+               file.write('o %s_%s\n' % (fixName(ob.name), fixName(m.name))) # Write Object name
                
                # Vert
                for v in m.verts:
                        file.write('v %.6f %.6f %.6f\n' % tuple(v.co))
-       
+               
                # UV
                if m.hasFaceUV():
                        for f in faces:
-                               for uv in f.uv:
-                                       uvKey = '%.6f %.6f' % uv
-                                       try:
-                                               dummy = globalUVCoords[uvKey]
-                                       except KeyError:
-                                               totuvco +=1 # 1 based index.
+                               for uvKey in f.uv:
+                                       if not globalUVCoords.has_key(uvKey):
                                                globalUVCoords[uvKey] = totuvco
-                                               file.write('vt %s 0.0\n' % uvKey)
-                                               
-               # NORMAL
-               for v in m.verts:
-                       file.write('vn %.6f %.6f %.6f\n' % tuple(v.no))
+                                               totuvco +=1
+                                               file.write('vt %.6f %.6f 0.0\n' % uvKey)
+               
+               # NORMAL, Smooth/Non smoothed.
+               
+               for f in faces:
+                       if f.smooth:
+                               for v in f.v:
+                                       noKey = tuple(v.no)
+                                       if not globalNormals.has_key( noKey ):
+                                               globalNormals[noKey] = totno
+                                               totno +=1
+                                               file.write('vn %.6f %.6f %.6f\n' % noKey)
+                       else:
+                               # Hard, 1 normal from the face.
+                               noKey = tuple(f.no)
+                               if not globalNormals.has_key( noKey ):
+                                       globalNormals[noKey] = totno
+                                       totno +=1
+                                       file.write('vn %.6f %.6f %.6f\n' % noKey)
+               
                
                uvIdx = 0
                for f in faces:
-                       # Check material and change if needed.
-                       if len(materials) > 0:
-                               if currentMatName != materials[f.mat].getName():
-                                       currentMatName = materials[f.mat].getName()
-                                       file.write('usemtl %s\n' % (currentMatName))
                        
-                       elif currentMatName != NULL_MAT:
-                               currentMatName = NULL_MAT
-                               file.write('usemtl %s\n' % (currentMatName))
-               
-                       # UV IMAGE
-                       # If the face uses a different image from the one last set then add a usemap line.
-                       if f.image:
-                               if f.image.filename != currentImgName:
-                                       currentImgName = f.image.filename
-                                       # Set a new image for all following faces
-                                       file.write( 'usemap %s\n' % currentImgName.split('\\')[-1].split('/')[-1] )
+                       # MAKE KEY
+                       if f.image: # Object is always true.
+                               key = materialNames[f.mat],  f.image.name
+                       else:
+                               key = materialNames[f.mat],  None # No image, use None instead.
+                       
+                       # CHECK FOR CONTEXT SWITCH
+                       if key == contextMat:
+                               pass # Context alredy switched, dont do anythoing
+                       elif key[0] == None and key[1] == None:
+                               # Write a null material, since we know the context has changed.
+                               file.write('usemtl (null)\n') # mat, image
                                
-                       elif currentImgName != NULL_IMG: # Not using an image so set to NULL_IMG
-                               currentImgName = NULL_IMG
-                               # Set a ne w image for all following faces
-                               file.write( 'usemap %s\n' % currentImgName) # No splitting needed.
+                       else:
+                               try: # Faster to try then 2x dict lookups.
+                                       
+                                       # We have the material, just need to write the context switch,
+                                       file.write('usemtl %s\n' % MTL_DICT[key]) # mat, image
+                                       
+                               except KeyError:
+                                       # First add to global dict so we can export to mtl
+                                       # Then write mtl
+                                       
+                                       # Make a new names from the mat and image name,
+                                       # converting any spaces to underscores with fixName.
+                                       
+                                       # If none image dont bother adding it to the name
+                                       if key[1] == None:
+                                               tmp_matname = MTL_DICT[key] ='%s' % fixName(key[0])
+                                               file.write('usemtl %s\n' % tmp_matname) # mat, image
+                                               
+                                       else:
+                                               tmp_matname = MTL_DICT[key] = '%s_%s' % (fixName(key[0]), fixName(key[1]))
+                                               file.write('usemtl %s\n' % tmp_matname) # mat, image
+                               
+                       contextMat = key
+                       
+                       if f.smooth != contextSmooth:
+                               if f.smooth:
+                                       file.write('s 1\n')
+                               else:
+                                       file.write('s off\n')
+                               contextSmooth = f.smooth
                        
                        file.write('f')
                        if m.hasFaceUV():
-                               for vi, v in enumerate(f.v):
-                                       uvIdx = globalUVCoords[ '%.6f %.6f' % f.uv[vi] ]
-                                       i = v.index + totverts + 1
-                                       file.write( ' %d/%d/%d' % (i, uvIdx, i)) # vert, uv, normal
-                                       
+                               if f.smooth: # Smoothed, use vertex normals
+                                       for vi, v in enumerate(f.v):
+                                               file.write( ' %d/%d/%d' % (\
+                                                 v.index+totverts,\
+                                                 globalUVCoords[ f.uv[vi] ],\
+                                                 globalNormals[ tuple(v.no) ])) # vert, uv, normal
+                               else: # No smoothing, face normals
+                                       no = globalNormals[ tuple(f.no) ]
+                                       for vi, v in enumerate(f.v):
+                                               file.write( ' %d/%d/%d' % (\
+                                                 v.index+totverts,\
+                                                 globalUVCoords[ f.uv[vi] ],\
+                                                 no)) # vert, uv, normal
+                       
                        else: # No UV's
-                               for v in f.v:
-                                       file.write( ' %d' % (v.index + totverts+1))
+                               if f.smooth: # Smoothed, use vertex normals
+                                       for v in f.v:
+                                               file.write( ' %d//%d' % (\
+                                                 v.index+totverts,\
+                                                 globalNormals[ tuple(v.no) ]))
+                               else: # No smoothing, face normals
+                                       no = globalNormals[ tuple(f.no) ]
+                                       for v in f.v:
+                                               file.write( ' %d//%d' % (\
+                                                 v.index+totverts,\
+                                                 no))
+                                       
                        file.write('\n')
                
                # Make the indicies global rather then per mesh
                totverts += len(m.verts)
        file.close()
+       
+       
+       # Now we have all our materials, save them
+       save_mtl(mtlfilename)
+       
        print "obj export time: %.2f" % (sys.time() - time1)
 
 Window.FileSelector(save_obj, 'Export Wavefront OBJ', newFName('obj'))
+
+'''
+TIME = sys.time()
+import os
+OBJDIR = '/obj_out/'
+for scn in Scene.Get():
+       scn.makeCurrent()
+       obj = OBJDIR + scn.name
+       print obj
+       save_obj(obj)
+
+print "TOTAL EXPORT TIME: ", sys.time() - TIME
+'''
index 268bf44f3530d18e611eb51eaf65bfe866b34c19..d8788f6d04239c9e12df9cc0fae6de759957d2ea 100644 (file)
@@ -4,7 +4,7 @@
 Name: 'Wavefront (.obj)...'
 Blender: 237
 Group: 'Import'
-Tooltip: 'Load a Wavefront OBJ File'
+Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
 """
 
 __author__ = "Campbell Barton"
@@ -43,14 +43,6 @@ Run this script from "File->Import" menu and then load the desired OBJ file.
 # ***** END GPL LICENCE BLOCK *****
 # --------------------------------------------------------------------------
 
-ABORT_MENU = 'Failed Reading OBJ%t|File is probably another type|if not send this file to|cbarton@metavr.com|with MTL and image files for further testing.'
-
-NULL_MAT = '(null)' # Name for mesh's that have no mat set.
-NULL_IMG = '(null)' # Name for mesh's that have no mat set.
-
-MATLIMIT = 16 # This isnt about to change but probably should not be hard coded.
-
-DIR = ''
 
 #==============================================#
 # Return directory, where the file is          #
@@ -71,52 +63,243 @@ def stripPath(path):
 # Strips the prefix off the name before writing      #
 #====================================================#
 def stripExt(name): # name is a string
-       return name[ : name.rfind('.') ]
+       index = name.rfind('.')
+       if index != -1:
+               return name[ : index ]
+       else:
+               return name
 
 
 from Blender import *
 
 
-#==================================================================================#
-# This function sets textures defined in .mtl file                                 #
-#==================================================================================#
-def getImg(img_fileName, dir):
-       img_fileName_strip = stripPath(img_fileName)
-       for i in Image.Get():
-               if stripPath(i.filename) == img_fileName_strip:
-                       return i
-       
-       try: # Absolute dir
-               return Image.Load(img_fileName)
-       except IOError:
-               pass
-       
-       # Relative dir
-       if img_fileName.startswith('/'):
-               img_fileName = img_fileName[1:]
-       elif img_fileName.startswith('./'):
-               img_fileName = img_fileName[2:]
-       elif img_fileName.startswith('\\'):
-               img_fileName = img_fileName[1:]         
-       elif img_fileName.startswith('.\\'):
-               img_fileName = img_fileName[2:]         
-       
-       # if we are this far it means the image hasnt been loaded.
-       try:
-               return Image.Load( dir + img_fileName)
-       except IOError:
-               pass
+
+# Adds a slash to the end of a path if its not there.
+def addSlash(path):
+       if path.endswith('\\') or path.endswith('/'):
+               return path
+       return path + sys.sep
+       
+
+def getExt(name):
+       index = name.rfind('.')
+       if index != -1:
+               return name[index+1:]
+       return name
+
+try:
+       import os
+except:
+       # So we know if os exists.
+       print 'Module "os" not found, install python to enable comprehensive image finding and batch loading.'
+       os = None
+
+#===========================================================================#
+# Comprehansive image loader, will search and find the image                #
+# Will return a blender image or none if the image is missing               #
+#===========================================================================#
+def comprehansiveImageLoad(imagePath, filePath):
+       
+       # When we have the file load it with this. try/except niceness.
+       def imageLoad(path):
+               try:
+                       img = Image.Load(path)
+                       print '\t\tImage loaded "%s"' % path
+                       return img
+               except:
+                       print '\t\tImage failed loading "%s", mabe its not a format blender can read.' % (path)
+                       return None
+       
+       # Image formats blender can read
+       IMAGE_EXT = ['jpg', 'jpeg', 'png', 'tga', 'bmp', 'rgb', 'sgi', 'bw', 'iff', 'lbm', # Blender Internal
+       'gif', 'psd', 'tif', 'tiff', 'pct', 'pict', 'pntg', 'qtif'] # Quacktime, worth a try.
+       
        
-       # Its unlikely but the image might be with the OBJ file, and the path provided not relevent.
-       # if the user extracted an archive with no paths this could happen.
-       try:
-               return Image.Load( dir + img_fileName_strip)
-       except IOError:
-               pass
        
-       print '\tunable to open image file: "%s"' % img_fileName
+       
+       print '\tAttempting to load "%s"' % imagePath
+       if sys.exists(imagePath):
+               print '\t\tFile found where expected.'
+               return imageLoad(imagePath)
+       
+       imageFileName =  stripPath(imagePath) # image path only
+       imageFileName_lower =  imageFileName.lower() # image path only
+       imageFileName_noext = stripExt(imageFileName) # With no extension.
+       imageFileName_noext_lower = stripExt(imageFileName_lower) # With no extension.
+       imageFilePath = stripFile(imagePath)
+       
+       # Remove relative path from image path
+       if imageFilePath.startswith('./') or imageFilePath.startswith('.\\'):
+               imageFilePath = imageFilePath[2:]
+       
+       
+       # Attempt to load from obj path.
+       tmpPath = stripFile(filePath) + stripFile(imageFilePath)
+       if sys.exists(tmpPath):
+               print '\t\tFile found in obj dir.'
+               return imageLoad(imagePath)
+       
+       # OS NEEDED IF WE GO ANY FURTHER.
+       if not os:
+               return
+       
+       
+       # We have os.
+       # GATHER PATHS.
+       paths = {} # Store possible paths we may use, dict for no doubles.
+       tmpPath = addSlash(sys.expandpath('//')) # Blenders path
+       if sys.exists(tmpPath):
+               print '\t\tSearching in %s' % tmpPath
+               paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
+               paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
+               paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
+               
+       tmpPath = imageFilePath
+       if sys.exists(tmpPath):
+               print '\t\tSearching in %s' % tmpPath
+               paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
+               paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
+               paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
+
+       tmpPath = stripFile(filePath)
+       if sys.exists(tmpPath):
+               print '\t\tSearching in %s' % tmpPath
+               paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
+               paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
+               paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
+       
+       tmpPath = addSlash(Get('texturesdir'))
+       if tmpPath and sys.exists(tmpPath):
+               print '\t\tSearching in %s' % tmpPath
+               paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
+               paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
+               paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
+       
+       # Add path if relative image patrh was given.
+       for k in paths.iterkeys():
+               tmpPath = k + imageFilePath
+               if sys.exists(tmpPath):
+                       paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading 
+                       paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
+                       paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
+       # DONE
+       
+       
+       # 
+       for path, files in paths.iteritems():
+               
+               if sys.exists(path + imageFileName):
+                       return imageLoad(path + imageFileName)
+               
+               # If the files not there then well do a case insensitive seek.
+               filesOrigCase = files[0]
+               filesLower = files[1]
+               filesLowerNoExt = files[2]
+               
+               # We are going to try in index the file directly, if its not there just keep on
+               index = None
+               try:
+                       # Is it just a case mismatch?
+                       index = filesLower.index(imageFileName_lower)
+               except:
+                       try:
+                               # Have the extensions changed?
+                               index = filesLowerNoExt.index(imageFileName_noext_lower)
+                               
+                               ext = getExt( filesLower[index] ) # Get the extension of the file that matches all but ext.
+                               
+                               # Check that the ext is useable eg- not a 3ds file :)
+                               if ext.lower() not in IMAGE_EXT:
+                                       index = None
+                       
+                       except:
+                               index = None
+               
+               if index != None:
+                       tmpPath = path + filesOrigCase[index]
+                       img = imageLoad( tmpPath )
+                       if img != None:
+                               print '\t\tImage Found "%s"' % tmpPath
+                               return img
+       
+       
+       # IMAGE NOT FOUND IN ANY OF THE DIRS!, DO A RECURSIVE SEARCH.
+       print '\t\tImage Not Found in any of the dirs, doing a recusrive search'
+       for path in paths.iterkeys():
+               # Were not going to use files
+               
+               
+               #------------------
+               # finds the file starting at the root.
+               #       def findImage(findRoot, imagePath):
+               #W---------------
+               
+               # ROOT, DIRS, FILES
+               pathWalk = os.walk(path)
+               pathList = [True]
+               
+               matchList = [] # Store a list of (match, size), choose the biggest.
+               while True:
+                       try:
+                               pathList  = pathWalk.next()
+                       except:
+                               break
+                       
+                       for file in pathList[2]:
+                               file_lower = file.lower()
+                               # FOUND A MATCH
+                               if (file_lower == imageFileName_lower) or\
+                               (stripExt(file_lower) == imageFileName_noext_lower and getExt(file_lower) in IMAGE_EXT):
+                                       name = pathList[0] + sys.sep + file
+                                       size = os.path.getsize(name)
+                                       print '\t\t\tfound:', name 
+                                       matchList.append( (name, size) )
+               
+               if matchList:
+                       # Sort by file size
+                       matchList.sort(lambda A, B: cmp(B[1], A[1]) )
+                       
+                       print '\t\tFound "%s"' % matchList[0][0]
+                       
+                       # Loop through all we have found
+                       img = None
+                       for match in matchList:
+                               img = imageLoad(match[0]) # 0 - first, 0 - pathname
+                               if img != None:
+                                       break
+                       return img
+       
+       
+       
+       # No go.
+       print '\t\tImage Not Found "%s"' % imagePath
        return None
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#==================================================================================#
+# This function sets textures defined in .mtl file                                 #
+#==================================================================================#
+# ___ Replaced by comprehensive imahge get
+
 #==================================================================================#
 # This function sets textures defined in .mtl file                                 #
 #==================================================================================#
@@ -127,7 +310,7 @@ def loadMaterialImage(mat, img_fileName, type, meshDict, dir):
        texture.setType('Image')
        
        # Absolute path - c:\.. etc would work here
-       image = getImg(img_fileName, dir)
+       image = comprehansiveImageLoad(img_fileName, dir)
        
        if image:
                texture.image = image
@@ -135,7 +318,7 @@ def loadMaterialImage(mat, img_fileName, type, meshDict, dir):
        # adds textures to faces (Textured/Alt-Z mode)
        # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
        if image and type == 'Kd':
-               for meshPair in meshDict.values():
+               for meshPair in meshDict.itervalues():
                        for f in meshPair[0].faces:
                                #print meshPair[0].materials[f.mat].name, mat.name
                                if meshPair[0].materials[f.mat].name == mat.name:
@@ -201,11 +384,11 @@ def load_mtl(dir, mtl_file, meshDict, materialDict):
                        elif l[0] == 'newmtl':
                                currentMat = getMat('_'.join(l[1:]), materialDict) # Material should alredy exist.
                        elif l[0] == 'Ka':
-                               currentMat.setMirCol(float(l[1]), float(l[2]), float(l[3]))
+                               currentMat.setMirCol((float(l[1]), float(l[2]), float(l[3])))
                        elif l[0] == 'Kd':
-                               currentMat.setRGBCol(float(l[1]), float(l[2]), float(l[3]))
+                               currentMat.setRGBCol((float(l[1]), float(l[2]), float(l[3])))
                        elif l[0] == 'Ks':
-                               currentMat.setSpecCol(float(l[1]), float(l[2]), float(l[3]))
+                               currentMat.setSpecCol((float(l[1]), float(l[2]), float(l[3])))
                        elif l[0] == 'Ns':
                                currentMat.setHardness( int((float(l[1])*0.51)) )
                        elif l[0] == 'Ni': # Refraction index
@@ -238,7 +421,9 @@ def load_mtl(dir, mtl_file, meshDict, materialDict):
                        
                        lIdx+=1
        except:
-               print '\tERROR: Unable to parse MTL file.'
+               print '\tERROR: Unable to parse MTL file: "%s"' % mtl_file
+               return
+       print '\tUsing MTL: "%s"' % mtl_file
 #===========================================================================#
 # Returns unique name of object/mesh (preserve overwriting existing meshes) #
 #===========================================================================#
@@ -262,6 +447,9 @@ def getUniqueName(name):
 # This loads data from .obj file                                                   #
 #==================================================================================#
 def load_obj(file):
+       
+       print '\nImporting OBJ file: "%s"' % file
+       
        time1 = sys.time()
        
        # Deselect all objects in the scene.
@@ -274,7 +462,7 @@ def load_obj(file):
        # Get the file name with no path or .obj
        fileName = stripExt( stripPath(file) )
 
-       mtl_fileName = None
+       mtl_fileName = [] # Support multiple mtl files if needed.
 
        DIR = stripFile(file)
        
@@ -282,11 +470,11 @@ def load_obj(file):
        fileLines = tempFile.readlines()
        tempFile.close()
        
-       uvMapList = [(0,0)] # store tuple uv pairs here
-
+       uvMapList = [] # store tuple uv pairs here
+       
        # This dummy vert makes life a whole lot easier-
        # pythons index system then aligns with objs, remove later
-       vertList = [None] # Could havea vert but since this is a placeholder theres no Point
+       vertList = [] # Could havea vert but since this is a placeholder theres no Point
        
        
        # Store all imported images in a dict, names are key
@@ -297,7 +485,7 @@ def load_obj(file):
        contextMeshMatIdx = -1
        
        # Keep this out of the dict for easy accsess.
-       nullMat = Material.New(NULL_MAT)
+       nullMat = Material.New('(null)')
        
        currentMat = nullMat # Use this mat.
        currentImg = None # Null image is a string, otherwise this should be set to an image object.\
@@ -320,57 +508,54 @@ def load_obj(file):
        materialDict = {} # Store all imported materials as unique dict, names are key
        lIdx = 0
        print '\tfile length: %d' % len(fileLines)
-       try:
-               while lIdx < len(fileLines):
-                       # Ignore vert normals
-                       if fileLines[lIdx].startswith('vn'):
-                               lIdx+=1
-                               continue
+       
+       while lIdx < len(fileLines):
+               # Ignore vert normals
+               if fileLines[lIdx].startswith('vn'):
+                       lIdx+=1
+                       continue
+               
+               # Dont Bother splitting empty or comment lines.
+               if len(fileLines[lIdx]) == 0 or\
+               fileLines[lIdx][0] == '\n' or\
+               fileLines[lIdx][0] == '#':
+                       pass
+               
+               else:
+                       fileLines[lIdx] = fileLines[lIdx].split()               
+                       l = fileLines[lIdx]
                        
-                       # Dont Bother splitting empty or comment lines.
-                       if len(fileLines[lIdx]) == 0 or\
-                       fileLines[lIdx][0] == '\n' or\
-                       fileLines[lIdx][0] == '#':
+                       # Splitting may 
+                       if len(l) == 0:
                                pass
+                       # Verts
+                       elif l[0] == 'v':
+                               vertList.append( NMesh.Vert(float(l[1]), float(l[2]), float(l[3]) ) )
+                       
+                       # UV COORDINATE
+                       elif l[0] == 'vt':
+                               uvMapList.append( (float(l[1]), float(l[2])) )
+                       
+                       # Smoothing groups, make a list of unique.
+                       elif l[0] == 's':
+                               if len(l) > 1:
+                                       smoothingGroups['_'.join(l[1:])] = None # Can we assign something more usefull? cant use sets yet
+                                               
+                               # Keep Smoothing group line
+                               nonVertFileLines.append(l)
+                       
+                       # Smoothing groups, make a list of unique.
+                       elif l[0] == 'usemtl':
+                               if len(l) > 1:
+                                       materialDict['_'.join(l[1:])] = None # Can we assign something more usefull? cant use sets yet
+                                               
+                               # Keep Smoothing group line
+                               nonVertFileLines.append(l)
                        
                        else:
-                               fileLines[lIdx] = fileLines[lIdx].split()               
-                               l = fileLines[lIdx]
-                               
-                               # Splitting may 
-                               if len(l) == 0:
-                                       pass
-                               # Verts
-                               elif l[0] == 'v':
-                                       vertList.append( NMesh.Vert(float(l[1]), float(l[2]), float(l[3]) ) )
-                               
-                               # UV COORDINATE
-                               elif l[0] == 'vt':
-                                       uvMapList.append( (float(l[1]), float(l[2])) )
-                               
-                               # Smoothing groups, make a list of unique.
-                               elif l[0] == 's':
-                                       if len(l) > 1:
-                                               smoothingGroups['_'.join(l[1:])] = None # Can we assign something more usefull? cant use sets yet
-                                                       
-                                       # Keep Smoothing group line
-                                       nonVertFileLines.append(l)
-                               
-                               # Smoothing groups, make a list of unique.
-                               elif l[0] == 'usemtl':
-                                       if len(l) > 1:
-                                               materialDict['_'.join(l[1:])] = None # Can we assign something more usefull? cant use sets yet
-                                                       
-                                       # Keep Smoothing group line
-                                       nonVertFileLines.append(l)
-                               
-                               else:
-                                       nonVertFileLines.append(l)
-                       lIdx+=1
-               
-       except:
-               print Draw.PupMenu(ABORT_MENU)
-               return
+                               nonVertFileLines.append(l)
+               lIdx+=1
+       
        
        del fileLines
        fileLines = nonVertFileLines
@@ -382,7 +567,7 @@ def load_obj(file):
        print '\tfound %d smoothing groups.' % (len(smoothingGroups) -1)
        
        # Add materials to Blender for later is in teh OBJ
-       for k in materialDict.keys():
+       for k in materialDict.iterkeys():
                materialDict[k] = Material.New(k)
        
        
@@ -419,113 +604,106 @@ def load_obj(file):
        # 0:NMesh, 1:SmoothGroups[UsedVerts[0,0,0,0]], 2:materialMapping['matname':matIndexForThisNMesh]
        meshDict[currentObjectName] = (currentMesh, currentUsedVertList, currentMaterialMeshMapping) 
        
-
-       
-               
+       # Only show the bad uv error once 
+       badObjUvs = 0
+       badObjFaceVerts = 0
+       badObjFaceTexCo = 0
        
        
-       currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indicies where 1 is the first item.
+       #currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indicies where 1 is the first item.
        if len(uvMapList) > 1:
                currentMesh.hasFaceUV(1) # Turn UV's on if we have ANY texture coords in this obj file.
        
-
+       
        #==================================================================================#
        # Load all faces into objects, main loop                                           #
        #==================================================================================#
-       try:
-               lIdx = 0
-               # Face and Object loading LOOP
-               while lIdx < len(fileLines):
-                       l = fileLines[lIdx]
+       lIdx = 0
+       # Face and Object loading LOOP
+       while lIdx < len(fileLines):
+               l = fileLines[lIdx]
+               
+               # FACE
+               if l[0] == 'f': 
+                       # Make a face with the correct material.
                        
-                       # FACE
-                       if l[0] == 'f': 
-                               # Make a face with the correct material.
+                       # Add material to mesh
+                       if contextMeshMatIdx == -1:
+                               tmpMatLs = currentMesh.materials
                                
-                               # Add material to mesh
-                               if contextMeshMatIdx == -1:
-                                       tmpMatLs = currentMesh.materials
-                                       
-                                       if len(tmpMatLs) == MATLIMIT:
-                                               contextMeshMatIdx = 0 # Use first material
-                                               print 'material overflow, attempting to use > 16 materials. defaulting to first.'
-                                       else:
-                                               contextMeshMatIdx = len(tmpMatLs)
-                                               currentMaterialMeshMapping[currentMat.name] = contextMeshMatIdx
-                                               currentMesh.addMaterial(currentMat)
+                               if len(tmpMatLs) == 16:
+                                       contextMeshMatIdx = 0 # Use first material
+                                       print 'material overflow, attempting to use > 16 materials. defaulting to first.'
+                               else:
+                                       contextMeshMatIdx = len(tmpMatLs)
+                                       currentMaterialMeshMapping[currentMat.name] = contextMeshMatIdx
+                                       currentMesh.addMaterial(currentMat)
+                       
+                       # Set up vIdxLs : Verts
+                       # Set up vtIdxLs : UV
+                       # Start with a dummy objects so python accepts OBJs 1 is the first index.
+                       vIdxLs = []
+                       vtIdxLs = []
+                       
+                       
+                       fHasUV = len(uvMapList) # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
+                       for v in l[1:]:
+                               # OBJ files can have // or / to seperate vert/texVert/normal
+                               # this is a bit of a pain but we must deal with it.
+                               objVert = v.split('/')
                                
-                               # Set up vIdxLs : Verts
-                               # Set up vtIdxLs : UV
-                               # Start with a dummy objects so python accepts OBJs 1 is the first index.
-                               vIdxLs = []
-                               vtIdxLs = []
-                               fHasUV = len(uvMapList)-1 # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
-                               for v in l[1:]:
-                                       # OBJ files can have // or / to seperate vert/texVert/normal
-                                       # this is a bit of a pain but we must deal with it.
-                                       objVert = v.split('/')
-                                       
-                                       # Vert Index - OBJ supports negative index assignment (like python)
+                               # Vert Index - OBJ supports negative index assignment (like python)
+                               
+                               vIdxLs.append(int(objVert[0])-1)
+                               if fHasUV:
+                                       # UV
+                                       index = 0 # Dummy var
+                                       if len(objVert) == 1:
+                                               index = vIdxLs[-1]
+                                       elif objVert[1]: # != '' # Its possible that theres no texture vert just he vert and normal eg 1//2
+                                               index = int(objVert[1])-1
                                        
-                                       vIdxLs.append(int(objVert[0]))
-                                       if fHasUV:
-                                               # UV
-                                               if len(objVert) == 1:
-                                                       #vtIdxLs.append(int(objVert[0])) # replace with below.
-                                                       vtIdxLs.append(vIdxLs[-1]) # Sticky UV coords
-                                               elif objVert[1]: # != '' # Its possible that theres no texture vert just he vert and normal eg 1//2
-                                                       vtIdxLs.append(int(objVert[1])) # Seperate UV coords
-                                               else:
-                                                       fHasUV = 0
+                                       if len(uvMapList) > index:
+                                               vtIdxLs.append(index) # Seperate UV coords
+                                       else:
+                                               # BAD FILE, I have found this so I account for it.
+                                               # INVALID UV COORD
+                                               # Could ignore this- only happens with 1 in 1000 files.
+                                               badObjFaceTexCo +=1
+                                               vtIdxLs.append(0)
+                                               
+                                               fHasUV = 0
        
-                                               # Dont add a UV to the face if its larger then the UV coord list
-                                               # The OBJ file would have to be corrupt or badly written for thi to happen
-                                               # but account for it anyway.
-                                               if len(vtIdxLs) > 0:
-                                                       if vtIdxLs[-1] > len(uvMapList):
-                                                               fHasUV = 0
-                                                               print 'badly written OBJ file, invalid references to UV Texture coordinates.'
+                                       # Dont add a UV to the face if its larger then the UV coord list
+                                       # The OBJ file would have to be corrupt or badly written for thi to happen
+                                       # but account for it anyway.
+                                       if len(vtIdxLs) > 0:
+                                               if vtIdxLs[-1] > len(uvMapList):
+                                                       fHasUV = 0
+                                                       
+                                                       badObjUvs +=1 # ERROR, Cont
+                       # Quads only, we could import quads using the method below but it polite to import a quad as a quad.
+                       if len(vIdxLs) == 4:
                                
-                               # Quads only, we could import quads using the method below but it polite to import a quad as a quad.
-                               if len(vIdxLs) == 4:
-                                       '''
-                                       f = NMesh.Face()
+                               # Have found some files where wach face references the same vert
+                               # - This causes a bug and stopts the import so lets check here
+                               if vIdxLs[0] == vIdxLs[1] or\
+                               vIdxLs[0] == vIdxLs[2] or\
+                               vIdxLs[0] == vIdxLs[3] or\
+                               vIdxLs[1] == vIdxLs[2] or\
+                               vIdxLs[1] == vIdxLs[3] or\
+                               vIdxLs[2] == vIdxLs[3]:
+                                       badObjFaceVerts+=1
+                               else:
                                        for i in quadList: #  quadList == [0,1,2,3] 
                                                if currentUsedVertListSmoothGroup[vIdxLs[i]] == 0:
-                                                       v = vertList[vIdxLs[i]]
-                                                       currentMesh.verts.append(v)
-                                                       f.append(v)
+                                                       faceQuadVList[i] = vertList[vIdxLs[i]]
+                                                       currentMesh.verts.append(faceQuadVList[i])
                                                        currentUsedVertListSmoothGroup[vIdxLs[i]] = len(currentMesh.verts)-1
                                                else:
-                                                       f.v.append(currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]])
-                                       '''
-                                       if currentUsedVertListSmoothGroup[vIdxLs[0]] == 0:
-                                               faceQuadVList[0] = vertList[vIdxLs[0]]
-                                               currentUsedVertListSmoothGroup[vIdxLs[0]] = len(currentMesh.verts)
-                                       else:
-                                               faceQuadVList[0] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[0]]]
+                                                       faceQuadVList[i] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
                                        
-                                       if currentUsedVertListSmoothGroup[vIdxLs[1]] == 0:
-                                               faceQuadVList[1] = vertList[vIdxLs[1]]
-                                               currentUsedVertListSmoothGroup[vIdxLs[1]] = len(currentMesh.verts)+1
-                                       else:
-                                               faceQuadVList[1] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[1]]]
-                                               
-                                       if currentUsedVertListSmoothGroup[vIdxLs[2]] == 0:
-                                               faceQuadVList[2] = vertList[vIdxLs[2]]
-                                               currentUsedVertListSmoothGroup[vIdxLs[2]] = len(currentMesh.verts)+2
-                                       else:
-                                               faceQuadVList[2] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[2]]]
-       
-                                       if currentUsedVertListSmoothGroup[vIdxLs[3]] == 0:
-                                               faceQuadVList[3] = vertList[vIdxLs[3]]
-                                               currentUsedVertListSmoothGroup[vIdxLs[3]] = len(currentMesh.verts)+3
-                                       else:
-                                               faceQuadVList[3] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[3]]]
-                                       
-                                       currentMesh.verts.extend(faceQuadVList)
                                        f = NMesh.Face(faceQuadVList)
-                                       
                                        # UV MAPPING
                                        if fHasUV:
                                                f.uv = [uvMapList[ vtIdxLs[0] ],uvMapList[ vtIdxLs[1] ],uvMapList[ vtIdxLs[2] ],uvMapList[ vtIdxLs[3] ]]
@@ -537,43 +715,23 @@ def load_obj(file):
                                        f.mat = contextMeshMatIdx
                                        f.smooth = currentSmooth
                                        currentMesh.faces.append(f) # move the face onto the mesh
-                               
-                               elif len(vIdxLs) >= 3: # This handles tri's and fans
-                                       for i in range(len(vIdxLs)-2):
-                                               '''
-                                               f = NMesh.Face()
-                                               for ii in [0, i+1, i+2]:
-                                                       if currentUsedVertListSmoothGroup[vIdxLs[ii]] == 0:
-                                                               v = vertList[vIdxLs[ii]]
-                                                               currentMesh.verts.append(v)
-                                                               f.append(v)
-                                                               currentUsedVertListSmoothGroup[vIdxLs[ii]] = len(currentMesh.verts)-1
+                       
+                       elif len(vIdxLs) >= 3: # This handles tri's and fans
+                               for i in range(len(vIdxLs)-2):
+                                       if vIdxLs[0] == vIdxLs[i+1] or\
+                                       vIdxLs[0] == vIdxLs[i+2] or\
+                                       vIdxLs[i+1] == vIdxLs[i+2]:
+                                               badObjFaceVerts+=1
+                                       else:
+                                               for k, j in [(0,0), (1,i+1), (2,i+2)]:
+                                                       if currentUsedVertListSmoothGroup[vIdxLs[j]] == 0:
+                                                               faceTriVList[k] = vertList[vIdxLs[j]]
+                                                               currentMesh.verts.append(faceTriVList[k])
+                                                               currentUsedVertListSmoothGroup[vIdxLs[j]] = len(currentMesh.verts)-1
                                                        else:
-                                                               f.v.append(currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[ii]]])
-                                               '''
-                                               
-                                                       
-                                               if currentUsedVertListSmoothGroup[vIdxLs[0]] == 0:
-                                                       faceTriVList[0] = vertList[vIdxLs[0]]
-                                                       currentUsedVertListSmoothGroup[vIdxLs[0]] = len(currentMesh.verts)
-                                               else:
-                                                       faceTriVList[0] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[0]]]
-                                               
-                                               if currentUsedVertListSmoothGroup[vIdxLs[i+1]] == 0:
-                                                       faceTriVList[1] = vertList[vIdxLs[i+1]]
-                                                       currentUsedVertListSmoothGroup[vIdxLs[i+1]] = len(currentMesh.verts)+1
-                                               else:
-                                                       faceTriVList[1] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i+1]]]
-                                                       
-                                               if currentUsedVertListSmoothGroup[vIdxLs[i+2]] == 0:
-                                                       faceTriVList[2] = vertList[vIdxLs[i+2]]
-                                                       currentUsedVertListSmoothGroup[vIdxLs[i+2]] = len(currentMesh.verts)+2
-                                               else:
-                                                       faceTriVList[2] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i+2]]]
-                                               
-                                               currentMesh.verts.extend(faceTriVList)
-                                               f = NMesh.Face(faceTriVList)                                    
+                                                               faceTriVList[k] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[j]]]  
                                                
+                                               f = NMesh.Face(faceTriVList)    
                                                
                                                # UV MAPPING
                                                if fHasUV:
@@ -586,177 +744,195 @@ def load_obj(file):
                                                f.mat = contextMeshMatIdx
                                                f.smooth = currentSmooth
                                                currentMesh.faces.append(f) # move the face onto the mesh
-                       
-                       # FACE SMOOTHING
-                       elif l[0] == 's':
-                               # No value? then turn on.
-                               if len(l) == 1:
-                                       currentSmooth = True
+               
+               # FACE SMOOTHING
+               elif l[0] == 's':
+                       # No value? then turn on.
+                       if len(l) == 1:
+                               currentSmooth = True
+                               currentSmoothGroup = '(null)'
+                               try:
+                                       currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
+                               except KeyError:
+                                       currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
+                                       currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup
+                                       
+                       else:
+                               if l[1] == 'off':
+                                       currentSmooth = False
                                        currentSmoothGroup = '(null)'
-                                       try:
-                                               currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
-                                       except KeyError:
-                                               currentUsedVertListSmoothGroup = VERT_USED_LIST[:]
-                                               currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup
-                                               
-                               else:
-                                       if l[1] == 'off':
-                                               currentSmooth = False
-                                               currentSmoothGroup = '(null)'
-                                               # We all have a null group so dont need to try
-                                               currentUsedVertListSmoothGroup = currentUsedVertList['(null)']
-                                       else: 
-                                               currentSmooth = True
-                                               currentSmoothGroup = '_'.join(l[1:])
-       
-                       # OBJECT / GROUP
-                       elif l[0] == 'o' or l[0] == 'g':
+                                       # We all have a null group so dont need to try
+                                       currentUsedVertListSmoothGroup = currentUsedVertList['(null)']
+                               else: 
+                                       currentSmooth = True
+                                       currentSmoothGroup = '_'.join(l[1:])
+       
+               # OBJECT / GROUP
+               elif l[0] == 'o' or l[0] == 'g':
+                       
+                       # Forget about the current image
+                       currentImg = None
+                       
+                       # This makes sure that if an object and a group have the same name then
+                       # they are not put into the same object.
+                       
+                       # Only make a new group.object name if the verts in the existing object have been used, this is obscure
+                       # but some files face groups seperating verts and faces which results in silly things. (no groups have names.)
+                       if len(l) > 1:
+                               currentObjectName = '_'.join(l[1:])
+                       else: # No name given
+                               # Make a new empty name
+                               if l[0] == 'g': # Make a blank group name
+                                       currentObjectName = 'unnamed_grp_%d' % currentUnnamedGroupIdx
+                                       currentUnnamedGroupIdx +=1
+                               else: # is an object.
+                                       currentObjectName = 'unnamed_ob_%d' % currentUnnamedObjectIdx
+                                       currentUnnamedObjectIdx +=1
+                       
+                       
+                       # If we havnt written to this mesh before then do so.
+                       # if we have then we'll just keep appending to it, this is required for soem files.
+                       
+                       # If we are new, or we are not yet in the list of added meshes
+                       # then make us new mesh.
+                       if len(l) == 1 or (not meshDict.has_key(currentObjectName)):
+                               currentMesh = NMesh.GetRaw()
                                
-                               # Forget about the current image
-                               currentImg = None
+                               currentUsedVertList = {}
                                
-                               # This makes sure that if an object and a group have the same name then
-                               # they are not put into the same object.
+                               # Sg is a string
+                               currentSmoothGroup = '(null)'
+                               currentUsedVertListSmoothGroup = VERT_USED_LIST[:]                                              
+                               currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup
+                               currentMaterialMeshMapping = {}
                                
-                               # Only make a new group.object name if the verts in the existing object have been used, this is obscure
-                               # but some files face groups seperating verts and faces which results in silly things. (no groups have names.)
-                               if len(l) > 1:
-                                       currentObjectName = '_'.join(l[1:])
-                               else: # No name given
-                                       # Make a new empty name
-                                       if l[0] == 'g': # Make a blank group name
-                                               currentObjectName = 'unnamed_grp_%d' % currentUnnamedGroupIdx
-                                               currentUnnamedGroupIdx +=1
-                                       else: # is an object.
-                                               currentObjectName = 'unnamed_ob_%d' % currentUnnamedObjectIdx
-                                               currentUnnamedObjectIdx +=1
+                               meshDict[currentObjectName] = (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
+                               currentMesh.hasFaceUV(1)
+                               contextMeshMatIdx = -1
                                
+                       else: 
+                               # Since we have this in Blender then we will check if the current Mesh has the material.
+                               # set the contextMeshMatIdx to the meshs index but only if we have it.
+                               currentMesh, currentUsedVertList, currentMaterialMeshMapping = meshDict[currentObjectName]
+                               #getMeshMaterialIndex(currentMesh, currentMat)
                                
-                               # If we havnt written to this mesh before then do so.
-                               # if we have then we'll just keep appending to it, this is required for soem files.
+                               try:
+                                       contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
+                               except KeyError:
+                                       contextMeshMatIdx -1
                                
-                               # If we are new, or we are not yet in the list of added meshes
-                               # then make us new mesh.
-                               if len(l) == 1 or currentObjectName not in meshDict.keys():
-                                       currentMesh = NMesh.GetRaw()
-                                       
-                                       currentUsedVertList = {}
-                                       
-                                       # Sg is a string
-                                       currentSmoothGroup = '(null)'
-                                       currentUsedVertListSmoothGroup = VERT_USED_LIST[:]                                              
-                                       currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup
-                                       currentMaterialMeshMapping = {}
-                                       
-                                       meshDict[currentObjectName] = (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
-                                       currentMesh.hasFaceUV(1)
-                                       currentMesh.verts.append( vertList[0] )
-                                       contextMeshMatIdx = -1
-                                       
-                               else: 
-                                       # Since we have this in Blender then we will check if the current Mesh has the material.
-                                       # set the contextMeshMatIdx to the meshs index but only if we have it.
-                                       currentMesh, currentUsedVertList, currentMaterialMeshMapping = meshDict[currentObjectName]
-                                       #getMeshMaterialIndex(currentMesh, currentMat)
-                                       
-                                       try:
-                                               contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
-                                       except KeyError:
-                                               contextMeshMatIdx -1
-                                       
-                                       # For new meshes switch smoothing groups to null
-                                       currentSmoothGroup = '(null)'
-                                       currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
+                               # For new meshes switch smoothing groups to null
+                               currentSmoothGroup = '(null)'
+                               currentUsedVertListSmoothGroup = currentUsedVertList[currentSmoothGroup]
+               
+               # MATERIAL
+               elif l[0] == 'usemtl':
+                       if len(l) == 1 or l[1] == '(null)':
+                               currentMat = nullMat # We know we have a null mat.
+                       else:
+                               currentMat = materialDict['_'.join(l[1:])]
+                               try:
+                                       contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name]
+                               except KeyError:
+                                       contextMeshMatIdx = -1 #getMeshMaterialIndex(currentMesh, currentMat)
                        
-                       # MATERIAL
-                       elif l[0] == 'usemtl':
-                               if len(l) == 1 or l[1] == NULL_MAT:
-                                       currentMat = nullMat # We know we have a null mat.
-                               else:
-                                       currentMat = materialDict['_'.join(l[1:])]
-                                       try:
-                                               contextMeshMatIdx = currentMaterialMeshMapping[currentMat.name]
-                                       except KeyError:
-                                               contextMeshMatIdx = -1 #getMeshMaterialIndex(currentMesh, currentMat)
+               # IMAGE
+               elif l[0] == 'usemat' or l[0] == 'usemap':
+                       if len(l) == 1 or l[1] == '(null)' or l[1] == 'off':
+                               currentImg = None
+                       else:
+                               # Load an image.
+                               newImgName = stripPath(' '.join(l[1:])) # Use space since its a file name.
                                
-                       # IMAGE
-                       elif l[0] == 'usemat' or l[0] == 'usemap':
-                               if len(l) == 1 or l[1] == '(null)' or l[1] == 'off':
-                                       currentImg = None
-                               else:
-                                       # Load an image.
-                                       newImgName = stripPath(' '.join(l[1:])) # Use space since its a file name.
+                               try:
+                                       # Assume its alredy set in the dict (may or maynot be loaded)
+                                       currentImg = imageDict[newImgName]
+                               
+                               except KeyError: # Not in dict, add for first time.
+                                       # Image has not been added, Try and load the image
+                                       currentImg = comprehansiveImageLoad(newImgName, DIR) # Use join in case of spaces 
+                                       imageDict[newImgName] = currentImg
+                                       # These may be None, thats okay.
                                        
-                                       try:
-                                               # Assume its alredy set in the dict (may or maynot be loaded)
-                                               currentImg = imageDict[newImgName]
                                        
-                                       except KeyError: # Not in dict, add for first time.
-                                               # Image has not been added, Try and load the image
-                                               currentImg = getImg(newImgName, DIR) # Use join in case of spaces 
-                                               imageDict[newImgName] = currentImg
-                                               # These may be None, thats okay.
-                                               
-                                               
-                       
-                       # MATERIAL FILE
-                       elif l[0] == 'mtllib':
-                               mtl_fileName = ' '.join(l[1:]) # SHOULD SUPPORT MULTIPLE MTL?
-                       lIdx+=1
                
-               # Applies material properties to materials alredy on the mesh as well as Textures.
-               if mtl_fileName:
-                       load_mtl(DIR, mtl_fileName, meshDict, materialDict)     
+               # MATERIAL FILE
+               elif l[0] == 'mtllib':
+                       mtl_fileName.append(' '.join(l[1:]) ) # SHOULD SUPPORT MULTIPLE MTL?
+               lIdx+=1
+       
+       # Applies material properties to materials alredy on the mesh as well as Textures.
+       for mtl in mtl_fileName:
+               load_mtl(DIR, mtl, meshDict, materialDict)      
+       
+       
+       importedObjects = []
+       for mk, me in meshDict.iteritems():
+               nme = me[0]
                
+               # Ignore no vert meshes.
+               if not nme.verts: # == []
+                       continue
                
-               importedObjects = []
-               for mk in meshDict.keys():
-                       meshDict[mk][0].verts.pop(0)
-                       
-                       # Ignore no vert meshes.
-                       if not meshDict[mk][0].verts:
-                               continue
-                       
-                       name = getUniqueName(mk)
-                       ob = NMesh.PutRaw(meshDict[mk][0], name)
-                       ob.name = name
-                       
-                       importedObjects.append(ob)
+               name = getUniqueName(mk)
+               ob = NMesh.PutRaw(nme, name)
+               ob.name = name
                
-               # Select all imported objects.
-               for ob in importedObjects:
-                       ob.sel = 1
+               importedObjects.append(ob)
        
-               print "obj import time: ", sys.time() - time1
+       # Select all imported objects.
+       for ob in importedObjects:
+               ob.sel = 1
+       if badObjUvs > 0:
+               print '\tERROR: found %d faces with badly formatted UV coords. everything else went okay.' % badObjUvs
        
-       except:
-               print Draw.PupMenu(ABORT_MENU)
-               return
-
+       if badObjFaceVerts > 0:
+               print '\tERROR: found %d faces reusing the same vertex. everything else went okay.' % badObjFaceVerts
+       
+       if badObjFaceTexCo > 0:
+               print '\tERROR: found %d faces with invalit texture coords. everything else went okay.' % badObjFaceTexCo               
+       
+       
+       print "obj import time: ", sys.time() - time1
+       
+# Batch directory loading.
+def load_obj_dir(obj_dir):
+       
+       # Strip file
+       obj_dir = stripFile(obj_dir)    
+       time = sys.time()
+       
+       objFiles = [f for f in os.listdir(obj_dir) if f.lower().endswith('obj')]
+       
+       Window.DrawProgressBar(0, '')
+       count = 0
+       obj_len = len(objFiles)
+       for obj in objFiles:
+               count+=1
+               
+               newScn = Scene.New(obj)
+               newScn.makeCurrent()
+               
+               Window.DrawProgressBar((float(count)/obj_len) - 0.01, '%s: %i of %i' % (obj, count, obj_len))
+               
+               load_obj(obj_dir  + obj)
+               
+       Window.DrawProgressBar(1, '')
+       print 'Total obj import "%s" dir: %.2f' % (obj_dir, sys.time() - time)
 
-def load_obj_callback(file):
-       # Try/Fails should realy account for these, but if somthing realy bad happens then Popup error.
-       try:
-               load_obj(file)
-       except:
-               print Draw.PupMenu(ABORT_MENU)
 
-Window.FileSelector(load_obj_callback, 'Import Wavefront OBJ')
+def main():
+       TEXT_IMPORT = 'Import a Wavefront OBJ'
+       TEXT_BATCH_IMPORT = 'Import *.obj to Scenes'
+       
+       if Window.GetKeyQualifiers() & Window.Qual.SHIFT:
+               if not os:
+                       Draw.PupMenu('Module "os" not found, needed for batch load, using normal selector.')
+                       Window.FileSelector(load_obj, TEXT_IMPORT)
+               else:
+                       Window.FileSelector(load_obj_dir, TEXT_BATCH_IMPORT)
+       else:
+               Window.FileSelector(load_obj, TEXT_IMPORT)
 
-# For testing compatibility
-'''
-TIME = sys.time()
-import os
-for obj in os.listdir('/obj/'):
-       if obj.lower().endswith('obj'):
-               print obj
-               newScn = Scene.New(obj)
-               newScn.makeCurrent()
-               load_obj('/obj/' + obj)
-
-print "TOTAL IMPORT TIME: ", sys.time() - TIME
-'''
-#load_obj('/obj/foot_bones.obj')
-#load_obj('/obj/mba1.obj')
-#load_obj('/obj/PixZSphere50.OBJ')
-#load_obj('/obj/obj_test/LHand.obj')
+if __name__ == '__main__':
+       main()
index d3d1d034db7d42fde349b301c02ee38442f961ee..adf49b1af7b261a90fab90097e21fd1bfaed7754 100644 (file)
@@ -1,15 +1,15 @@
 #!BPY
 
 """
-Name: 'Bridge/Skin/Loft'
-Blender: 234
+Name: 'Bridge Faces/Edge-Loops'
+Blender: 237
 Group: 'Mesh'
-Tooltip: 'Select 2 or more vert loops, then run this script'
+Tooltip: 'Select 2 vert loops, then run this script.'
 """
 
 __author__ = "Campbell Barton AKA Ideasman"
 __url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
-__version__ = "1.1 2005/06/13"
+__version__ = "1.0 2004/04/25"
 
 __bpydoc__ = """\
 With this script vertex loops can be skinned: faces are created to connect the
@@ -19,11 +19,7 @@ Usage:
 
 In mesh Edit mode select the vertices of the loops (closed paths / curves of
 vertices: circles, for example) that should be skinned, then run this script.
-A pop-up will provide further options.
-
-Notes:
-
-If the results of a method chosen from the pop-up are not adequate, undo and try one of the others.
+A pop-up will provide further options, if the results of a method are not adequate try one of the others.
 """
 
 
@@ -51,536 +47,443 @@ If the results of a method chosen from the pop-up are not adequate, undo and try
 # ***** END GPL LICENCE BLOCK ***** 
 # -------------------------------------------------------------------------- 
 
-
-
-# Made by Ideasman/Campbell 2004/04/25 - ideasman@linuxmail.org
+# Made by Ideasman/Campbell 2005/06/15 - ideasman@linuxmail.org
 
 import Blender
 from Blender import *
-import math
-from math import *
-
-
-choice = Draw.PupMenu(\
-'Loft-loop - shortest edge method|\
-Loft-loop - even method|\
-Loft-segment - shortest edge|\
-Loft-segment - even method')
-
-if choice == 1:
-       arg='A1'
-elif choice == 2:
-       arg='A2'
-elif choice == 3:
-       arg='B1'
-elif choice == 4:
-       arg='B2'
-
-
 
-#================#
-# Math functions #
-#================#
+BIG_NUM = 1<<30
 
-# Measure 2 points
-def measure(v1, v2):
-  return Mathutils.Vector([v1[0]-v2[0], v1[1] - v2[1], v1[2] - v2[2]]).length
-  
-# Clamp
-def clamp(max, number):
-       while number >= max:
-               number = number - max
-       return number
+global CULL_METHOD
+CULL_METHOD = 0
 
-#=============================================================#
-# List func that takes the last item and adds it to the front #
-#=============================================================#
-def listRotate(ls):
-       ls.append(ls.pop(0))
-
-#=================================================================#
-# Recieve a list of locs: [x,y,z] and return the average location #
-#=================================================================#
-def averageLocation(locList):
-       avLoc = [0,0,0]
-       
-       # Loop through x/y/z
-       for coordIdx in [0,1,2]:
+class edge:
+       def __init__(self, v1,v2):
+               self.v1 = v1
+               self.v2 = v2
                
-               # Add all the values from 1 of the 3 coords at the avLoc.
-               for loc in locList:
-                       avLoc[coordIdx] += loc[coordIdx]
+               # uv1 uv2 vcol1 vcol2 # Add later
+               self.length = (v1.co - v2.co).length
                
-               avLoc[coordIdx] = avLoc[coordIdx] / len(locList)        
-       return avLoc
-
-
+               self.removed = 0 # Have we been culled from the eloop
+               self.match = None # The other edge were making a face with
 
-#=============================#
-# Blender functions/shortcuts #
-#=============================#
-def error(str):
-       Draw.PupMenu('ERROR%t|'+str)
 
-# Returns a new face that has the same properties as the origional face
-# With no verts though
-def copyFace(face):
-  newFace = NMesh.Face()
-  # Copy some generic properties
-  newFace.mode = face.mode
-  if face.image != None:
-    newFace.image = face.image
-  newFace.flag = face.flag
-  newFace.mat = face.mat
-  newFace.smooth = face.smooth
-  return newFace
-
-#=============================================#
-# Find a selected vert that 2 faces share.    #
-#=============================================#
-def selVertBetween2Faces(face1, face2):
-       for v1 in face1.v:
-               if v1.sel:
-                       for v2 in face2.v:
-                               if v1 == v2:
-                                       return v1
-       
-       
-#=======================================================#
-# Measure the total distance between all the edges in   #
-# 2 vertex loops                                        #
-#=======================================================#
-def measureVloop(mesh, v1loop, v2loop, surplusFaces, bestSoFar):
-       totalDist = 0
-       
-       # Rotate the vertloops to cycle through each pair.
-       # of faces to compate the distance between the 2 poins
-       for ii in range(len(v1loop)):
-               if ii not in surplusFaces:
-                       # Clamp
-                       v2clampii = ii
-                       while v2clampii >= len(v2loop):
-                               v2clampii -= len(v2loop)
-                       print v2clampii
-                       
-                       V1 = selVertBetween2Faces(mesh.faces[v1loop[ii-1]], mesh.faces[v1loop[ii]])
-                       V2 = selVertBetween2Faces(mesh.faces[v2loop[v2clampii-1]], mesh.faces[v2loop[v2clampii]])
-                       
-                       totalDist += measure(V1, V2)
-                       # Bail out early if not an improvement on previously measured.
-                       if bestSoFar != None and totalDist > bestSoFar:
-                               return totalDist
-       
-       #selVertBetween2Faces(mesh.faces[v2loop[0]], mesh.faces[v2loop[1]])
-       return totalDist
-
-# Remove the shortest edge from a vert loop
-def removeSmallestFace(mesh, vloop):
-       bestDistSoFar = None
-       bestFIdxSoFar = None
-       for fIdx in vloop:
-               vSelLs = []
-               for v in mesh.faces[fIdx].v:
-                       if v.sel:
-                               vSelLs.append(v)
-               
-               dist = measure(vSelLs[0].co, vSelLs[1].co)
-               
-               if bestDistSoFar == None:
-                       bestDistSoFar = dist
-                       bestFIdxSoFar = fIdx 
-               elif dist < bestDistSoFar:
-                       bestDistSoFar = dist
-                       bestFIdxSoFar = fIdx
-       
-       # Return the smallest face index of the vloop that was sent
-       return bestFIdxSoFar
-
-
-#=============================================#
-# Take 2 vert loops and skin them             #
-#=============================================#
-def skinVertLoops(mesh, v1loop, v2loop):
-       
-       
-       #=============================================#
-       # Handle uneven vert loops, this is tricky    #
-       #=============================================#
-       # Reorder so v1loop is always the biggest
-       if len(v1loop) < len(v2loop):
-               v1loop, v2loop = v2loop, v1loop
-       
-       # Work out if the vert loops are equel or not, if not remove the extra faces from the larger
-       surplusFaces = []
-       tempv1loop = v1loop[:]  # strip faces off this one, use it to keep track of which we have taken faces from.
-       if len(v1loop) > len(v2loop):
-               
-               # Even face method.
-               if arg[1] == '2':
-                       remIdx = 0
-                       faceStepping = len(     v1loop) / len(v2loop)
-                       while len(v1loop) - len(surplusFaces) > len(v2loop):
-                               remIdx += faceStepping
-                               surplusFaces.append(tempv1loop[ clamp(len(tempv1loop),remIdx) ]) 
-                               tempv1loop.remove(surplusFaces[-1])
-               
-               # Shortest face
-               elif arg[1] == '1':
-                       while len(v1loop) - len(surplusFaces) > len(v2loop):
-                               surplusFaces.append(removeSmallestFace(mesh, tempv1loop)) 
-                               tempv1loop.remove(surplusFaces[-1])
-                       
-       
-       tempv1loop = None
-       
-       v2loop = optimizeLoopOrdedShortEdge(mesh, v1loop, v2loop, surplusFaces)
-       
-       # make Faces from 
-       lenVloop = len(v1loop)
-       lenSupFaces = len(surplusFaces)
-       fIdx = 0
-       offset = 0
-       while fIdx < lenVloop:
+class edgeLoop:
+       def __init__(self, loop): # Vert loop
+               # Use next and prev, nextDist, prevDist
                
-               face = copyFace( mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]] )
+               # Get Loops centre.
+               self.centre = Mathutils.Vector()
+               f = 1.0/len(loop)
+               for v in loop:
+                       self.centre += v.co * f
                
-               if v1loop[fIdx] in surplusFaces:
-                       # Draw a try, this face does not catch with an edge.
-                       # So we must draw a tri and wedge it in.
-                       
-                       # Copy old faces properties
+               
+               
+               
+               # Convert Vert loop to Edges.
+               self.edges = []
+               vIdx = 0
+               while vIdx < len(loop):
+                       self.edges.append( edge(loop[vIdx-1], loop[vIdx]) )
+                       vIdx += 1
+               
+               # Assign linked list
+               for eIdx in range(len(self.edges)-1):
+                       self.edges[eIdx].next = self.edges[eIdx+1]
+                       self.edges[eIdx].prev = self.edges[eIdx-1]
+               # Now last
+               self.edges[-1].next = self.edges[0]
+               self.edges[-1].prev = self.edges[-2]
+               
+               
+               
+               # GENERATE AN AVERAGE NORMAL FOR THE WHOLE LOOP.
+               self.normal = Mathutils.Vector()
+               for e in self.edges:
+                       n = Mathutils.CrossVecs(self.centre-e.v1.co, self.centre-e.v2.co)
+                       # Do we realy need tot normalize?
+                       n.normalize()
+                       self.normal += n
+               self.normal.normalize()
+               
+               
+               # Generate a normal for each edge.
+               for e in self.edges:
                        
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx)]],\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]]) )
+                       n1 = e.v1.co
+                       n2 = e.v2.co
+                       n3 = e.prev.v1.co
                        
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]],\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx+2)]]) )
+                       a = n1-n2
+                       b = n1-n3
+                       normal1 = Mathutils.CrossVecs(a,b)
+                       normal1.normalize()
                        
-                       #face.v.append( selVertBetween2Faces(\
-                       #mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset +1 ))]],\
-                       #mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset + 2))]]) )
+                       n1 = e.v2.co
+                       n3 = e.next.v2.co
+                       n2 = e.v1.co
                        
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset))]],\
-                               mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, fIdx - offset + 1)]]) )
+                       a = n1-n2
+                       b = n1-n3
                        
-                       mesh.faces.append(face)                 
+                       normal2 = Mathutils.CrossVecs(a,b)
+                       normal2.normalize()
                        
-                       # We need offset to work out how much smaller v2loop is at this current index.
-                       offset+=1               
+                       # Reuse normal1 var
+                       normal1 += normal1 + normal2
+                       normal1.normalize()
                        
+                       e.normal = normal1
+                       #print e.normal
+               
 
-               else:   
-                       # Draw a normal quad between the 2 edges/faces
-                       
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx)]],\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]]) )
+
+               
+       def backup(self):
+               # Keep a backup of the edges
+               self.backupEdges = self.edges[:]
                        
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]],\
-                               mesh.faces[v1loop[clamp(lenVloop, fIdx+2)]]) )
+       def restore(self):
+               self.edges = self.backupEdges[:]
+               for e in self.edges:
+                       e.removed = 0
+               
+       def reverse(self):
+               self.edges.reverse()
+               for e in self.edges:
+                       e.normal = -e.normal
+                       e.v1, e.v2 = e.v2, e.v1
+               self.normal = -self.normal
+       
+       # Removes N Smallest edges and backs up
+       def removeSmallest(self, cullNum, otherLoopLen):
+               global CULL_METHOD
+               if CULL_METHOD == 0: # Shortest edge
                        
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset +1 ))]],\
-                               mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset + 2))]]) )
+                       eloopCopy = self.edges[:]
+                       eloopCopy.sort(lambda e1, e2: cmp(e1.length, e2.length )) # Length sort, smallest first
+                       eloopCopy = eloopCopy[:cullNum]
+                       for e in eloopCopy:
+                               e.removed = 1
+                               self.edges.remove( e ) # Remove from own list, still in linked list.
                        
-                       face.v.append( selVertBetween2Faces(\
-                               mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset))]],\
-                               mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, fIdx - offset + 1)]]) )
+               else: # CULL METHOD is even
+                               
+                       culled = 0
                        
-                       mesh.faces.append(face)
+                       step = int(otherLoopLen / float(cullNum))
                        
-               fIdx +=1
-               
-       return mesh
-
-
-
-#=======================================================#
-# Takes a face and returns the number of selected verts #
-#=======================================================#
-def faceVSel(face):
-       vSel = 0
-       for v in face.v:
-               if v.sel:
-                       vSel +=1
-       return vSel
-
-
-
-
-#================================================================#
-# This function takes a face and returns its selected vert loop  #
-# it returns a list of face indicies
-#================================================================#
-def vertLoop(mesh, startFaceIdx, fIgLs): # fIgLs is a list of faces to ignore.
-       # Here we store the faces indicies that
-       # are a part of the first vertex loop
-       vertLoopLs = [startFaceIdx]
+                       currentEdge = self.edges[0]
+                       while culled < cullNum:
+                               
+                               # Get the shortest face in the next STEP
+                               while currentEdge.removed == 1:
+                                       # Bug here!
+                                       currentEdge = currentEdge.next
+                               smallestEdge = currentEdge
+                               
+                               for i in range(step):
+                                       currentEdge = currentEdge.next
+                                       while currentEdge.removed == 1:
+                                               currentEdge = currentEdge.next
+                                       if smallestEdge.length > currentEdge.length:
+                                               smallestEdge = currentEdge
+                               
+                               # In that stepping length we have the smallest edge.remove it
+                               smallestEdge.removed = 1
+                               self.edges.remove(smallestEdge)
+                               
+                               culled+=1
+       
 
-       restart = 0
-       while restart == 0:
-               # this keeps the face loop going until its told to stop,
-               # If the face loop does not find an adjacent face then the vert loop has been compleated
-               restart = 1 
-               
-               # Get my selected verts for the active face/edge.
-               selVerts = []
-               for v in mesh.faces[vertLoopLs[-1]].v:
-                       selVerts.append(v)
-               
-               fIdx = 0
-               while fIdx < len(mesh.faces) and restart:
-                       # Not already added to the vert list
-                       if fIdx not in fIgLs + vertLoopLs:
-                               # Has 2 verts selected
-                               if faceVSel(mesh.faces[fIdx]) > 1:
-                                       # Now we need to find if any of the selected verts
-                                       # are shared with our active face. (are we next to ActiveFace)
-                                       for v in mesh.faces[fIdx].v:
-                                               if v in selVerts:
-                                                       vertLoopLs.append(fIdx)
-                                                       restart = 0 # restart the face loop.
-                                                       break
+# Returns face edges.
+# face must have edge data.
+def faceEdges(me, f):
+       if len(f) == 3:
+               return [\
+                me.findEdge(f[0], f[1]),\
+                me.findEdge(f[1], f[2]),\
+                me.findEdge(f[2], f[0])\
+               ]
+       elif len(f) == 4:
+               return [\
+                me.findEdge(f[0], f[1]),\
+                me.findEdge(f[1], f[2]),\
+                me.findEdge(f[2], f[3]),\
+                me.findEdge(f[3], f[0])\
+               ]
+
+
+def getSelectedEdges(me, ob):  
+       SEL_FLAG = NMesh.EdgeFlags['SELECT']
+       FGON_FLAG = NMesh.EdgeFlags['FGON']     
+       
+       edges = [e for e in me.edges if e.flag & SEL_FLAG if (e.flag & FGON_FLAG) == 0 ]
+       
+       # Now remove edges that face 2 or more selected faces usoing them
+       edgeFromSelFaces = []
+       for f in me.faces:
+               if len(f) >2 and f.sel:
+                       edgeFromSelFaces.extend(faceEdges(me, f))
+       
+       # Remove all edges with 2 or more selected faces as uses.
+       for e in edgeFromSelFaces:
+               if edgeFromSelFaces.count(e) > 1:
+                       me.removeEdge(e.v1, e.v2)
+       
+       # Remove selected faces?
+       fIdx = len(me.faces)
+       while fIdx:
+               fIdx-=1
+               if len(me.faces[fIdx]) > 2:
+                       if me.faces[fIdx].sel:
+                               me.faces.pop(fIdx)
+       return [e for e in edges if edgeFromSelFaces.count(e) < 2]
+       
+       
+# Like vert loops 
+def getVertLoops(selEdges):
+       mainVertLoops = []
+       while selEdges:
+               e = selEdges.pop()
+               contextVertLoop= [e.v1, e.v2] # start the vert loop
+               
+               eIdx = 1 # Get us into the loop. dummy var.
+               
+               # if eIdx == 0 then it means we searched and found no matches... 
+               # time for a new vert loop,
+               while eIdx:
+                       eIdx = len(selEdges)
+                       while eIdx:
+                               eIdx-=1
+                               
+                               # Check for edge attached at the head of the loop.
+                               if contextVertLoop[0] == selEdges[eIdx].v1:
+                                       contextVertLoop.insert(0, selEdges.pop(eIdx).v2)
+                               elif contextVertLoop[0] == selEdges[eIdx].v2:
+                                       contextVertLoop.insert(0, selEdges.pop(eIdx).v1)
                                        
-                       fIdx +=1
-                       
-       return vertLoopLs
-
-
+                               # Chech for edge vert at the tail.
+                               elif contextVertLoop[-1] == selEdges[eIdx].v1:
+                                       contextVertLoop.append(selEdges.pop(eIdx).v2)
+                               elif contextVertLoop[-1] == selEdges[eIdx].v2:
+                                       contextVertLoop.append(selEdges.pop(eIdx).v1)
+                               else:
+                                       # None found? Keep looking
+                                       continue
+                               
+                               # Once found we.
+                               break
+               
+               # Is this a loop? if so then its forst and last vert must be teh same.
+               if contextVertLoop[0].index == contextVertLoop[-1].index:
+                       contextVertLoop.pop() # remove double vert
+                       mainVertLoops.append(contextVertLoop)
+               
+               # Build context vert loops
+       return mainVertLoops
 
 
-#================================================================#
-# Now we work out the optimum order to 'skin' the 2 vert loops   #
-# by measuring the total distance of all edges created,          #
-# test this for every possible series of joins                   # 
-# and find the shortest, Once this is done the                   #
-# shortest dist can be skinned.                                  #
-# returns only the 2nd-reordered vert loop                       #
-#================================================================#
-def optimizeLoopOrded(mesh, v1loop, v2loop):
-       bestSoFar = None
-       
-       # Measure the dist, ii is just a counter
-       for ii in range(len(v1loop)):
-               
-               # Loop twice , Once for the forward test, and another for the revearsed
-               for iii in [None, None]:
-                       dist = measureVloop(mesh, v1loop, v2loop, bestSoFar)
-                       # Initialize the Best distance recorded
-                       if bestSoFar == None or dist < bestSoFar:
-                               bestSoFar = dist
-                               bestv2Loop = v2loop[:]
-                       
-                       # We might have got the vert loop backwards, try the other way
-                       v2loop.reverse()
-               listRotate(v2loop)
-       return bestv2Loop
-       
-       
-#================================================================#
-# Now we work out the optimum order to 'skin' the 2 vert loops   #
-# by measuring the total distance of all edges created,          #
-# test this for every possible series of joins                   # 
-# and find the shortest, Once this is done the                   #
-# shortest dist can be skinned.                                  #
-# returns only the 2nd-reordered vert loop                       #
-#================================================================#
-def optimizeLoopOrdedShortEdge(mesh, v1loop, v2loop, surplusFaces):
-       bestSoFar = None
-       
-       # Measure the dist, ii is just a counter
-       for ii in range(len(v2loop)):
-               
-               # Loop twice , Once for the forward test, and another for the revearsed
-               for iii in [None, None]:
-                       dist = measureVloop(mesh, v1loop, v2loop, surplusFaces, bestSoFar)
-                       print 'dist', dist 
-                       # Initialize the Best distance recorded
-                       if bestSoFar == None or dist < bestSoFar:
-                               bestSoFar = dist
-                               bestv2Loop = v2loop[:]
-                               
-                       
-                       # We might have got the vert loop backwards, try the other way
-                       v2loop.reverse()
-               #v2loop = listRotate(v2loop)
-               listRotate(v2loop)
-       print 'best so far ', bestSoFar
-       return bestv2Loop       
+def skin2EdgeLoops(eloop1, eloop2, me, ob, MODE):
+       # Make sure e1 loops is bigger then e2
+       if len(eloop1.edges) != len(eloop2.edges):
+               if len(eloop1.edges) < len(eloop2.edges):
+                       eloop1, eloop2 = eloop2, eloop1
+               
+               eloop1.backup() # were about to cull faces
+               CULL_FACES = len(eloop1.edges) - len(eloop2.edges)
+               eloop1.removeSmallest(CULL_FACES, len(eloop1.edges))
+       else:
+               CULL_FACES = 0
+       # First make sure poly vert loops are in sync with eachother.
        
-
-#==============================#
-#  Find our     vert loop list #
-#==============================#
-# Find a face with 2 verts selected,
-#this will be the first face in out vert loop
-def findVertLoop(mesh, fIgLs): # fIgLs is a list of faces to ignore.
-       
-       startFaceIdx = None
-       
-       fIdx = 0
-       while fIdx < len(mesh.faces):   
-               if fIdx not in fIgLs:
-                       # Do we have an edge?
-                       if faceVSel(mesh.faces[fIdx]) > 1:
-                               # THIS IS THE STARTING FACE.
-                               startFaceIdx = fIdx
+       # The vector allong which we are skinning.
+       skinVector = eloop1.centre - eloop2.centre
+       
+       loopDist = skinVector.length
+       
+       
+       # IS THE LOOP FLIPPED, IF SO FLIP BACK.
+       angleBetweenLoopNormals = Mathutils.AngleBetweenVecs(eloop1.normal, eloop2.normal)
+       
+       if angleBetweenLoopNormals > 90:
+               eloop2.reverse()
+       
+       
+       bestEloopDist = BIG_NUM
+       bestOffset = 0
+       # Loop rotation offset to test.1
+       eLoopIdxs = range(len(eloop1.edges))
+       for offset in range(len(eloop1.edges)):
+               totEloopDist = 0 # Measure this total distance for thsi loop.
+               
+               offsetIndexLs = eLoopIdxs[offset:] + eLoopIdxs[:offset] # Make offset index list
+               
+               # e1Idx is always from 0 to N, e2Idx is offset.
+               for e1Idx, e2Idx in enumerate(offsetIndexLs):
+                       # Measure the vloop distance ===============
+                       totEloopDist += ((eloop1.edges[e1Idx].v1.co - eloop2.edges[e2Idx].v1.co).length / loopDist) #/ nangle1
+                       totEloopDist += ((eloop1.edges[e1Idx].v2.co - eloop2.edges[e2Idx].v2.co).length / loopDist) #/ nangle1
+                       
+                       # Premeture break if where no better off
+                       if totEloopDist > bestEloopDist:
                                break
-               fIdx+=1
+               
+               if totEloopDist < bestEloopDist:
+                       bestOffset = offset
+                       bestEloopDist = totEloopDist
        
-       # Here we access the function that generates the real vert loop
-       if startFaceIdx != None:
-               return vertLoop(mesh, startFaceIdx, fIgLs)
-       else:
-               # We are out'a vert loops, return a None,
-               return None
-
-#===================================#
-# Get the average loc of a vertloop #
-# This is used when working out the #
-# order to loft an object           #
-#===================================#
-def vLoopAverageLoc(mesh, vertLoop):
-       locList = [] # List of vert locations
-               
-       fIdx = 0
-       while fIdx < len(mesh.faces):   
-               if fIdx in vertLoop:
-                       for v in mesh.faces[fIdx].v:
-                               if v.sel:
-                                       locList.append(v.co)
-               fIdx+=1
-       
-       return averageLocation(locList)
-
-
+       # Modify V2 LS for Best offset
+       eloop2.edges = eloop2.edges[bestOffset:] + eloop2.edges[:bestOffset]
+       
+       
+       
+       for loopIdx in range(len(eloop2.edges)):
+               e1 = eloop1.edges[loopIdx]
+               e2 = eloop2.edges[loopIdx]
+               
+               # Remember the pairs for fan filling culled edges.
+               e1.match = e2; e2.match = e1
+               
+               # need some smart face flipping code here.
+               f = NMesh.Face([e1.v1, e1.v2, e2.v2, e2.v1])
+               
+               f.sel = 1
+               me.faces.append(f)
+       
+       # FAN FILL MISSING FACES.
+       if CULL_FACES:
+               # Culled edges will be in eloop1.
+               FAN_FILLED_FACES = 0
+               
+               contextEdge = eloop1.edges[0] # The larger of teh 2
+               while FAN_FILLED_FACES < CULL_FACES:
+                       while contextEdge.next.removed == 0:
+                               contextEdge = contextEdge.next
+                       
+                       vertFanPivot = contextEdge.match.v2
+                       
+                       while contextEdge.next.removed == 1:
+                               
+                               f = NMesh.Face([contextEdge.next.v1, contextEdge.next.v2, vertFanPivot] )
+                               
+                               
+                               f.sel = 1
+                               me.faces.append(f)
+                               
+                               # Should we use another var?, this will work for now.
+                               contextEdge.next.removed = 1
+                               
+                               contextEdge = contextEdge.next
+                               FAN_FILLED_FACES += 1
+               
+               eloop1.restore() # Add culled back into the list.
+       #if angleBetweenLoopNormals > 90:
+       #       eloop2.reverse()
 
-#=================================================#
-# Vert loop group functions
 
-def getAllVertLoops(mesh):
-       # Make a chain of vert loops.
-       fIgLs = [] # List of faces to ignore 
-       allVLoops = [findVertLoop(mesh, fIgLs)]
-       while allVLoops[-1] != None:
+def main():
+       global CULL_METHOD
+       
+       is_editmode = Window.EditMode()
+       if is_editmode: Window.EditMode(0)
+       ob = Scene.GetCurrent().getActiveObject()
+       if ob == None or ob.getType() != 'Mesh':
+               return
+       
+       me = ob.getData()
+       if not me.edges:
+               Draw.PupMenu('Error, add edge data first')
+               if is_editmode: Window.EditMode(1)
+               return
+       
+       # BAD BLENDER PYTHON API, NEED TO ENTER EXIT EDIT MODE FOR ADDING EDGE DATA.
+       # ADD EDGE DATA HERE, Python API CANT DO IT YET, LOOSES SELECTION
+       
+       selEdges = getSelectedEdges(me, ob)
+       vertLoops = getVertLoops(selEdges) # list of lists of edges.
+       
+       if len(vertLoops) > 2:
+               choice = Draw.PupMenu('Loft '+str(len(vertLoops))+' edge loops%t|loop|segment')
+               if choice == -1:
+                       if is_editmode: Window.EditMode(1)
+                       return
+       elif len(vertLoops) < 2:
+               Draw.PupMenu('Error, No Vertloops found%t|if you have a valid selection, go in and out of face edit mode to update the selection state.')
+               if is_editmode: Window.EditMode(1)      
+               return
+       else:
+               choice = 2
+       
+       
+       # The line below checks if any of the vert loops are differenyt in length.
+       if False in [len(v) == len(vertLoops[0]) for v in vertLoops]:
+               CULL_METHOD = Draw.PupMenu('Small to large edge loop distrobution method%t|remove edges evenly|remove smallest edges edges')
+               if CULL_METHOD == -1:
+                       if is_editmode: Window.EditMode(1)
+                       return
                
-               # In future ignore all faces in this vert loop
-               fIgLs += allVLoops[-1]          
+               if CULL_METHOD ==1: # RESET CULL_METHOD
+                       CULL_METHOD = 0 # shortest
+               else:
+                       CULL_METHOD = 1 # even
+       
+       
+       time1 = sys.time()
+       # Convert to special edge data.
+       edgeLoops = []
+       for vloop in vertLoops:
+               edgeLoops.append(edgeLoop(vloop))
                
-               # Add the new vert loop to the list
-               allVLoops.append( findVertLoop(mesh, fIgLs) )
        
-       return allVLoops[:-1] # Remove the last Value- None.
+       # VERT LOOP ORDERING CODE
+       # Build a worm list - grow from Both ends
+       edgeOrderedList = [edgeLoops.pop()]
        
+       # Find the closest.
+       bestSoFar = BIG_NUM
+       bestIdxSoFar = None
+       for edLoopIdx, edLoop in enumerate(edgeLoops):
+               l =(edgeOrderedList[-1].centre - edLoop.centre).length 
+               if l < bestSoFar:
+                       bestIdxSoFar = edLoopIdx
+                       bestSoFar = l
+                       
+       edgeOrderedList.append( edgeLoops.pop(bestIdxSoFar) )
        
-def reorderCircularVLoops(mesh, allVLoops):
-       # Now get a location for each vert loop.
-       allVertLoopLocs = []
-       for vLoop in allVLoops:
-               allVertLoopLocs.append( vLoopAverageLoc(mesh, vLoop) )
-
-       # We need to find the longest distance between 2 vert loops so we can 
-       reorderedVLoopLocs = []
-
-       # Start with this one, then find the next closest.
-       # in doing this make a new list called reorderedVloop
-       currentVLoop = 0
-       reorderedVloopIdx = [currentVLoop]
-       newOrderVLoops = [allVLoops[0]] # This is a re-ordered allVLoops
-       while len(reorderedVloopIdx) != len(allVLoops):
-               bestSoFar = None
-               bestVIdxSoFar = None
-               for vLoopIdx in range(len(allVLoops)):
-                       if vLoopIdx not in reorderedVloopIdx + [currentVLoop]:
-                               if bestSoFar == None:
-                                       bestSoFar = measure( allVertLoopLocs[vLoopIdx], allVertLoopLocs[currentVLoop] )
-                                       bestVIdxSoFar = vLoopIdx
-                               else:
-                                       newDist = measure( allVertLoopLocs[vLoopIdx], allVertLoopLocs[currentVLoop] )
-                                       if newDist < bestSoFar:
-                                               bestSoFar = newDist
-                                               bestVIdxSoFar = vLoopIdx
-               
-               reorderedVloopIdx.append(bestVIdxSoFar)
-               reorderedVLoopLocs.append(allVertLoopLocs[bestVIdxSoFar])
-               newOrderVLoops.append( allVLoops[bestVIdxSoFar] ) 
-               
-               # Start looking for the next best fit
-               currentVLoop = bestVIdxSoFar
-       
-       # This is not the locicle place to put this but its convieneint.
-       # Here we find the 2 vert loops that are most far apart
-       # We use this to work out which 2 vert loops not to skin when making an open loft.
-       vLoopIdx = 0
-       # Longest measured so far - 0 dummy.
-       bestSoFar = 0
-       while vLoopIdx < len(reorderedVLoopLocs):
-               
-               # Skin back to the start if needs be, becuase this is a crcular loft
-               toSkin2 = vLoopIdx + 1
-               if toSkin2 == len(reorderedVLoopLocs):
-                       toSkin2 = 0
-               
-               newDist  = measure( reorderedVLoopLocs[vLoopIdx], reorderedVLoopLocs[toSkin2] )
-               
-               if newDist >= bestSoFar:
-                       bestSoFar = newDist
-                       vLoopIdxNotToSkin = vLoopIdx + 1        
-                               
-               vLoopIdx +=1 
+       # Now we have the 2 closest, append to either end-
+       # Find the closest.
+       while edgeLoops:
+               bestSoFar = BIG_NUM
+               bestIdxSoFar = None
+               first_or_last = 0 # Zero is first
+               for edLoopIdx, edLoop in enumerate(edgeLoops):
+                       l1 =(edgeOrderedList[-1].centre - edLoop.centre).length 
+                       
+                       if l1 < bestSoFar:
+                               bestIdxSoFar = edLoopIdx
+                               bestSoFar = l1
+                               first_or_last = 1 # last
+                       
+                       l2 =(edgeOrderedList[0].centre - edLoop.centre).length 
+                       if l2 < bestSoFar:
+                               bestIdxSoFar = edLoopIdx
+                               bestSoFar = l2
+                               first_or_last = 0 # last
+               
+               if first_or_last: # add closest Last
+                       edgeOrderedList.append( edgeLoops.pop(bestIdxSoFar) )   
+               else: # Add closest First
+                       edgeOrderedList.insert(0, edgeLoops.pop(bestIdxSoFar) )  # First
        
-       return newOrderVLoops, vLoopIdxNotToSkin
-
-
-is_editmode = Window.EditMode()
-if is_editmode: Window.EditMode(0)
-
-# Get a mesh and raise errors if we cant
-mesh = None
-if choice == -1:
-       pass
-elif len(Object.GetSelected()) > 0:
-  if Object.GetSelected()[0].getType() == 'Mesh':
-    mesh = Object.GetSelected()[0].getData()
-  else:
-    error('please select a mesh')
-else:
-  error('no mesh object selected')
-
-time1 = sys.time()
-if mesh != None:
-  Window.WaitCursor(1)
-  allVLoops = getAllVertLoops(mesh)
-  
-  # Re order the vert loops
-  allVLoops, vLoopIdxNotToSkin = reorderCircularVLoops(mesh, allVLoops)        
-  
-  vloopIdx = 0
-  while vloopIdx < len(allVLoops):
-    #print range(len(allVLoops) )
-    #print vloopIdx
-    #print allVLoops[vloopIdx]
-    
-    # Skin back to the start if needs be, becuase this is a crcular loft
-    toSkin2 = vloopIdx + 1
-    if toSkin2 == len(allVLoops):
-      toSkin2 = 0
-
-    # Circular loft or not?
-    if arg[0] == 'B': # B for open
-      if vloopIdx != vLoopIdxNotToSkin:
-        mesh = skinVertLoops(mesh, allVLoops[vloopIdx], allVLoops[toSkin2])
-    elif arg[0] == 'A': # A for closed
-      mesh = skinVertLoops(mesh, allVLoops[vloopIdx], allVLoops[toSkin2])
-    
-    vloopIdx +=1       
-  
-  mesh.update(1,(mesh.edges != []),0)
+       for i in range(len(edgeOrderedList)-1):
+               skin2EdgeLoops(edgeOrderedList[i], edgeOrderedList[i+1], me, ob, 0)     
+       if choice == 1 and len(edgeOrderedList) > 2: # Loop
+               skin2EdgeLoops(edgeOrderedList[0], edgeOrderedList[-1], me, ob, 0)      
+       
+       me.update(1, 1, 0)
+       if is_editmode: Window.EditMode(1)
 
-if is_editmode: Window.EditMode(1)
-Window.WaitCursor(0)
-print "skinning time: %.2f" % (sys.time() - time1)
+main()
index 0d0f4dfe065b8a3e622fb771ea7daf5358de7ca4..b4a9d9bc188e3ee1e7c5aef1eacdff91a045f568 100644 (file)
@@ -2,7 +2,7 @@
 
 """ Registration info for Blender menus:
 Name: 'Texture Baker'
-Blender: 237
+Blender: 236
 Group: 'UV'
 Tooltip: 'Procedural to uvmapped texture baker'
 """
@@ -11,10 +11,11 @@ __author__ = "Jean-Michel Soler (jms)"
 __url__ = ("blender", "elysiun",
 "Official Page, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_mesh3d2uv2d_en.htm",
 "Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
-__version__ = "0.2.8 2005/7/20"
+__version__ = "0.3.0 2005/10/09"
 
 __bpydoc__ = """\
-Texture Baker "bakes" Blender procedural materials (including textures): it saves them as 2d uv-mapped images.
+This script "bakes" Blender procedural materials (including textures): it saves
+them as 2d uv-mapped images.
 
 This script saves an uv texture layout of the chosen mesh, that can be used as
 an uv map for it.  It is a way to export procedurals from Blender as normal
@@ -23,21 +24,22 @@ with the mesh in games and other 3d applications.
 
 Usage:
 
-a) Enter face mode and define uv coordinates for your mesh (do not forget to choose a development shape);<br>
-b) Define its materials and textures;<br>
+a) Enter face mode and define uv coordinates for your mesh;<br>
+b) Define its materials and textures ;
 c) Run this script and check the console.
 
-Global variables:
+Global variables 
 
-a) FRAME (integer): the last frame of the animation, autodocumented.<br>
-b) LIMIT (integer): 0 or 1, uvcoords may exceed limits 0.0 to 1.0, this variable obliges the script to do a complete framing of the uvcoord.
+a)  FRAME  integer, the last frame of the animation, autodocumented .
+b)  LIMIT  integer, 0 or 1, uvcoords may exceed limits 0.0 to 1.0 , this variable
+obliges the script to do a complete framing of the uvcoord . 
 
 Notes:<br>
-   This script was based on a suggestion by Martin (Theeth) Poirier.
+   This script was based on a suggestion by Martin (Theeth) Poirier;<br>
 """
 
 #---------------------------------------------
-# Last release : 0.2.8 ,  2005/07/20 , 17h10
+# Last release : 0.3.0 ,  2005/10/09 , 23h23
 #---------------------------------------------
 #---------------------------------------------
 # (c) jm soler  07/2004 : 'Procedural Texture Baker'
@@ -47,6 +49,14 @@ Notes:<br>
 #
 #     Released under Blender Artistic Licence
 #
+#
+#   0.3.0
+#      TAILLEIMAGE variable 
+# 
+#   0.2.9
+#      -- little probleme with the KEEPRENDERWINDOW variable .
+#      removed . script seems to works correctly now .
+# 
 #   0.2.8
 #       -- added the forgotten image property in face
 #       data. a little longer but better.
@@ -201,6 +211,8 @@ DEBUG=1
 RENDERLAYER=20
 SCENELAYERS=[]
 
+
+
 helpmsg = """
 Texture Baker:
 
@@ -250,6 +262,7 @@ def RenameImage(RDIR, MYDIR, FILENAME, name):
     """
     newfname = RDIR + MYDIR + name
     if newfname.find('.png', -4) < 0 : newfname += '.png'
+       
     if not Blender.sys.exists(newfname):
        os.rename(FILENAME, newfname)
     else:
@@ -290,13 +303,13 @@ def SAVE_image (rc, name, FRAME, result):
    rc.startFrame(NEWFRAME)
    rc.endFrame(NEWFRAME)
    rc.renderAnim()
-   if result!=2 and not KEEPRENDERWINDOW:
+   if result!=2 
       Blender.Scene.Render.CloseRenderWindow()
       FILENAME = "%04d" % NEWFRAME
       FILENAME = FILENAME.replace (' ', '0')
       FILENAME = RDIR + MYDIR + FILENAME + '.png'
       RenameImage(RDIR, MYDIR, FILENAME, name)
-       
+
    rc.endFrame(OLDEFRAME)
    rc.startFrame(OLDSFRAME)
    rc.setRenderPath(RENDERDIR)
@@ -346,14 +359,24 @@ def SHOOT (XYlimit, frame, obj, name, FRAME, result):
    OLDy = context.imageSizeY()
    OLDx = context.imageSizeX()
 
-   tres = Draw.PupMenu('TEXTURE OUT RESOLUTION : %t | 256 %x1 | 512 %x2 | 768 %x3 | 1024 %x4 | 2048 %x5 ')
+   TAILLEIMAGE='TEXTURE OUT RESOLUTION : %t |'
+   TAILLEIMAGE+='256 %x1 |'
+   TAILLEIMAGE+='512 %x2 |'
+   TAILLEIMAGE+='768 %x3 |'
+   TAILLEIMAGE+='1024 %x4 |'
+   TAILLEIMAGE+='2048 %x5 '
+   #TAILLEIMAGE+='| 4096 %x6 ' 
+   tres = Draw.PupMenu(TAILLEIMAGE)
 
    if (tres) == 1: res = 256
    elif (tres) == 2: res = 512
    elif (tres) == 3: res = 768
    elif (tres) == 4: res = 1024
    elif (tres) == 5: res = 2048
+   # elif (tres) == 6: res = 4096
    else: res = 512
+   #...
 
    SCENELAYERS=SC.layers
    SC.layers = [20]