From b970eadedfd9b70ba08a0c99e5f3fac3c31ec1e7 Mon Sep 17 00:00:00 2001 From: Willian Padovani Germano Date: Tue, 11 Oct 2005 02:32:58 +0000 Subject: [PATCH] Scripts: - 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. --- release/scripts/console.py | 491 +++-- release/scripts/disp_paint.py | 383 +++- release/scripts/hotkeys.py | 184 +- release/scripts/lightwave_export.py | 76 +- release/scripts/lightwave_import.py | 1684 ++++++++++++++++-- release/scripts/obj_export.py | 301 +++- release/scripts/obj_import.py | 956 ++++++---- release/scripts/skin.py | 935 +++++----- release/scripts/tex2uvbaker.py | 67 +- release/scripts/{UVpaint05.py => uvpaint.py} | 419 +++-- release/scripts/wings_export.py | 22 - source/blender/python/api2_2x/BGL.c | 29 +- 12 files changed, 3994 insertions(+), 1553 deletions(-) rename release/scripts/{UVpaint05.py => uvpaint.py} (51%) diff --git a/release/scripts/console.py b/release/scripts/console.py index e95d6bc34b1..4ce58e06e10 100644 --- a/release/scripts/console.py +++ b/release/scripts/console.py @@ -18,11 +18,16 @@ that points to its official homepage, with news, downloads and documentation. Usage:
Type your code and hit "Enter" to get it executed.
- Right mouse click: Console Menu (Save output, etc);
+ - Mousewheel: Scroll text - Arrow keys: command history and cursor;
- - Shift + arrow keys: jump words;
+ - Shift + Backspace: Backspace whole word;
+ - Shift + Arrow keys: jump words;
+ - Ctrl + (+/- or mousewheel): Zoom text size;
- Ctrl + Tab: auto compleate based on variable names and modules loaded -- multiple choices popup a menu;
- 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: - 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) ) - - # 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 + 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: # Tab added at the start for added if 1: statement + multiLineCode.append('\t%s' % cmdBuffer[-i].cmd ) + + # Mark as executed + 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. - - #choice = Draw.PupMenu('Select Variabe name%t|' + '|'.join(possibilities) ) - choice = Draw.PupMenu(menuText + '|'.join(menuListAbs)) - if choice != -1: + # No items to display? no menu + if not menuList: + return - 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:])) + 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] + + if choice != -1: + 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('
'): + 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-----------------------------------------------------------------# diff --git a/release/scripts/disp_paint.py b/release/scripts/disp_paint.py index e7628a00cae..e5ce81f65f3 100644 --- a/release/scripts/disp_paint.py +++ b/release/scripts/disp_paint.py @@ -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 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.") + 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.") - + 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) diff --git a/release/scripts/hotkeys.py b/release/scripts/hotkeys.py index ca2ef1689cb..d56951959c3 100644 --- a/release/scripts/hotkeys.py +++ b/release/scripts/hotkeys.py @@ -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:
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)4: + UP-=5 + elif (evt== UPARROWKEY): + if (UP+SCROLL)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) diff --git a/release/scripts/lightwave_export.py b/release/scripts/lightwave_export.py index dc14d036503..a109acc02c1 100644 --- a/release/scripts/lightwave_export.py +++ b/release/scripts/lightwave_export.py @@ -26,7 +26,7 @@ Usage:
Supported:
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:
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 === # =================== diff --git a/release/scripts/lightwave_import.py b/release/scripts/lightwave_import.py index 7e479536678..8680890b0ce 100644 --- a/release/scripts/lightwave_import.py +++ b/release/scripts/lightwave_import.py @@ -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" +"Author's homepage, http://www.redrival.com/scorpius", "Author's homepage, http://uaraus.altervista.org") -__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:
- Execute this script from the "File->Import" menu and choose a LightWave -file to open. - -Supported:
- Meshes only. - -Missing:
- Materials, UV Coordinates, and Vertex Color info will be ignored. - -Known issues:
- 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:
- Also reads lwo files in the old LW v5.5 format. -""" - -# $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:
# +---------------------------------------------------------+ # | 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 | +# +---------------------------------------------------------+ -import Blender, meshtools -import struct, chunk, os, cStringIO, time, operator +#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 + +# =========================================================== +# === 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") + global tobj - # === 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 + tobj.logcon ("#####################################################################") + tobj.logcon ("This is: %s" % importername) + tobj.logcon ("Importing file:") + tobj.logcon (filename) + tobj.pprint ("#####################################################################") - objname = os.path.splitext(os.path.basename(filename))[0] + start = time.clock() + file = open(filename, "rb") - 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() + # === 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 - meshtools.print_boxed(message) + 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 + 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) - facev.reverse() - faces.append(facev) - return faces + 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") diff --git a/release/scripts/obj_export.py b/release/scripts/obj_export.py index 205a280f878..c220d66b709 100644 --- a/release/scripts/obj_export.py +++ b/release/scripts/obj_export.py @@ -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) + 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(): - # illum, 0 to disable lightng, 2 is normal. - if mat.getMode() & Material.Modes['SHADELESS']: - file.write('illum 0\n\n') # ignore lighting + # 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) - m.transform(ob.matrix) - - if not m.faces: # Make sure there is somthing to write - continue #dont bother with this mesh. 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 not faces: # Make sure there is somthing to write + continue # dont bother with this mesh. - # Set the default mat - currentMatName = NULL_MAT - currentImgName = NULL_IMG + m.transform(ob.matrix) - file.write('o %s_%s\n' % (ob.getName(), m.name)) # Write Object name + # # Crash Blender + #materials = m.getMaterials(1) # 1 == will return None in the list. + materials = m.getMaterials() + + + 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]) + + + # 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 +''' diff --git a/release/scripts/obj_import.py b/release/scripts/obj_import.py index 268bf44f353..d8788f6d042 100644 --- a/release/scripts/obj_import.py +++ b/release/scripts/obj_import.py @@ -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,51 +63,242 @@ 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 * + + +# 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. + + + + + 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 # #==================================================================================# -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 - - # 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 - return None +# ___ 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) == 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('/') + + # 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 - if len(tmpMatLs) == MATLIMIT: - contextMeshMatIdx = 0 # Use first material - print 'material overflow, attempting to use > 16 materials. defaulting to first.' + if len(uvMapList) > index: + vtIdxLs.append(index) # Seperate UV coords 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)-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) - - 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 + # 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 - 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': - - # 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 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)' + + # 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)' + # 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': - # 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) + # 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() - # 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. + currentUsedVertList = {} + + # Sg is a string + currentSmoothGroup = '(null)' + currentUsedVertListSmoothGroup = VERT_USED_LIST[:] + currentUsedVertList[currentSmoothGroup] = currentUsedVertListSmoothGroup + currentMaterialMeshMapping = {} + + 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) + + 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] + + # 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) + + # 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) - - - 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) - - # Select all imported objects. - for ob in importedObjects: - ob.sel = 1 + # MATERIAL FILE + elif l[0] == 'mtllib': + mtl_fileName.append(' '.join(l[1:]) ) # SHOULD SUPPORT MULTIPLE MTL? + lIdx+=1 - print "obj import time: ", sys.time() - time1 + # Applies material properties to materials alredy on the mesh as well as Textures. + for mtl in mtl_fileName: + load_mtl(DIR, mtl, meshDict, materialDict) - except: - print Draw.PupMenu(ABORT_MENU) - return - - -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') - -# For testing compatibility -''' -TIME = sys.time() -import os -for obj in os.listdir('/obj/'): - if obj.lower().endswith('obj'): - print obj + + importedObjects = [] + for mk, me in meshDict.iteritems(): + nme = me[0] + + # Ignore no vert meshes. + if not nme.verts: # == [] + continue + + name = getUniqueName(mk) + ob = NMesh.PutRaw(nme, name) + ob.name = name + + importedObjects.append(ob) + + # 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 + + 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() - load_obj('/obj/' + obj) + + 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) -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') + +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) + +if __name__ == '__main__': + main() diff --git a/release/scripts/skin.py b/release/scripts/skin.py index d3d1d034db7..adf49b1af7b 100644 --- a/release/scripts/skin.py +++ b/release/scripts/skin.py @@ -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 * +BIG_NUM = 1<<30 -choice = Draw.PupMenu(\ -'Loft-loop - shortest edge method|\ -Loft-loop - even method|\ -Loft-segment - shortest edge|\ -Loft-segment - even method') +global CULL_METHOD +CULL_METHOD = 0 -if choice == 1: - arg='A1' -elif choice == 2: - arg='A2' -elif choice == 3: - arg='B1' -elif choice == 4: - arg='B2' - - - -#================# -# Math functions # -#================# - -# 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 - -#=============================================================# -# 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) +class edgeLoop: + def __init__(self, loop): # Vert loop + # Use next and prev, nextDist, prevDist - dist = measure(vSelLs[0].co, vSelLs[1].co) + # Get Loops centre. + self.centre = Mathutils.Vector() + f = 1.0/len(loop) + for v in loop: + self.centre += v.co * f - if bestDistSoFar == None: - bestDistSoFar = dist - bestFIdxSoFar = fIdx - elif dist < bestDistSoFar: - bestDistSoFar = dist - bestFIdxSoFar = fIdx + + + + # 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: + + n1 = e.v1.co + n2 = e.v2.co + n3 = e.prev.v1.co + + a = n1-n2 + b = n1-n3 + normal1 = Mathutils.CrossVecs(a,b) + normal1.normalize() + + n1 = e.v2.co + n3 = e.next.v2.co + n2 = e.v1.co + + a = n1-n2 + b = n1-n3 + + normal2 = Mathutils.CrossVecs(a,b) + normal2.normalize() + + # Reuse normal1 var + normal1 += normal1 + normal2 + normal1.normalize() + + e.normal = normal1 + #print e.normal + + + + + def backup(self): + # Keep a backup of the edges + self.backupEdges = self.edges[:] + + 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 - # Return the smallest face index of the vloop that was sent - return bestFIdxSoFar + # Removes N Smallest edges and backs up + def removeSmallest(self, cullNum, otherLoopLen): + global CULL_METHOD + if CULL_METHOD == 0: # Shortest edge + + 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. + + else: # CULL METHOD is even + + culled = 0 + + step = int(otherLoopLen / float(cullNum)) + + 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 + + +# 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])\ + ] -#=============================================# -# Take 2 vert loops and skin them # -#=============================================# -def skinVertLoops(mesh, v1loop, v2loop): +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] - #=============================================# - # 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): +# Like vert loops +def getVertLoops(selEdges): + mainVertLoops = [] + while selEdges: + e = selEdges.pop() + contextVertLoop= [e.v1, e.v2] # start the vert loop - # 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]) + eIdx = 1 # Get us into the loop. dummy var. - # 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: - - face = copyFace( mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]] ) - - 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 - - face.v.append( selVertBetween2Faces(\ - mesh.faces[v1loop[clamp(lenVloop, fIdx)]],\ - mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]]) ) - - face.v.append( selVertBetween2Faces(\ - mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]],\ - mesh.faces[v1loop[clamp(lenVloop, fIdx+2)]]) ) - - #face.v.append( selVertBetween2Faces(\ - #mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset +1 ))]],\ - #mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset + 2))]]) ) - - face.v.append( selVertBetween2Faces(\ - mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset))]],\ - mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, fIdx - offset + 1)]]) ) - - mesh.faces.append(face) - - # We need offset to work out how much smaller v2loop is at this current index. - offset+=1 - - - 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)]]) ) - - face.v.append( selVertBetween2Faces(\ - mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]],\ - mesh.faces[v1loop[clamp(lenVloop, fIdx+2)]]) ) - - face.v.append( selVertBetween2Faces(\ - mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset +1 ))]],\ - mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset + 2))]]) ) - - face.v.append( selVertBetween2Faces(\ - mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset))]],\ - mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, fIdx - offset + 1)]]) ) - - mesh.faces.append(face) - - 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] - - 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 + # 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 - - - - -#================================================================# -# 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 - - -#==============================# -# 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 - break - fIdx+=1 - - # 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) - - - -#=================================================# -# 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: - - # In future ignore all faces in this vert loop - fIgLs += allVLoops[-1] - - # Add the new vert loop to the list - allVLoops.append( findVertLoop(mesh, fIgLs) ) - - return allVLoops[:-1] # Remove the last Value- None. - - -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 + # 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: - 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 + # None found? Keep looking + continue - vLoopIdx +=1 + # 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 + + +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. - return newOrderVLoops, vLoopIdxNotToSkin + # 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 + + if totEloopDist < bestEloopDist: + bestOffset = offset + bestEloopDist = totEloopDist + + # 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() -is_editmode = Window.EditMode() -if is_editmode: Window.EditMode(0) +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 + + 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)) + + + # 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) ) + + # 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 + + 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) -# 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) - -if is_editmode: Window.EditMode(1) -Window.WaitCursor(0) -print "skinning time: %.2f" % (sys.time() - time1) +main() diff --git a/release/scripts/tex2uvbaker.py b/release/scripts/tex2uvbaker.py index 0d0f4dfe065..b4a9d9bc188 100644 --- a/release/scripts/tex2uvbaker.py +++ b/release/scripts/tex2uvbaker.py @@ -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);
-b) Define its materials and textures;
+a) Enter face mode and define uv coordinates for your mesh;
+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.
-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:
- This script was based on a suggestion by Martin (Theeth) Poirier. + This script was based on a suggestion by Martin (Theeth) Poirier;
""" #--------------------------------------------- -# 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:
# # 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] @@ -419,7 +442,7 @@ def Mesh2UVCoord (LIMIT): """ global PUTRAW, FRAME, SCENELAYERS - try: + try : MESH3D = Object.GetSelected()[0] if MESH3D.getType() == 'Mesh': MESH = MESH3D.getData() @@ -444,7 +467,7 @@ def Mesh2UVCoord (LIMIT): v1.co[2] = 0.0 MESH2.verts.append(v1) f1.v.append(MESH2.verts[len(MESH2.verts) - 1]) - + MESH2.faces.append(f1) f1.uv = f.uv[:] f1.col = f.col[:] @@ -464,6 +487,7 @@ def Mesh2UVCoord (LIMIT): NewOBJECT.setLocation (OBJPOS, OBJPOS, 0.0) NewOBJECT.setEuler (0.0, 0.0, 0.0) + MESH2.removeAllKeys() MESH2.update() MESH2.insertKey (1, 'absolute') @@ -471,7 +495,7 @@ def Mesh2UVCoord (LIMIT): imagename = 'uvtext' - name = "CHANGE IMAGE NAME ? %t | Replace it | No replacing | Script help" + name = "CHANGE IMAGE NAME ? %t | Replace it | No replace | Script help" result = Draw.PupMenu(name) if result == 1: @@ -501,18 +525,17 @@ def Mesh2UVCoord (LIMIT): Blender.Redraw() else: - Blender.ShowHelp('tex2uvbaker.py') - #Draw.PupMenu("Ready%t|Please check console for instructions") - if DEBUG: print helpmsg + Draw.PupMenu("Ready%t|Please check console for instructions") + print helpmsg else: - name = "ERROR: active object is not a mesh or has no UV coordinates" + name = "Error%t|Active object is not a mesh or has no UV coordinates" result = Draw.PupMenu(name) print 'problem : no object selected or not mesh' except: - name = "ERROR: active object is not a mesh or has no UV coordinates" + name = "Error%t|Active object is not a mesh or has no UV coordinates" result = Draw.PupMenu(name) print 'problem : no object selected or not mesh' -Mesh2UVCoord(LIMIT) +Mesh2UVCoord(LIMIT) diff --git a/release/scripts/UVpaint05.py b/release/scripts/uvpaint.py similarity index 51% rename from release/scripts/UVpaint05.py rename to release/scripts/uvpaint.py index 5217d942109..357c2d94aed 100644 --- a/release/scripts/UVpaint05.py +++ b/release/scripts/uvpaint.py @@ -11,7 +11,7 @@ __author__ = "Jean-Michel Soler (jms)" __url__ = ("blender", "elysiun", "Script's homepage, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_uvpainting.htm", "Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender") -__version__ = "0.5 05/2004" +__version__ = "0.8 08/2005" __bpydoc__ = """\ This script "paints" uv-mappings with the model's vertex colors. @@ -33,6 +33,29 @@ Notes:
the "Make" VColors button in the edit mesh buttons win, the current light setup is saved as vertex colors for the model;
Check the script's homepage for example images. + +Short keys Documentation + +KEYS +M : dipslay GUI Menu +D : Set/Unset Documentation +S : Save current window content + in a tga file +Q or ESC : Exit +T : Set/Unset Transparency +L : Set/Unset lines +E : Set/Unset outline +B : Set lines color to Black +W : Set lines color to white +ARROW : displace model on + UP/DOWN/LEFT/RIGHT side +PADPLUS : increase ZOOM +PADMINUS : decrease ZOOM +HOME : cancel display modifs + +Mouse button +RIGHTMOUSE : same as arrows + """ # $Id$ @@ -99,18 +122,87 @@ def exist(path): return 0 return 1 -loc0= Blender.sys.dirname(Blender.Get ("filename")) -loc2=loc0+Blender.sys.dirsep+'test00.tga' +loc0= Blender.Get ("filename").replace('\\','/') +loc0=loc0[:loc0.rfind('/')] + +loc2=loc0+'/'+'test00.tga' + + +mouse_x,mouse_y=0,0 +mouse_xr=1 +mouse_yr=1 +POS=[0,0] +ANC=[0,0] +XY=[0,0] +size=[] +sel=0 +X,Y=0,0 +TRANSP,EMPTY,DOCU=0,0,0 +MENU, SAVE =1,0 glCr=glRasterPos2d glCl3=glColor3f glCl4=glColor4f glRct=glRectf +LC=1.0 + xlimit=0 selmatlist=[] +LIM=[0.0,0.0,0.0,0.0] +NOLIM=1 +if not NOLIM : LIM=[-1.0,1.0,-1.0,1.0] -def triangle(a,b,c): +TR=0.8 + +def Doc(size): + S0,S1=40,50 + + a=[S0,size[3]-S1, .8,.8,.8] + b=[S0*7,size[3]-S1, .8,0.8,0.8] + c=[S0*7,size[3]-S1*7, 0.8,0.8,0.8] + d=[S0,size[3]-S1*7, 0.8,0.8,0.8] + Tcarre(a,b,c,d,0.8) + Lcarre(a,b,c,d,0.0) + DOC=[' ', +'Documentation', +' ', +'KEYS ', +'M : dipslay GUI Menu', +'D : Set/Unset Documentation', +'S : Save current window content', +' in a tga file', +'Q or ESC : Exit', +'T : Set/Unset Transparency', +'L : Set/Unset lines', +'E : Set/Unset outline', +'B : Set lines color to Black ', +'W : Set lines color to white', +'ARROW : displace model on ', +' UP/DOWN/LEFT/RIGHT side' , +'PADPLUS : increase ZOOM ', +'PADMINUS : decrease ZOOM ', +'HOME : cancel display modifs', +' ', +'Mouse button', +'RIGHTMOUSE : same as arrows', +] + glColor3f(0.0,0.0,0.0) + for D in DOC : + glRasterPos2f(S0+8, size[3]-S1-13*DOC.index(D)) + Text(D) + +def Ttriangle(a,b,c): + glBegin(GL_TRIANGLES); + glColor4f(a[2],a[3],a[4],TR) + glVertex2f(a[0],a[1]); + glColor4f(b[2],b[3],b[4],TR) + glVertex2f(b[0],b[1]); + glColor4f(c[2],c[3],c[4],TR) + glVertex2f(c[0],c[1]); + glEnd(); + +def Ftriangle(a,b,c): glBegin(GL_TRIANGLES); glColor3f(a[2],a[3],a[4]) glVertex2f(a[0],a[1]); @@ -120,33 +212,46 @@ def triangle(a,b,c): glVertex2f(c[0],c[1]); glEnd(); -def Ltriangle(a,b,c): - glBegin(GL_LINES); - glColor3f(1.0,1.0,1.0) - glVertex2f(a[0],a[1]); - glVertex2f(b[0],b[1]); - 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); + glColor4f(LC,LC,LC,TR+0.2) + glVertex2f(TL[v][0],TL[v][1]); + glVertex2f(TL[v+1][0],TL[v+1][1]); + glEnd(); -def carre(a,b,c,d): - triangle(a,b,c) - triangle(a,c,d) -def Lcarre(a,b,c,d): - glBegin(GL_LINES); - glColor3f(1.0,1.0,1.0) - glVertex2f(a[0],a[1]); - glVertex2f(b[0],b[1]); - glVertex2f(c[0],c[1]); - glVertex2f(d[0],d[1]); - glEnd(); +def Tcarre(a,b,c,d,LC=1.0): + Ttriangle(a,b,c) + Ttriangle(a,c,d) +def Fcarre(a,b,c,d,LC=1.0): + Ftriangle(a,b,c) + Ftriangle(a,c,d) + +def Lcarre(a,b,c,d,LC=0.5): + TL=[a,b,c,d,a] + for v in [0,1,2,3] : + glBegin(GL_LINES); + glColor4f(LC,LC,LC,TR+0.2) + glVertex2f(TL[v][0],TL[v][1]); + glVertex2f(TL[v+1][0],TL[v+1][1]); + glEnd(); -def transface(f,x,y): - global xlimit +def transface(f,x,y,u=0.0, v=0.0): + global xlimit, LIM + global mouse_xr,sel, ANC, X,Y + global mouse_yr, POS, XY,size + global mouse_x, mouse_y - + mouse_x=mouse_xr-size[0] + mouse_y=mouse_yr-size[1] + + if sel==1: + POS=[mouse_x-ANC[0],mouse_y-ANC[1]] + u,v=POS a=[0,0,0.0, 0.0,0.0,0.0] b=[0,0,0.0, 0.0,0.0,0.0] @@ -154,8 +259,8 @@ def transface(f,x,y): d=[0,0,0.0, 0.0,0.0,0.0] if len(f.v)>=3: - a[0]=int(f.uv[0][0]*x) - a[1]=int(f.uv[0][1]*y) + a[0]=int((f.uv[0][0]-LIM[1])*x+u) + a[1]=int((f.uv[0][1]-LIM[3])*y+v) if a[0]>xlimit: xlimit=a[0] @@ -164,8 +269,8 @@ def transface(f,x,y): a[3]=f.col[0].g/255.0 a[4]=f.col[0].b/255.0 - c[0]=int(f.uv[2][0]*x) - c[1]=int(f.uv[2][1]*y) + c[0]=int((f.uv[2][0]-LIM[1])*x+u) + c[1]=int((f.uv[2][1]-LIM[3])*y+v) if c[0]>xlimit: xlimit=c[0] @@ -175,8 +280,8 @@ def transface(f,x,y): c[4]=f.col[2].b/255.0 - b[0]=int(f.uv[1][0]*x) - b[1]=int(f.uv[1][1]*y) + b[0]=int((f.uv[1][0]-LIM[1])*x+u) + b[1]=int((f.uv[1][1]-LIM[3])*y+v) if b[0]>xlimit: xlimit=b[0] @@ -187,8 +292,8 @@ def transface(f,x,y): if len(f.v)==4: - d[0]=int(f.uv[3][0]*x) - d[1]=int(f.uv[3][1]*y) + d[0]=int((f.uv[3][0]-LIM[1])*x+u) + d[1]=int((f.uv[3][1]-LIM[3])*y+v) if d[0]>xlimit: xlimit=d[0] @@ -214,44 +319,53 @@ def extract_faces(me,MENU): return listf def affiche_mesh(ME,x,y): - global LINE,xlimit,MMENU,XLIMIT,xwin,xlimit - + global LINE,xlimit,MMENU,XLIMIT,xwin,xlimit,LC + global LIM, EMPTY,TRANSP + if not NOLIM : LIM=[-1.0,1.0,-1.0,1.0] + if ME.getType()=='Mesh': - me=GetRaw(ME.getData().name) - + me=ME.getData() if MMENU.val==1: se=me.faces - elif MMENU.val==3: - se=me.getSelectedFaces() - + se=[s for s in me.faces if s in me.getSelectedFaces() or s.sel] elif MMENU.val==2: - se=extract_faces(me,2) - + se=extract_faces(me,2) + if not NOLIM : + for s in se: + for u in s.uv: + if u[0] >LIM[0] : LIM[0]=u[0] + if u[0] LIM[2] : LIM[2]=u[1] + if u[1] ndimensions; i++) - bufsize *= bgl_buffer5->dimensions[i]; - - if (bgl_var1*bgl_var2 > bufsize) - return EXPP_ReturnPyObjError(PyExc_AttributeError, - "pixel buffer size can't be smaller than drawing rect area"); - - glDrawPixels (bgl_var1, bgl_var2, bgl_var3, bgl_var4, - (bgl_buffer5)->buf.asvoid); - - return EXPP_incr_ret(Py_None); -} - BGL_Wrap(2, Accum, void, (GLenum, GLfloat)) BGL_Wrap(2, AlphaFunc, void, (GLenum, GLclampf)) @@ -511,7 +484,7 @@ BGL_Wrap(1, DepthMask, void, (GLboolean)) BGL_Wrap(2, DepthRange, void, (GLclampd, GLclampd)) BGL_Wrap(1, Disable, void, (GLenum)) BGL_Wrap(1, DrawBuffer, void, (GLenum)) -/*BGL_Wrap(5, DrawPixels, void, (GLsizei, GLsizei, GLenum, GLenum, GLvoidP))*/ +BGL_Wrap(5, DrawPixels, void, (GLsizei, GLsizei, GLenum, GLenum, GLvoidP)) BGL_Wrap(1, EdgeFlag, void, (GLboolean)) BGL_Wrap(1, EdgeFlagv, void, (GLbooleanP)) BGL_Wrap(1, Enable, void, (GLenum))