- 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.
This commit is contained in:
Willian Padovani Germano
2005-10-11 02:32:58 +00:00
parent 93a4f6a876
commit b970eadedf
12 changed files with 3994 additions and 1553 deletions

View File

@@ -18,11 +18,16 @@ that points to its official homepage, with news, downloads and documentation.
Usage:<br>
Type your code and hit "Enter" to get it executed.<br>
- Right mouse click: Console Menu (Save output, etc);<br>
- Mousewheel: Scroll text
- Arrow keys: command history and cursor;<br>
- Shift + arrow keys: jump words;<br>
- Shift + Backspace: Backspace whole word;<br>
- Shift + Arrow keys: jump words;<br>
- Ctrl + (+/- or mousewheel): Zoom text size;<br>
- Ctrl + Tab: auto compleate based on variable names and modules loaded -- multiple choices popup a menu;<br>
- Ctrl + Enter: multiline functions -- delays executing code until only Enter is pressed.
"""
__author__ = "Campbell Barton AKA Ideasman"
__url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
import Blender
from Blender import *
@@ -31,14 +36,18 @@ import StringIO
import types
# Constants
__DELIMETERS__ = '. ,=+-*/%<>&~][{}():'
__LINE_HISTORY__ = 200
__DELIMETERS__ = '. ,=+-*/%<>&~][{}():\t'
__VARIABLE_DELIMETERS__ = ' ,=+-*/%<>&~{}():\t'
__LINE_HISTORY__ = 500
global __LINE_HEIGHT__
__LINE_HEIGHT__ = 14
global __FONT_SIZE__
__FONT_SIZE__ = "normal"
__FONT_SIZES__ = ( ('tiny', 10), ('small', 12), ('normal', 14), ('large', 16) )
__FONT_SIZE__ = 2 # index for the list above, normal default.
global __CONSOLE_LINE_OFFSET__
__CONSOLE_LINE_OFFSET__ = 0
'''
# Generic Blender functions
@@ -52,10 +61,93 @@ def getActScriptWinRect():
return None
'''
class cmdLine:
# cmd: is the command string, or any other message
# type: 0:user input 1:program feedback 2:error message. 3:option feedback
# exe; 0- not yet executed 1:executed
# menuText, # per group
def PupMenuLess(menu, groupSize=35):
more = [' more...']
less = [' less...']
menuList= menu.split('|')
# No Less Needed, just call.
if len(menuList) < groupSize:
return Draw.PupMenu(menu)
title = menuList[0].split('%t')[0]
# Split the list into groups
menuGroups = [[]]
for li in menuList[1:]:
if len(menuGroups[-1]) < groupSize:
menuGroups[-1].append(li)
else:
menuGroups.append([li])
# Stores teh current menu group we are looking at
groupIdx = 0
while 1:
# Give us a title with the menu number
numTitle = [ ' '.join([title, str(groupIdx + 1), 'of', str(len(menuGroups)), '%t'])]
if groupIdx == 0:
menuString = '|'.join(numTitle + menuGroups[groupIdx] + more)
elif groupIdx == len(menuGroups)-1:
menuString = '|'.join(numTitle + less + menuGroups[groupIdx])
else: # In the middle somewhere so Show a more and less
menuString = '|'.join(numTitle + less + menuGroups[groupIdx] + more)
result = Draw.PupMenu(menuString)
# User Exit
if result == -1:
return -1
if groupIdx == 0: # First menu
if result-1 < groupSize:
return result
else: # must be more
groupIdx +=1
elif groupIdx == len(menuGroups): # Last Menu
if result == 1: # Must be less
groupIdx -= 1
else: # Must be a choice
return result + (groupIdx*groupSize)
else:
if result == 1: # Must be less
groupIdx -= 1
elif result-2 == groupSize:
groupIdx +=1
else:
return result - 1 + (groupIdx*groupSize)
def unzip(list):
"""
unzip: inverse of zip - converts a list of tuples into a tuple of lists
e.g.
a,b = unzip(zip(a,b))
* note: all tuples in list have to have the same length, if not,
this function will fail
"""
if len(list) == 0: return ()
l = []
for t in range(len(list[0])):
l.append(map( lambda x,t=t: x[t], list ))
return tuple(l)
# Use newstyle classes, Im not bothering with inheretence
# but slots are faster.
class cmdLine(object):
__slots__ = [\
'cmd', # is the command string, or any other message
'type',# type: 0:user input 1:program feedback 2:error message. 3:option feedback
'exe' # 0- not yet executed 1:executed
]
def __init__(self, cmd, type, exe):
self.cmd = cmd
self.type = type
@@ -97,32 +189,68 @@ def insertCmdData(cmdBuffer):
COLLECTED_VAR_NAMES = {} # a list of keys, each key has a list of absolute paths
# Pain and simple recursice dir(), accepts a string
def rdir(dirString):
def rdir(dirString, depth=0):
# MAX DEPTH SET HERE
if depth > 4:
print 'maxdepoth reached.'
return
global COLLECTED_VAR_NAMES
dirStringSplit = dirString.split('.')
exec('dirList = dir(%s)' % dirString)
for dirItem in dirList:
if not dirItem.startswith('_'):
if dirItem not in COLLECTED_VAR_NAMES.keys():
COLLECTED_VAR_NAMES[dirItem] = []
if dirItem.startswith('_'):
continue
# Add the string
splitD = dirString.split('"')[-2]
if splitD not in COLLECTED_VAR_NAMES[dirItem]:
COLLECTED_VAR_NAMES[dirItem].append(splitD)
# Stops recursice stuff, overlooping
if type(dirItem) == types.ClassType or \
type(dirItem) == types.ModuleType:
print dirString, splitD, dirItem
# Dont loop up dirs for strings ints etc.
if d not in dirStringSplit:
rdir( '%s.%s' % (dirString, d))
dirData = None
try:
# Rare cases this can mess up, material.shader was a problem.
exec('dirData = %s.%s' % (dirString, dirItem))
#print dirData
except:
# Dont bother with this data.
continue
if type(dirItem) != type('str'):
print dirItem, type(dirItem)
if dirItem not in COLLECTED_VAR_NAMES.keys():
COLLECTED_VAR_NAMES[dirItem] = []
# Add the string
splitD = dirString.split('"')[-2]
if splitD not in COLLECTED_VAR_NAMES[dirItem]:
COLLECTED_VAR_NAMES[dirItem].append(splitD)
# Stops recursice stuff, overlooping
#print type(dirItem)
#if type(dirData) == types.ClassType or \
# type(dirData) == types.ModuleType:
if type(dirData) != types.StringType and\
type(dirData) != types.DictType and\
type(dirData) != types.DictionaryType and\
type(dirData) != types.FloatType and\
type(dirData) != types.IntType and\
type(dirData) != types.NoneType and\
type(dirData) != types.StringTypes and\
type(dirData) != types.TypeType and\
type(dirData) != types.TupleType and\
type(dirData) != types.BuiltinFunctionType:
# print type(dirData), dirItem
# Dont loop up dirs for strings ints etc.
if dirItem not in dirStringSplit:
rdir( '%s.%s' % (dirString, dirItem), depth+1)
'''
elif depth == 0: # Add local variables
# print type(dirData), dirItem
# Dont loop up dirs for strings ints etc.
if dirItem not in dirStringSplit:
rdir( '%s.%s' % (dirString, dirItem), depth+1)
'''
def recursive_dir():
global COLLECTED_VAR_NAMES
@@ -148,13 +276,17 @@ def runUserCode(__USER_CODE_STRING__):
# Try and run the user entered line(s)
try:
# Load all variabls from global dict to local space.
for __TMP_VAR_NAME__ in __CONSOLE_VAR_DICT__.keys():
exec('%s%s%s%s' % (__TMP_VAR_NAME__,'=__CONSOLE_VAR_DICT__["', __TMP_VAR_NAME__, '"]'))
for __TMP_VAR_NAME__, __TMP_VAR__ in __CONSOLE_VAR_DICT__.items():
exec('%s%s' % (__TMP_VAR_NAME__,'=__TMP_VAR__'))
del __TMP_VAR_NAME__
del __TMP_VAR__
# Now all the vars are loaded, execute the code. # Newline thanks to phillip,
exec(compile(__USER_CODE_STRING__, 'blender_cmd.py', 'single')) #exec(compile(__USER_CODE_STRING__, 'blender_cmd.py', 'exec'))
# Flush global dict, allow the user to remove items.
__CONSOLE_VAR_DICT__ = {}
# Write local veriables to global __CONSOLE_VAR_DICT__
for __TMP_VAR_NAME__ in dir():
if __TMP_VAR_NAME__ != '__FILE_LIKE_STRING__' and\
@@ -167,14 +299,16 @@ def runUserCode(__USER_CODE_STRING__):
del __TMP_VAR_NAME__
except: # Prints the REAL exception.
error = str(python_sys.exc_value)
error = '%s: %s' % (python_sys.exc_type, python_sys.exc_value)
for errorLine in error.split('\n'):
cmdBuffer.append(cmdLine(errorLine, 2, None)) # new line to type into
python_sys.stdout = __STD_OUTPUT__ # Go back to output to the normal blender console
# Copy all new output to cmdBuffer
__FILE_LIKE_STRING__.seek(0) # the readline function requires that we go back to the start of the file.
for line in __FILE_LIKE_STRING__.readlines():
cmdBuffer.append(cmdLine(line, 1, None))
@@ -203,34 +337,61 @@ def handle_event(evt, val):
#------------------------------------------------------------------------------#
def actionEnterKey():
global histIndex, cursor, cmdBuffer
# Check for the neter kay hit
if Window.GetKeyQualifiers() & Window.Qual.CTRL: # HOLDING DOWN SHIFT, GO TO NEXT LINE.
cmdBuffer.append(cmdLine(' ', 0, 0))
else:
def getIndent(string):
# Gather white space to add in the previous line
# Ignore the last char since its padding.
whiteSpace = ''
#for i in range(len(cmdBuffer[-1].cmd)):
for i in range(len(string)-1):
if cmdBuffer[-1].cmd[i] == ' ' or cmdBuffer[-1].cmd[i] == '\t':
whiteSpace += string[i]
else:
break
return whiteSpace
# Are we in the moddle of a multiline part or not?
# try be smart about it
if cmdBuffer[-1].cmd.split('#')[0].rstrip().endswith(':'):
# : indicates an indent is needed
cmdBuffer.append(cmdLine('\t%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
print ': indicates an indent is needed'
elif cmdBuffer[-1].cmd[0] in [' ', '\t'] and len(cmdBuffer[-1].cmd) > 1 and cmdBuffer[-1].cmd.split():
# white space at the start means he havnt finished the multiline.
cmdBuffer.append(cmdLine('%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
print 'white space at the start means he havnt finished the multiline.'
elif Window.GetKeyQualifiers() & Window.Qual.CTRL:
# Crtl forces multiline
cmdBuffer.append(cmdLine('%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
print 'Crtl forces multiline'
else: # Execute multiline code block
# Multiline code will still run with 1 line,
multiLineCode = ['if 1:']
if cmdBuffer[-1].cmd != ' ':
multiLineCode = ['%s%s' % (' ', cmdBuffer[-1].cmd)] # added space for fake if.
else:
cmdBuffer[-1].type = 1
multiLineCode = []
cmdBuffer[-1].exe = 1
i = 2
multiLineCode = ['if 1:'] # End of the multiline first.
# Seek the start of the file multiline
i = 1
while cmdBuffer[-i].exe == 0:
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('<br>'):
cmdBuffer.append( cmdLine(l, 1, None) )
histIndex = cursor = -1 # How far back from the first letter are we? - in current CMD line, history if for moving up and down lines.
# Autoexec, startup code.
console_autoexec = '%s%s' % (Get('datadir'), '/console_autoexec.py')
scriptDir = Get('scriptsdir')
if not scriptDir.endswith(Blender.sys.sep):
scriptDir += Blender.sys.sep
console_autoexec = '%s%s' % (scriptDir, 'console_autoexec.py')
if not sys.exists(console_autoexec):
# touch the file
cmdBuffer.append(cmdLine('...console_autoexec.py not found, making new in scripts dir', 1, None))
open(console_autoexec, 'w').close()
cmdBuffer.append(cmdLine('...console_autoexec.py not found, making new in scripts data dir', 1, None))
else:
cmdBuffer.append(cmdLine('...Using existing console_autoexec.py in scripts data dir', 1, None))
cmdBuffer.append(cmdLine('...Using existing console_autoexec.py in scripts dir', 1, None))
#-Autoexec---------------------------------------------------------------------#
# Just use the function to jump into local naming mode.
# This is so we can loop through all of the autoexec functions / vars and add them to the __CONSOLE_VAR_DICT__
def autoexecToVarList():
def include_console(includeFile):
global __CONSOLE_VAR_DICT__ # write autoexec vars to this.
# Execute an external py file as if local
exec(include(console_autoexec))
exec(include(includeFile))
# Write local to global __CONSOLE_VAR_DICT__ for reuse,
for __TMP_VAR_NAME__ in dir() + dir(Blender):
# Execute the local > global coversion.
exec('%s%s' % ('__CONSOLE_VAR_DICT__[__TMP_VAR_NAME__]=', __TMP_VAR_NAME__))
autoexecToVarList() # pass the blender module
include_console(console_autoexec) # pass the blender module
#-end autoexec-----------------------------------------------------------------#

View File

@@ -2,7 +2,7 @@
""" Registration info for Blender menus: <- these words are ignored
Name: 'Dispaint'
Blender: 233
Blender: 237
Group: 'Mesh'
Tip: 'use vertex paint color value to modify shape displacing vertices along normal'
"""
@@ -11,7 +11,7 @@ __author__ = "Jean-Michel Soler (jms)"
__url__ = ("blender", "elysiun",
"Script's homepage, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_displacementpainting.htm",
"Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
__version__ = "233i"
__version__ = "237"
__bpydoc__ = """\
This script displaces mesh vertices according to vertex color values.
@@ -81,6 +81,7 @@ from Blender.Draw import *
from Blender.BGL import *
from Blender.Noise import *
from Blender.Scene import *
from Blender.Window import *
sc=Scene.getCurrent()
# niveau du deplacement
@@ -117,6 +118,7 @@ E_AXESELX = 46
E_AXESELY = 47
E_AXESELZ = 48
E_NOISEME = 49
E_NOISEH = 50
E_NOISELAC = 51
@@ -126,10 +128,28 @@ E_NOISEBAS = 54
E_NOISEVAL=[E_NOISEH,E_NOISELAC,E_NOISEOCT,E_NOISEOFF,E_NOISEBAS]
E_NOISEDIM = 55
E_GETCOLORS = 56
E_UVCOLORS = 57
E_SAVECOLORS = 58
B_SAVECOLORS = 0
E_RESTCOLORS = 60
V_RESTCOL=0
F_RESTCOL=0
BUF_COLORS=[]
RVBA_VALUE=61
RVBA_VERTICES=62
RVBA_FACES=63
ExitTIP="Exit from this script session "
CreateTIP="Create a new copy of the selected shape"
ActionTIP="Do the current selected actions"
UVCOLORSTIP="Get colrs from first available UV image "
GETCOLORSTIP="Get color from textures "
REPEATTIP="Replay the same action with new values ."
def copy_transform(ozero,Obis):
Obis.setSize(ozero.getSize());
@@ -298,9 +318,23 @@ def DOCMat_list(TMATList):
TMATList[0]=0
return TMATList
MOname = "MODE MENU %t|Normal %x1|Material %x2|Selected %x3"
MOname = "MODE MENU %t|Normal %x1|Material %x2|Selected %x3| Find color %x4"
MOname_doc=["",
"Displace all vertices",
"Displace vertices only on selected materials . ",
"Displace only selected vertices .",
"Try to find and set selected the vertices with this color."]
ORname = "ORIENT MENU %t|From Normal %x1|Local Axes %x2| Noise %x3"
NOname = "NOISE MENU %t|BLENDER %x1|STDPERLIN %x2|NEWPERLIN %x3|VORONOI_F1%x4|VORONOI_F2%x5|VORONOI_F3%x6|VORONOI_F4%x7|VORONOI_F2F1%x8|VORONOI_CRACKLE%x9|CELLNOISE%x10|HETEROTENOISE%x11"
ORname_doc=["",
"Use normal orientation to calculate displacement",
"Use selected axes value to calculate displacement",
"Blend the color value with Nosie values to calculate the displacement"]
NOname = "NOISE MENU %t|BLENDER %x1|STDPERLIN %x2|\
NEWPERLIN %x3|VORONOI_F1%x4|VORONOI_F2%x5|\
VORONOI_F3%x6|VORONOI_F4%x7|VORONOI_F2F1%x8|\
VORONOI_CRACKLE%x9|CELLNOISE%x10|HETEROTENOISE%x11"
MODEMenu = Create(1)
ORIENTMenu = Create(1)
@@ -351,48 +385,190 @@ glCl3=glColor3f
glCl4=glColor4f
glRct=glRectf
def triangle(a,b,c):
glBegin(GL_TRIANGLES);
glColor3f(a[2],a[3],a[4])
glVertex2f(a[0],a[1]);
glVertex2f(b[0],b[1]);
glVertex2f(c[0],c[1]);
glEnd();
def triangleFcolor(a,b,c):
glBegin(GL_TRIANGLES);
glColor4f(a[2],a[3],a[4],a[5])
glVertex2f(a[0],a[1]);
glColor4f(b[2],b[3],b[4],a[5])
glVertex2f(b[0],b[1]);
glColor4f(c[2],c[3],c[4],a[5])
glVertex2f(c[0],c[1]);
glEnd();
def Ltriangle(a,b,c,LC=0.5):
TL=[a,b,c,a]
for v in [0,1,2] :
glBegin(GL_LINES);
glColor3f(LC,LC,LC)
glVertex2f(TL[v][0],TL[v][1]);
glVertex2f(TL[v+1][0],TL[v+1][1]);
glEnd();
def carreFcolor(a,b,c,d):
triangleFcolor(a,b,c)
triangleFcolor(a,c,d)
RVBA=[Create(255),Create(255),Create(255),Create(255),Create(0)]
# _*_ p1
# _/ \_
# _/ \_
# / \_
# p0*_ /* p2
# | \_ _/ |
# | \_ _/ |
# | \_ _/ |
# | * p3 |
# | | |
# *_ | /* p4
# p6 \_ | _/
# \_ | _/
# \_|_/
# * p5
def flatcolorcube(r,g,b,a,m,x,y):
h0=60
v0=40
A=[x, y, (r-m)/255.0,g/255.0,b/255.0,a/255.0] #p0
B=[x+h0,y-v0, r/255.0,g/255.0,b/255.0,a/255.0] #p3
c=[x+h0*2,y, r/255.0, g/255.0, (b-m)/255.0,a/255.0] #p2
d=[x+h0,y+v0, (r-m)/255.0,g/255.0,(b-m)/255.0,a/255.0] #p1
carreFcolor(A,B,c,d)
A=[x,y,(r-m)/255.0,g/255.0,b/255.0,a/255.0] #p0
B=[x+h0,y-v0,r/255.0,g/255.0,b/255.0,a/255.0] #p3
c=[x+h0,y-v0*2.5, r/255.0, (g-m)/255.0, b/255.0,a/255.0] #p5
d=[x,y-v0*1.5,(r-m)/255.0,(g-m)/255.0,b/255.0,a/255.0] #p6
carreFcolor(A,B,c,d)
d=[x+h0,y-v0,r/255.0,g/255.0,b/255.0,a/255.0] #p3
A=[x+h0*2,y,r/255.0,g/255.0,(b-m)/255.0,a/255.0] #p2
B=[x+h0*2,y-v0*1.5, r/255.0, (g-m)/255.0,(b-m)/255.0,a/255.0] #p4
c=[x+h0,y-v0*2.5,r/255.0,(g-m)/255.0,b/255.0,a/255.0] #p5
carreFcolor(A,B,c,d)
def col_egal2col(col,RVBA):
eps=RVBA[4].val
if ( (RVBA[0].val-col[0]>=0 and RVBA[0].val-col[0]<=eps) and
(RVBA[1].val-col[1]>=0 and RVBA[1].val-col[1]<=eps) and
(RVBA[2].val-col[2]>=0 and RVBA[2].val-col[2]<=eps) and
(RVBA[3].val-col[3]>=0 and RVBA[3].val-col[3]<=eps) ) :
#print 'ok',col, [RVBA[n].val-col[n] for n in 0,1,2,3]
return 1
else:
#print 'not',col, [RVBA[n].val-col[n] for n in 0,1,2,3]
return 0
def select_bycolors(TYPE,RVBA):
global RVBA_VERTICES, RVBA_FACES
SEL = Blender.NMesh.FaceFlags['SELECT']
try:
ME=Blender.Scene.getCurrent().getActiveObject().getData()
VC={}
for f in ME.faces:
for v in f.v:
try:
VC[v].append(f)
except:
VC[v]=[f]
#print '.',
for C in VC.iteritems():
color=[0,0,0]
for f in C[1]:
col=f.col[f.v.index(C[0])]
col=[col.r,col.g,col.b,col.a]
if col_egal2col(col,RVBA):
if TYPE== RVBA_VERTICES:
C[0].sel=1
else:
f.sel=1
f.flag |= SEL
#VC[C[0]].append(color[:])
ME.update()
except:
pass
def draw():
global MODEMenu, NSIZE, TDOCMat,TMATList, TAXEList
global mat, ORIName, NEWName, ORIENTMenu
global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu, NOISEDIMbout,NOISEDIM
global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu
global NOISEDIMbout,NOISEDIM, RVBA,RVB_VALUE, RVBA_VERTICES
global HBout,lacunarityBout,octavesBout,offsetBout,basisBout
global noiseTYPE, ExitTIP, CreateTIP, ActionTIP
global noiseTYPE, ExitTIP, CreateTIP, ActionTIP, E_GETCOLORS
global E_UVCOLORS, UVCOLORSTIP, GETCOLORSTIP, REPEATTIP,RVBA_FACES
global E_SAVECOLORS, B_SAVECOLORS, E_RESTCOLORS, MOname_doc, ORname_doc
size=Buffer(GL_FLOAT, 4)
glGetFloatv(GL_SCISSOR_BOX, size)
size= size.list
for s in [0,1,2,3]: size[s]=int(size[s])
glClearColor(0.72,0.72,0.72,1.0)
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0.0,0.0,0.0)
glRectf(4,size[3],534,size[3]-32 )
glColor3f(0.66,0.66,0.66)
glRectf(4,size[3]-4,404,size[3]-32 )
glColor3f(0.76,0.76,0.76)
glRectf(4,size[3]-32,404,size[3]-294 )
triangle([4+9,size[3],0.72,0.72,0.72],
[4,size[3],],
[4,size[3]-9])
triangle([404-9,size[3],0.72,0.72,0.72],
[404,size[3],],
[404,size[3]-9])
triangle([404,size[3]-294,.72,0.72,0.72],
[404,size[3]-294+9,],
[404-9,size[3]-294])
triangle([4,size[3]-294,.72,0.72,0.72],
[4,size[3]-294+9,],
[4+9,size[3]-294])
glColor3f(1.0,1.0,1.0)
glRasterPos2f(20, size[3]-15)
Text("Script Python de displacement paintingt")
Text("Script Python de displacement painting")
glRasterPos2f(20, size[3]-28)
Text("Jean-michel Soler, juillet 2004")
Text("Jean-michel Soler, Aout 2005")
n0=70
n1=55
if MODEMenu.val<4 :
Button("Create" ,E_CREATE ,5 ,size[3]-n0+11 ,60 ,20,CreateTIP)
Button("Action" ,E_ACTION ,5 ,size[3]-n0-11 ,60 ,20,ActionTIP)
NRepeat=Number("repeat" ,E_REPEAT ,5 ,size[3]-n0-56 ,75 ,20, NRepeat.val,1,10,REPEATTIP)
Button("Exit" ,E_EXIT ,5 ,size[3]-n0-32 ,60 ,20,ExitTIP)
Button("Tex colors" ,E_GETCOLORS ,5 ,size[3]-n0-80 ,75 ,20,GETCOLORSTIP)
Button("UV colors" ,E_UVCOLORS ,5 ,size[3]-n0-102 ,75 ,20,UVCOLORSTIP)
if B_SAVECOLORS :
Button("Rest colors" ,E_RESTCOLORS ,5 ,size[3]-n0-146 ,75 ,20,UVCOLORSTIP)
else:
Button("Save colors" ,E_SAVECOLORS ,5 ,size[3]-n0-124 ,75 ,20,GETCOLORSTIP)
Button("Create" ,E_CREATE ,5 ,size[3]-n0+16 ,60 ,20,CreateTIP)
Button("Action" ,E_ACTION ,5 ,size[3]-n0-4 ,60 ,20,ActionTIP)
Button("Exit" ,E_EXIT ,5 ,size[3]-n0-24 ,60 ,20,ExitTIP)
NRepeat=Number("repeat" ,E_REPEAT ,5 ,size[3]-n0-50 ,75 ,20, NRepeat.val,1,10)
glColor3f(0.0,0.0,0.0)
glRasterPos2f(80 ,size[3]-n0+24)
Text("MODE")
MODEMenu= Menu(MOname, E_MODE ,80 ,size[3]-n0 ,100,20, MODEMenu.val, "MODE menu.")
MODEMenu= Menu(MOname, E_MODE ,80 ,size[3]-n0 ,100,20, MODEMenu.val, MOname_doc[MODEMenu.val])
if MODEMenu.val==2:
TDOCMat=Toggle("Doc Mat" ,E_DOCMAT ,180 ,size[3]-n0 ,60 ,20,TDOCMat.val)
@@ -402,14 +578,22 @@ def draw():
glCl3(TMATList[1][t][0],
TMATList[1][t][1],
TMATList[1][t][2])
glRct(80+t*40,
size[3]-n0-60,
80+t*40+40,
size[3]-n0-60+40)
TMATList[2][t]=Toggle("%s"%t , 32+t ,80+t*40+5 ,size[3]-n0-50 ,30 , 20,TMATList[2][t].val)
if t<=7:
glRct(80+t*40,
size[3]-n0-60,
80+t*40+40,
size[3]-n0-60+40)
TMATList[2][t]=Toggle("%s"%(t+1) , 32+t ,80+t*40+5 ,size[3]-n0-50 ,30 , 20,TMATList[2][t].val)
else:
glRct(80+(t-8)*40,
size[3]-n0-50-50,
80+(t-8)*40+40,
size[3]-n0-60)
TMATList[2][t]=Toggle("%s"%(t+1) , 32+t ,80+(t-8)*40+5 ,size[3]-n0-45*2 ,30 , 20,TMATList[2][t].val)
glColor3f(1.0,0.3,0.0)
glRasterPos2f(80+40+5 ,size[3]-n0-80)
glRasterPos2f(80+40+5 ,size[3]-n0-110)
if ERROR>1:
Text('Last error : '+TextERROR)
else:
@@ -417,35 +601,66 @@ def draw():
glColor3f(0.0,0.0,0.0)
glRasterPos2f(240 ,size[3]-n0+24)
Text("ORIENTATION")
ORIENTMenu= Menu(ORname, E_ORIENT ,240 ,size[3]-n0 ,100,20, ORIENTMenu.val, "ORIENT menu.")
if ORIENTMenu.val==2 :
for t in range(3):
TAXEList[1][t]=Toggle("%s"%TAXEList[0][t],
E_AXESEL+t,
240+100+t*30 , size[3]-n0 ,30 , 20,
TAXEList[1][t].val)
if 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)

View File

@@ -2,7 +2,7 @@
""" Registration info for Blender menus:
Name: 'HotKey and MouseAction Reference'
Blender: 232
Blender: 237
Group: 'Help'
Tip: 'All the hotkeys/short keys'
"""
@@ -11,7 +11,7 @@ __author__ = "Jean-Michel Soler (jms)"
__url__ = ("blender", "elysiun",
"Script's homepage, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_hotkeyscript.htm",
"Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
__version__ = "10/2004"
__version__ = "10/04/2005"
__bpydoc__ = """\
This script is a reference about all hotkeys and mouse actions in Blender.
@@ -22,6 +22,8 @@ Open the script from the Help menu and select group of keys to browse.
Notes:<br>
Additional entries in the database (c) 2004 by Bart.
Additional entries in the database for blender 2.37 (c) 2005 by jms.
"""
# $Id$
@@ -67,11 +69,12 @@ from Blender.Draw import *
from Blender.BGL import *
hotkeys={
'Search ':[['', '']],
'Specials 1 ':[
[',', 'Set Bounding Box rotation scaling pivot'],
['Ctrl-,', 'Set Median Point rotation scaling pivot'],
['.', 'Set 3D cursor as rotation scaling pivot'],
['Ctrl-.', 'Set Individual Object Centers as rotation scaling pivot'] ,
['Ctrl-.', 'Set Individual Object Centers as rotation scaling pivot'],
['~', 'Display all layers (German keys: <20>)'],
['Shift-~', 'Display all/previous layers (German keys: Shift-<2D>)'],
['Space', 'Popup menu'],
@@ -82,9 +85,10 @@ hotkeys={
['TAB', 'IPO: Edit selected'],
['Ctrl-TAB', 'Enter/exit Pose Mode'],
['Shift-TAB', 'Enter Object Mode'],
['Ctrl-Open menu/', ''],
['Ctrl-Open menu /', ''],
['Ctrl-Load Image', 'Opens a thumbnail browser instead of file browser for images']
],
'Mouse ':[
['Actions:', ''],
['LMB', '3D View: Set 3D Cursor'],
@@ -111,6 +115,7 @@ hotkeys={
['MMB', 'Toggle optional transform feature'],
['RMB', 'Abort transformation']
],
'F-Keys ':[
['F1', 'Open File'],
['F2', 'Save File'],
@@ -133,13 +138,18 @@ hotkeys={
['Shift-F7', 'Buttons Window'],
['Shift-F8', 'Video Sequencer Window'],
['Shift-F9', 'OOP Window'],
['Alt-Shift-F9', 'OutLiner Window'],
['Shift-F10', 'UV Image Editor'],
['Shift-F11', 'Text Editor'],
['Shift-F12', 'Action Editor']
['Shift-F12', 'Action Editor'],
['Ctrl-F2', 'Save/export in vrml 1.0 format' ],
['Ctrl-F3', 'Save image : dump 3d view'],
['Ctrl-Shift-F3', 'Save image : dump screen']
],
'Numbers ':[
['1..2..0-=', 'Show layer 1..2..12'],
['1..2..0-=', 'Edit Mode with Size, Grab, rotate tools : enter value'],
['Alt-1..2..0', 'Show layer 11..12..20'],
['Shift-1..2..0-=', 'Toggle layer 1..2..12'],
['Shift-ALT-...', 'Toggle layer 11..12..20']
@@ -150,10 +160,12 @@ hotkeys={
['Numpad /', 'Local view on object (hide others)'],
['Numpad *', 'Rotate view to objects local axes'],
['Numpad +', 'Zoom in (works everywhere)'],
['Numpad +', 'Proportional vertex Edit Mode: Increase range of influence'],
['Numpad +', 'In OutLiner window, Collapse one level of the hierarchy'],
['Alt-Numpad +', 'Proportional vertex Edit Mode: Increase range of influence'],
['Ctrl-Numpad +', 'Edit Mode: Select More vertices'],
['Numpad -', 'Zoom out (works everywhere)'],
['Numpad -', 'Proportional vertex Edit Mode: Decrease range of influence'],
['Numpad +', 'In OutLiner window, Expand one level of the hierarchy'],
['Alt-Numpad -', 'Proportional vertex Edit Mode: Decrease range of influence'],
['Ctrl-Numpad +', 'Edit Mode: Select Less vertices'],
['Numpad INS', 'Set Camera view'],
['Ctrl-Numpad INS', 'Set active object as camera'],
@@ -174,10 +186,19 @@ hotkeys={
'Arrows ':[
['Home/Pos1', 'View all'],
['Home', 'In OutLiner Windows, Show hierarchy'],
['PgUp', 'Edit Mode and Proportionnal Editing Tools, increase influence'],
['PgUp', 'Strip Editor, Move Down'],
['PgUn', 'TimeLine: Jump to next marker'],
['PgUp', 'IPO: Select next keyframe'],
['Ctrl-PgUp', 'IPO: Select and jump to next keyframe'],
['Ctrl-PgUn', 'TimeLine: Jump to next key'],
['PgDn', 'Edit Mode and Proportionnal Editing Tools, decrease influence'],
['PgDn', 'Strip Editor, Move Up'],
['PgDn', 'TimeLine: Jump to prev marker'],
['PgDn', 'IPO: Select previous keyframe'],
['Ctrl-PgDn', 'IPO: Select and jump to previous keyframe'],
['Ctrl-PgDn', 'TimeLine: Jump to prev key'],
['Left', 'One frame backwards'],
['Right', 'One frame forwards'],
['Down', '10 frames backwards'],
@@ -191,7 +212,9 @@ hotkeys={
['Shift-Arrow', 'Toggle first frame/ last frame']
],
'Letters ':[ {"A":[
'Letters ':[
{
"A":[
['A', 'Select all/Deselect all'],
['Alt-A', 'Play animation in current window'],
['Ctrl-A', 'Apply objects size/rotation to object data'],
@@ -199,7 +222,8 @@ hotkeys={
['Shift-A', 'Sequencer: Add menu'],
['Shift-A', '3D-View: Add menu'],
['Shift-ALT-A', 'Play animation in all windows'],
['Shift-CTRL-A', 'Apply lattice / Make dupliverts real']
['Shift-CTRL-A', 'Apply lattice / Make dupliverts real'],
['Shift-CTRL-A', 'Apply Deform ']
],
"B":[
@@ -214,6 +238,7 @@ hotkeys={
['C', 'UV Image Editor: Active Face Select toggle'],
['C', 'Sequencer: Change images'],
['C', 'IPO: Snap current frame to selected key'],
['C', 'TimeLine: Center View'],
['Alt-C', 'Object Mode: Convert menu'],
['Alt-C', 'Text Editor: Copy selection to clipboard'],
['Ctrl-C', 'Copy menu (Copy properties of active to selected objects)'],
@@ -232,6 +257,7 @@ hotkeys={
"E":[
['E', 'Edit Mode: Extrude'],
['E', 'UV Image Editor: LSCM Unwrap'],
['E', 'TimeLine: Set End'],
['ER', 'Edit Mode: Extrude Rotate'],
['ES', 'Edit Mode: Extrude Scale'],
['ESX', 'Edit Mode: Extrude Scale X axis'],
@@ -267,10 +293,13 @@ hotkeys={
"H":[
['H', 'Hide selected vertices/faces'],
['H', 'Curves: Set handle type'],
['H', 'Action editor: Handle type aligned'],
['H', 'Action editor: Handle type free'],
['Alt-H', 'Show Hidden vertices/faces'],
['Ctrl-H', 'Curves: Automatic handle calculation'],
['Shift-H', 'Hide deselected vertices/faces'],
['Shift-H', 'Curves: Set handle type']
['Shift-H', 'Curves: Automatic handle calculation'],
['Shift-H', 'Action editor: Handle type auto'],
['Shift-H', 'Edit Mode, Hide deselected vertices/faces'],
['Ctrl-H', 'Edit Mode, Add a hook on selected points or show the hook menu .']
],
"I":[
@@ -279,12 +308,11 @@ hotkeys={
"J":[
['J', 'IPO: Join menu'],
['J', 'Mesh: Join all adjacent triangles to quads'],
['J', 'Mesh: Join all adjacent triangles to quads'],
['J', 'Render Window: Swap render buffer'],
['Ctrl-J', 'Join selected objects'],
['Ctrl-J', 'Nurbs: Add segment'],
['Ctrl-J', 'IPO: Join keyframes menu'],
['Alt-J', 'Edit Mode: convert quads to triangles']
],
"K":[
@@ -304,7 +332,7 @@ hotkeys={
['L', 'Edit mode: Select linked vertices (near mouse pointer)'],
['L', 'OOPS window: Select linked objects'],
['L', 'UV Face Select: Select linked faces'],
['Ctrl-L', 'Make links menu'],
['Ctrl-L', 'Make links menu (for instance : to scene...)'],
['Shift-L', 'Select links menu']
],
@@ -312,8 +340,12 @@ hotkeys={
['M', 'Move object to different layer'],
['M', 'Sequencer: Make meta strip (group) from selected strips'],
['M', 'Edit Mode: Mirros Axis menu'],
['M', 'Video Sequence Editor : Make Meta strip...'],
['M', 'TimeLine: Add marker'],
['Alt-M', 'Edit Mode: Merge vertices menu'],
['Ctrl-M', 'Object Mode: Mirros Axis menu']
['Alt-M', 'Video Sequence Editor : Separate Meta strip...'],
['Ctrl-M', 'Object Mode: Mirros Axis menu'],
['Ctrl-M', 'TimeLine: Name marker']
],
"N":[
@@ -360,6 +392,7 @@ hotkeys={
"S":[
['S', 'Scale'] ,
['S', 'TimeLine: Set Start'],
['SX', 'Flip around X axis'] ,
['SY', 'Flip around Y axis'] ,
['SZ', 'Flip around Z axis'] ,
@@ -376,14 +409,15 @@ hotkeys={
['T', 'Adjust texture space'] ,
['T', 'Edit mode: Flip 3d curve'] ,
['T', 'IPO: Change IPO type'] ,
['T', 'TimeLine: Show second'],
['Alt-T', 'Clear tracking of object'] ,
['Ctrl-T', 'Make selected object track active object'] ,
['Ctrl-T', 'Edit Mode: Convert to triangles'] ,
['Ctrl-ALT-T', 'Benchmark'] ],
"U":[
['U', 'Make single user menu'] ,
['U', '3D View: Global undo'] ,
['U', 'Make single user menu (for import completly linked object to another scene for instance) '] ,
['U', '3D View: Make Single user Menu'] ,
['U', 'Edit Mode: Reload object data from before entering Edit Mode'] ,
['U', 'UV Face Select: Automatic UV calculation menu'] ,
['U', 'Vertex-/Weightpaint mode: Undo'] ,
@@ -395,9 +429,11 @@ hotkeys={
['V', 'Curves/Nurbs: Vector handle'],
['V', 'Vertexpaint mode'],
['V', 'UV Image Editor: Stitch UVs'],
['V', 'Action editor: Vector'],
['Alt-V', "Scale object to match image texture's aspect ratio"],
['Shift-V', 'Edit mode: Align view to selected vertices'],
['Shift-V', 'UV Image Editor: Limited Stitch UVs popup'],
],
"W":[
@@ -408,11 +444,16 @@ hotkeys={
['WY', 'UV Image Editor: Weld/Align Y axis'],
['Ctrl-W', 'Save current file'] ,
['Ctrl-W', 'Nurbs: Switch direction'] ,
['Shift-W', 'Warp/bend selected vertices around cursor'] ],
['Shift-W', 'Warp/bend selected vertices around cursor'],
['alt-W', 'Export in videoscape format']
],
"X":[
['X', 'Delete menu'] ,
['Ctrl-X', 'Restore default state (Erase all)'] ],
['X', 'TimeLine: Remove marker'],
['Ctrl-X', 'Restore default state (Erase all)']
],
"Y":[
['Y', 'Mesh: Split selected vertices/faces from the rest'] ],
@@ -429,6 +470,12 @@ hotkeys={
up=128
down=129
UP=0
SEARCH=131
OLDSEARCHLINE=''
SEARCHLINE=Create('')
LINE=130
FINDED=[]
LEN=0
for k in hotkeys.keys():
hotkeys[k].append(Create(0))
@@ -442,6 +489,24 @@ hotL.sort()
hot=hotkeys.keys()
hot.sort()
def searchfor(SEARCHLINE):
global hotkeys, hot
FINDLIST=[]
for k in hot:
if k not in ['Letters ', 'Search '] :
for l in hotkeys[k][:-1]:
#print 'k, l : ', k, l, l[1]
if l[1].upper().find(SEARCHLINE.upper())!=-1:
FINDLIST.append(l)
elif k == 'Letters ':
for l in hotL :
for l0 in hotkeys['Letters '][0][l][:-1]:
#print 'k, l : ',l, k, l0
if l0[1].upper().find(SEARCHLINE.upper())!=-1:
FINDLIST.append(l0)
return FINDLIST
glCr=glRasterPos2d
glCl3=glColor3f
glCl4=glColor4f
@@ -462,8 +527,8 @@ def trace_rectangle3(r,c,c1):
glCl3(c1[0],c1[1],c1[2])
def draw():
global r,c,c1,hotkeys, hot, hotL, up, down, UP
global r,c,c1,hotkeys, hot, hotL, up, down, UP, SEARCH, SEARCHLINE,LINE
global OLDSEARCHLINE, FINDED, SCROLL, LEN
size=Buffer(GL_FLOAT, 4)
glGetFloatv(GL_SCISSOR_BOX, size)
size= size.list
@@ -490,7 +555,7 @@ def draw():
glRasterPos2f(42, size[3]-25)
Text("HotKey and MouseAction Reference")
l=0
listed=0
Llisted=0
@@ -499,14 +564,12 @@ def draw():
for k in hot:
#hotkeys[k][-1]=Toggle(k, hot.index(k)+10, 4+(20*26)/6*hot.index(k), size[3]-(42), len(k)*8, 20, hotkeys[k][-1].val )
hotkeys[k][-1]=Toggle(k, hot.index(k)+10, 78*hot.index(k), size[3]-(47), 78, 24, hotkeys[k][-1].val )
l+=len(k)
if hotkeys[k][-1].val==1.0:
listed=hot.index(k)
l=0
size[3]=size[3]-4
if hot[listed]!='Letters ':
if hot[listed]!='Letters ' and hot[listed]!='Search ' :
size[3]=size[3]-8
SCROLL=size[3]/21
END=-1
@@ -520,9 +583,7 @@ def draw():
UP=len(hotkeys[hot[listed]][:-1])-SCROLL
else :
UP=0
for n in hotkeys[hot[listed]][:-1][UP:END]:
if l%2==0:
r=[0,size[3]-(21*l+66),
size[2], size[3]-(21*l+43)]
@@ -533,20 +594,50 @@ def draw():
glRasterPos2f(4+8*15, size[3]-(58+21*l))
Text(' : '+n[1])
l+=1
elif hot[listed]=='Search ' :
r=[0,size[3]-70,
size[2], size[3]-44]
trace_rectangle4(r,c2)
SEARCHLINE=String(' ', LINE, 42, size[3]-68,200,18,SEARCHLINE.val, 256,'')
if len(FINDED)>0:
LEN=len(FINDED)
size[3]=size[3]-8
SCROLL=size[3]/21
END=-1
if SCROLL < len(FINDED):
Button('/\\',up,4,size[3]+8,20,14,'Scroll up')
Button('\\/',down,4,size[3]-8,20,14,'Scroll down')
if (SCROLL+UP)<len(FINDED):
END=(UP+SCROLL-1)
else:
END=-1
#UP=len(FINDED)-SCROLL
else:
UP=0
for n in FINDED[UP:END]:
if l%2==0:
r=[0,size[3]-(21*l+66+24),
size[2], size[3]-(21*l+43+24)]
trace_rectangle4(r,c2)
glColor3f(0,0,0)
glRasterPos2f(4+8, size[3]-(58+24+21*l))
Text(n[0])
glRasterPos2f(4+8*15, size[3]-(58+24+21*l))
Text(' : '+n[1])
l+=1
else:
for k in hotL:
pos=hotL.index(k)
hotkeys['Letters '][0][k][-1]=Toggle(k,pos+20,hotL.index(k)*21, size[3]-(52+18), 21, 18, hotkeys['Letters '][0][k][-1].val )
if hotkeys['Letters '][0][k][-1].val==1.0:
Llisted=pos
size[3]=size[3]-8
SCROLL=(size[3]-88)/21
END=-1
if SCROLL < len(hotkeys['Letters '][0][hotL[Llisted]]):
Button('/\\',up,4,size[3]+8,20,14,'Scroll up')
Button('\\/',down,4,size[3]-8,20,14,'Scroll down')
LEN=len(hotkeys['Letters '][0][hotL[Llisted]])
Button('/\\',up,4,size[3]+8,20,14,'Scroll up, you can use arrow or page keys too ')
Button('\\/',down,4,size[3]-8,20,14,'Scroll down, you can use arrow or page keys too ')
if (UP+SCROLL)<len(hotkeys['Letters '][0][hotL[Llisted]]):
END=(UP+SCROLL)
else:
@@ -569,13 +660,27 @@ def draw():
l+=1
def event(evt, val):
global hotkeys, UP
if ((evt== QKEY or evt== ESCKEY) and not val):
global hotkeys, UP, SCROLL , LEN
if (evt== QKEY or evt== ESCKEY):
Exit()
elif val:
if (evt== PAGEUPKEY):
if (UP+SCROLL)<LEN-5:
UP+=5
elif (evt== PAGEDOWNKEY):
if UP>4:
UP-=5
elif (evt== UPARROWKEY):
if (UP+SCROLL)<LEN-1:
UP+=1
elif (evt== DOWNARROWKEY):
if UP>0:
UP-=1
Redraw()
def bevent(evt):
global hotkeysmhot, hotL, up,down,UP
global hotkeysmhot, hotL, up,down,UP, FINDED
global SEARCH, SEARCHLINE,LINE, OLDSEARCHLINE
if (evt== 1):
Exit()
@@ -602,4 +707,11 @@ def bevent(evt):
if UP>0: UP-=1
Blender.Window.Redraw()
elif (evt==LINE):
if SEARCHLINE.val!='' and SEARCHLINE.val!=OLDSEARCHLINE:
OLDSEARCHLINE=SEARCHLINE.val
FINDED=searchfor(OLDSEARCHLINE)
Blender.Window.Redraw()
Register(draw, event, bevent)

View File

@@ -26,7 +26,7 @@ Usage:<br>
Supported:<br>
UV Coordinates, Meshes, Materials, Material Indices, Specular
Highlights, and Vertex Colors. For added functionality, each object is
placed on its own layer.
placed on its own layer. Someone added the CLIP chunk and imagename support.
Missing:<br>
Not too much, I hope! :).
@@ -95,6 +95,7 @@ def write(filename):
bbox = generate_bbox(mesh)
pols = generate_pols(mesh)
ptag = generate_ptag(mesh, material_names)
clip = generate_clip(mesh, material_names)
if mesh.hasFaceUV():
vmad_uv = generate_vmad_uv(mesh) # per face
@@ -111,10 +112,6 @@ def write(filename):
write_chunk(meshdata, "POLS", pols); chunks.append(pols)
write_chunk(meshdata, "PTAG", ptag); chunks.append(ptag)
if mesh.hasFaceUV():
write_chunk(meshdata, "VMAD", vmad_uv)
chunks.append(vmad_uv)
if meshtools.has_vertex_colors(mesh):
if meshtools.average_vcols:
write_chunk(meshdata, "VMAP", vmap_vc)
@@ -122,6 +119,13 @@ def write(filename):
else:
write_chunk(meshdata, "VMAD", vmad_vc)
chunks.append(vmad_vc)
if mesh.hasFaceUV():
write_chunk(meshdata, "VMAD", vmad_uv)
chunks.append(vmad_uv)
write_chunk(meshdata, "CLIP", clip)
chunks.append(clip)
layer_index += 1
for surf in surfs:
@@ -135,6 +139,7 @@ def write(filename):
file.write(meshdata.getvalue()); meshdata.close()
for surf in surfs:
write_chunk(file, "SURF", surf)
write_chunk(file, "DATE", "August 19, 2005")
Blender.Window.DrawProgressBar(1.0, "") # clear progressbar
file.close()
@@ -456,6 +461,46 @@ def generate_surf(material_name):
comment = generate_nstring(comment)
data.write(struct.pack(">H", len(comment)))
data.write(comment)
# Check if the material contains any image maps
mtextures = material.getTextures() # Get a list of textures linked to the material
for mtex in mtextures:
if (mtex) and (mtex.tex.type == Blender.Texture.Types.IMAGE): # Check if the texture is of type "IMAGE"
data.write("BLOK") # Surface BLOK header
data.write(struct.pack(">H", 104)) # Hardcoded and ugly! Will only handle 1 image per material
# IMAP subchunk (image map sub header)
data.write("IMAP")
data_tmp = cStringIO.StringIO()
data_tmp.write(struct.pack(">H", 0)) # Hardcoded - not sure what it represents
data_tmp.write("CHAN")
data_tmp.write(struct.pack(">H", 4))
data_tmp.write("COLR")
data_tmp.write("OPAC") # Hardcoded texture layer opacity
data_tmp.write(struct.pack(">H", 8))
data_tmp.write(struct.pack(">H", 0))
data_tmp.write(struct.pack(">f", 1.0))
data_tmp.write(struct.pack(">H", 0))
data_tmp.write("ENAB")
data_tmp.write(struct.pack(">HH", 2, 1)) # 1 = texture layer enabled
data_tmp.write("NEGA")
data_tmp.write(struct.pack(">HH", 2, 0)) # Disable negative image (1 = invert RGB values)
data_tmp.write("AXIS")
data_tmp.write(struct.pack(">HH", 2, 1))
data.write(struct.pack(">H", len(data_tmp.getvalue())))
data.write(data_tmp.getvalue())
# IMAG subchunk
data.write("IMAG")
data.write(struct.pack(">HH", 2, 1))
data.write("PROJ")
data.write(struct.pack(">HH", 2, 5)) # UV projection
data.write("VMAP")
uvname = generate_nstring("Blender's UV Coordinates")
data.write(struct.pack(">H", len(uvname)))
data.write(uvname)
return data.getvalue()
# =============================================
@@ -536,6 +581,27 @@ def generate_icon():
#print len(icon_data)
return data.getvalue()
# ===============================================
# === Generate CLIP chunk with STIL subchunks ===
# ===============================================
def generate_clip(mesh, material_names):
data = cStringIO.StringIO()
clipid = 1
for i in range(len(mesh.materials)): # Run through list of materials used by mesh
material = Blender.Material.Get(mesh.materials[i].name)
mtextures = material.getTextures() # Get a list of textures linked to the material
for mtex in mtextures:
if (mtex) and (mtex.tex.type == Blender.Texture.Types.IMAGE): # Check if the texture is of type "IMAGE"
pathname = mtex.tex.image.filename # If full path is needed use filename in place of name
pathname = pathname[0:2] + pathname.replace("\\", "/")[3:] # Convert to Modo standard path
imagename = generate_nstring(pathname)
data.write(struct.pack(">L", clipid)) # CLIP sequence/id
data.write("STIL") # STIL image
data.write(struct.pack(">H", len(imagename))) # Size of image name
data.write(imagename)
clipid += 1
return data.getvalue()
# ===================
# === Write Chunk ===
# ===================

View File

@@ -1,46 +1,22 @@
#!BPY
"""
Name: 'LightWave (.lwo)...'
Blender: 232
Name: 'LightWave + Materials (.lwo)...'
Blender: 237
Group: 'Import'
Tooltip: 'Import LightWave Object File Format (.lwo)'
"""
__author__ = "Anthony D'Agostino (Scorpius)"
__author__ = "Alessandro Pirovano, Anthony D'Agostino (Scorpius)"
__url__ = ("blender", "elysiun",
"Author's homepage, http://www.redrival.com/scorpius")
__version__ = "Part of IOSuite 0.5"
"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:<br>
Execute this script from the "File->Import" menu and choose a LightWave
file to open.
Supported:<br>
Meshes only.
Missing:<br>
Materials, UV Coordinates, and Vertex Color info will be ignored.
Known issues:<br>
Triangulation of convex polygons works fine, and uses a very simple
fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
require a different algorithm, and will be triagulated incorrectly.
Notes:<br>
Also reads lwo files in the old LW v5.5 format.
"""
# $Id$
#
importername = "lwo_import 0.1.16"
# +---------------------------------------------------------+
# | Save your work before and after use. |
# | Please report any useful comment to: |
# | uaraus-dem@yahoo.it |
# | Thanks |
# +---------------------------------------------------------+
# +---------------------------------------------------------+
# | Copyright (c) 2002 Anthony D'Agostino |
# | http://www.redrival.com/scorpius |
@@ -51,156 +27,1578 @@ Notes:<br>
# +---------------------------------------------------------+
# | Read and write LightWave Object File Format (*.lwo) |
# +---------------------------------------------------------+
# +---------------------------------------------------------+
# | Alessandro Pirovano tweaked starting on March 2005 |
# | http://uaraus.altervista.org |
# +---------------------------------------------------------+
# +---------------------------------------------------------+
# | Release log: |
# | 0.1.16: fixed (try 2) texture offset calculations |
# | added hint on axis mapping |
# | added hint on texture blending mode |
# | added hint on texture transparency setting |
# | search images in original directory first |
# | fixed texture order application |
# | 0.1.15: added release log |
# | fixed texture offset calculations (non-UV) |
# | fixed reverting vertex order in face generation |
# | associate texture on game-engine settings |
# | vector math definitely based on mathutils |
# | search images in "Images" and "../Images" dir |
# | revised logging facility |
# | fixed subsurf texture and material mappings |
# | 0.1.14: patched missing mod_vector (not definitive) |
# | 0.1.13: first public release |
# +---------------------------------------------------------+
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")

View File

@@ -9,7 +9,7 @@ Tooltip: 'Save a Wavefront OBJ File'
__author__ = "Campbell Barton, Jiri Hnidek"
__url__ = ["blender", "elysiun"]
__version__ = "0.9"
__version__ = "1.0"
__bpydoc__ = """\
This script is an exporter to OBJ file format.
@@ -49,134 +49,287 @@ def newFName(ext):
return Get('filename')[: -len(Get('filename').split('.', -1)[-1]) ] + ext
def fixName(name):
if name == None:
return 'None'
else:
return name.replace(' ', '_')
from Blender import *
NULL_MAT = '(null)'
NULL_IMG = '(null)' # from docs at http://astronomy.swin.edu.au/~pbourke/geomformats/obj/ also could be 'off'
global MTL_DICT
# A Dict of Materials
# (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
MTL_DICT = {}
def save_mtl(filename):
global MTL_DICT
world = World.GetCurrent()
if world:
worldAmb = world.getAmb()
else:
worldAmb = (0,0,0) # Default value
file = open(filename, "w")
file.write('# Blender MTL File: %s\n' % (Get('filename')))
for mat in Material.Get():
file.write('newmtl %s\n' % (mat.getName())) # Define a new material
file.write('Ns %s\n' % ((mat.getHardness()-1) * 1.9607843137254901 ) ) # Hardness, convert blenders 1-511 to MTL's
file.write('Kd %.6f %.6f %.6f\n' % tuple(mat.getRGBCol())) # Diffuse
file.write('Ka %.6f %.6f %.6f\n' % tuple(mat.getMirCol())) # Ambient, uses mirror colour,
file.write('Ks %.6f %.6f %.6f\n' % tuple(mat.getSpecCol())) # Specular
file.write('Ni %.6f\n' % mat.getIOR()) # Refraction index
file.write('d %.6f\n' % mat.getAlpha()) # Alpha (obj uses 'd' for dissolve)
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
'''

View File

@@ -4,7 +4,7 @@
Name: 'Wavefront (.obj)...'
Blender: 237
Group: 'Import'
Tooltip: 'Load a Wavefront OBJ File'
Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
"""
__author__ = "Campbell Barton"
@@ -43,14 +43,6 @@ Run this script from "File->Import" menu and then load the desired OBJ file.
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
ABORT_MENU = 'Failed Reading OBJ%t|File is probably another type|if not send this file to|cbarton@metavr.com|with MTL and image files for further testing.'
NULL_MAT = '(null)' # Name for mesh's that have no mat set.
NULL_IMG = '(null)' # Name for mesh's that have no mat set.
MATLIMIT = 16 # This isnt about to change but probably should not be hard coded.
DIR = ''
#==============================================#
# Return directory, where the file is #
@@ -71,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()

View File

@@ -1,15 +1,15 @@
#!BPY
"""
Name: 'Bridge/Skin/Loft'
Blender: 234
Name: 'Bridge Faces/Edge-Loops'
Blender: 237
Group: 'Mesh'
Tooltip: 'Select 2 or more vert loops, then run this script'
Tooltip: 'Select 2 vert loops, then run this script.'
"""
__author__ = "Campbell Barton AKA Ideasman"
__url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
__version__ = "1.1 2005/06/13"
__version__ = "1.0 2004/04/25"
__bpydoc__ = """\
With this script vertex loops can be skinned: faces are created to connect the
@@ -19,11 +19,7 @@ Usage:
In mesh Edit mode select the vertices of the loops (closed paths / curves of
vertices: circles, for example) that should be skinned, then run this script.
A pop-up will provide further options.
Notes:
If the results of a method chosen from the pop-up are not adequate, undo and try one of the others.
A pop-up will provide further options, if the results of a method are not adequate try one of the others.
"""
@@ -51,536 +47,443 @@ If the results of a method chosen from the pop-up are not adequate, undo and try
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
# Made by Ideasman/Campbell 2004/04/25 - ideasman@linuxmail.org
# Made by Ideasman/Campbell 2005/06/15 - ideasman@linuxmail.org
import Blender
from Blender import *
import math
from math import *
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()

View File

@@ -2,7 +2,7 @@
""" Registration info for Blender menus:
Name: 'Texture Baker'
Blender: 237
Blender: 236
Group: 'UV'
Tooltip: 'Procedural to uvmapped texture baker'
"""
@@ -11,10 +11,11 @@ __author__ = "Jean-Michel Soler (jms)"
__url__ = ("blender", "elysiun",
"Official Page, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_mesh3d2uv2d_en.htm",
"Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
__version__ = "0.2.8 2005/7/20"
__version__ = "0.3.0 2005/10/09"
__bpydoc__ = """\
Texture Baker "bakes" Blender procedural materials (including textures): it saves them as 2d uv-mapped images.
This script "bakes" Blender procedural materials (including textures): it saves
them as 2d uv-mapped images.
This script saves an uv texture layout of the chosen mesh, that can be used as
an uv map for it. It is a way to export procedurals from Blender as normal
@@ -23,21 +24,22 @@ with the mesh in games and other 3d applications.
Usage:
a) Enter face mode and define uv coordinates for your mesh (do not forget to choose a development shape);<br>
b) Define its materials and textures;<br>
a) Enter face mode and define uv coordinates for your mesh;<br>
b) Define its materials and textures ;
c) Run this script and check the console.
Global variables:
Global variables
a) FRAME (integer): the last frame of the animation, autodocumented.<br>
b) LIMIT (integer): 0 or 1, uvcoords may exceed limits 0.0 to 1.0, this variable obliges the script to do a complete framing of the uvcoord.
a) FRAME integer, the last frame of the animation, autodocumented .
b) LIMIT integer, 0 or 1, uvcoords may exceed limits 0.0 to 1.0 , this variable
obliges the script to do a complete framing of the uvcoord .
Notes:<br>
This script was based on a suggestion by Martin (Theeth) Poirier.
This script was based on a suggestion by Martin (Theeth) Poirier;<br>
"""
#---------------------------------------------
# Last release : 0.2.8 , 2005/07/20 , 17h10
# Last release : 0.3.0 , 2005/10/09 , 23h23
#---------------------------------------------
#---------------------------------------------
# (c) jm soler 07/2004 : 'Procedural Texture Baker'
@@ -47,6 +49,14 @@ Notes:<br>
#
# Released under Blender Artistic Licence
#
#
# 0.3.0
# TAILLEIMAGE variable
#
# 0.2.9
# -- little probleme with the KEEPRENDERWINDOW variable .
# removed . script seems to works correctly now .
#
# 0.2.8
# -- added the forgotten image property in face
# data. a little longer but better.
@@ -201,6 +211,8 @@ DEBUG=1
RENDERLAYER=20
SCENELAYERS=[]
helpmsg = """
Texture Baker:
@@ -250,6 +262,7 @@ def RenameImage(RDIR, MYDIR, FILENAME, name):
"""
newfname = RDIR + MYDIR + name
if newfname.find('.png', -4) < 0 : newfname += '.png'
if not Blender.sys.exists(newfname):
os.rename(FILENAME, newfname)
else:
@@ -290,13 +303,13 @@ def SAVE_image (rc, name, FRAME, result):
rc.startFrame(NEWFRAME)
rc.endFrame(NEWFRAME)
rc.renderAnim()
if result!=2 and not KEEPRENDERWINDOW:
if result!=2 :
Blender.Scene.Render.CloseRenderWindow()
FILENAME = "%04d" % NEWFRAME
FILENAME = FILENAME.replace (' ', '0')
FILENAME = RDIR + MYDIR + FILENAME + '.png'
RenameImage(RDIR, MYDIR, FILENAME, name)
rc.endFrame(OLDEFRAME)
rc.startFrame(OLDSFRAME)
rc.setRenderPath(RENDERDIR)
@@ -346,14 +359,24 @@ def SHOOT (XYlimit, frame, obj, name, FRAME, result):
OLDy = context.imageSizeY()
OLDx = context.imageSizeX()
tres = Draw.PupMenu('TEXTURE OUT RESOLUTION : %t | 256 %x1 | 512 %x2 | 768 %x3 | 1024 %x4 | 2048 %x5 ')
TAILLEIMAGE='TEXTURE OUT RESOLUTION : %t |'
TAILLEIMAGE+='256 %x1 |'
TAILLEIMAGE+='512 %x2 |'
TAILLEIMAGE+='768 %x3 |'
TAILLEIMAGE+='1024 %x4 |'
TAILLEIMAGE+='2048 %x5 '
#TAILLEIMAGE+='| 4096 %x6 '
tres = Draw.PupMenu(TAILLEIMAGE)
if (tres) == 1: res = 256
elif (tres) == 2: res = 512
elif (tres) == 3: res = 768
elif (tres) == 4: res = 1024
elif (tres) == 5: res = 2048
# elif (tres) == 6: res = 4096
else: res = 512
#...
SCENELAYERS=SC.layers
SC.layers = [20]
@@ -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)

View File

@@ -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:<br>
the "Make" VColors button in the edit mesh buttons win, the current light setup
is saved as vertex colors for the model;<br>
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[1] : LIM[1]=u[0]
if u[1] >LIM[2] : LIM[2]=u[1]
if u[1] <LIM[3] : LIM[3]=u[1]
xlimit=0
for f in se:
a,b,c,d=transface(f,x,y)
if len(f.v)==4:
triangle(a,b,c)
triangle(a,c,d)
elif len(f.v)==3:
triangle(a,b,c)
if not EMPTY and not TRANSP:
if len(f.v)==4:
Ftriangle(a,b,c)
Ftriangle(a,c,d)
elif len(f.v)==3:
Ftriangle(a,b,c)
elif not EMPTY :
if len(f.v)==4:
Ttriangle(a,b,c)
Ttriangle(a,c,d)
elif len(f.v)==3:
Ttriangle(a,b,c)
if LINE.val==1:
if LINE.val==1 or EMPTY:
for f in se:
a,b,c,d=transface(f,x,y)
if len(f.v)==4:
Lcarre(a,b,c,d)
Lcarre(a,b,c,d,LC)
elif len(f.v)==3:
Ltriangle(a,b,c)
Ltriangle(a,b,c,LC)
if XLIMIT.val==0:
Lcarre([1,1],[1,y-2],[xlimit+2,y-2],[xlimit+2,1])
else:
Lcarre([1,1],[1,y-2],[xwin-2,y-2],[xwin-2,1])
def write_tgafile(loc2,bitmap,width,height,profondeur):
f=open(loc2,'wb')
@@ -286,10 +400,13 @@ def write_tgafile(loc2,bitmap,width,height,profondeur):
def save(x0,y0,dx,dy):
global SAVE
im = Buffer(GL_BYTE,[dx*(dy+1),4])
glReadPixels(x0,y0,dx,dy,GL_RGBA, GL_BYTE,im);
print len(im), dx*dy, dx, dy, len(im)/dy
write_tgafile(loc2,im,dx,dy+1,4)
SAVE=0
Blender.Redraw()
def DOCMat_list(TMATList,ME):
me=Blender.NMesh.GetRaw(ME.getData().name)
@@ -342,9 +459,13 @@ TEXT=Create(loc2)
# uvpaint4
# ----------
TMENU="MODE MENU %t|All %x1|Material %x2|Selected %x3"
# ----------
# uvpaint4
# ----------
# coming soon : "|vertex group %x4", perhaps in uvpainter v0.5
LCOLOR= Create(64)
SAVE=0
# coming soon : "|Bone %x4", perhaps in uvpainter v0.5
MMENU=Create(3)
TDOCMat = Create(0)
# ----------
@@ -370,13 +491,14 @@ n0=32
def draw():
global NSIZE,LINE,x0,y0,y,x,TEXT,MMENU,TDOCMat
global XLIMIT,selmatlist,xwin
global XLIMIT,selmatlist,xwin, LCOLOR, SAVE
global mouse_xr,sel, ANC, X,Y, TRANSP, DOCU
global mouse_yr, POS, XY,size, TRANSP,EMPTY
global MENU, SAVE
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])
size=[int(s) for s in size ]
n0=32
x0=size[0]
@@ -390,78 +512,138 @@ def draw():
glClear(GL_COLOR_BUFFER_BIT)
glShadeModel(GL_SMOOTH)
#transparence
if TRANSP :
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
SelecMESH=Blender.Object.GetSelected()
if SelecMESH!=[]:
if SelecMESH[0].getType()=='Mesh':
affiche_mesh(SelecMESH[0],int(y*NSIZE.val),int(y*NSIZE.val-n0-2))
if SelecMESH!=[] and SelecMESH[0].getType()=='Mesh':
affiche_mesh(SelecMESH[0],int(y*NSIZE.val),int(y*NSIZE.val-2))
glColor3f(0.0,0.0,0.0)
glRectf(4,size[3],555,size[3]-32 )
if MENU:
glColor3f(0.0,0.0,0.0)
glRectf(4,size[3],555,size[3]-32 )
glColor3f(1.0,1.0,1.0)
glRasterPos2f(8, size[3]-13)
Text("uvpainter v0.8")
glRasterPos2f(8, size[3]-28)
Text("Jm Soler, 08/2005")
glColor3f(1.0,1.0,1.0)
Button("ReDraw" ,16 ,290-118+61 ,size[3]-30 ,60 ,13)
Button("Exit" ,1 ,250-122+63 ,size[3]-30 ,38 ,13)
Button("Save" ,6 ,250-16+61 ,size[3]-30 ,40 ,13)
glRasterPos2f(8, size[3]-13)
Text("uvpainter v0.5")
glRasterPos2f(8, size[3]-28)
Text("Jm Soler, 05/2004")
NSIZE= Slider("Sc:",4 ,290-118+61 ,size[3]-15 , 102, 13, NSIZE.val, 0.1,5.0,0,"SIZE.")
LINE=Toggle("line", 5 ,250-122+63 ,size[3]-15 , 38, 13, LINE.val, "Draw lines")
Button("ReDraw" ,16 ,290-118+61 ,size[3]-30 ,60 ,13)
Button("Exit" ,1 ,250-122+63 ,size[3]-30 ,38 ,13)
Button("Save" ,6 ,250-16+61 ,size[3]-30 ,40 ,13)
glRasterPos2f(250-130 ,size[3]-13,)
Text("Mode")
NSIZE= Slider("Sc:",4 ,290-118+61 ,size[3]-15 , 102, 13, NSIZE.val, 0.1,1.5,0,"SIZE.")
LINE=Toggle("line", 5 ,250-122+63 ,size[3]-15 , 38, 13, LINE.val, "Draw lines")
LCOLOR=Number("", 50, 250-77,size[3]-15,18,13,LCOLOR.val,1,64,'Line color, 64-lighter, 1-darker.')
glRasterPos2f(250-130 ,size[3]-13,)
Text("Mode")
MMENU= Menu(TMENU ,2 ,250-130, size[3]-30, 63, 13, MMENU.val, "MODE menu.")
MMENU= Menu(TMENU ,2 ,250-130, size[3]-30, 63, 13, MMENU.val, "MODE menu.")
if MMENU.val==1 or MMENU.val==3:
glRasterPos2f( 250-16+61+42+80,size[3]-13)
if XLIMIT.val:
xl=xwin
else:
xl=xlimit
if MMENU.val==1 or MMENU.val==3:
glRasterPos2f( 250-16+61+42+80,size[3]-13)
if XLIMIT.val:
xl=xwin
else:
xl=xlimit
Text("x :"+"%d"%(xl+2))
Text("x :"+"%d"%(xl+2))
glRasterPos2f(250-16+61+42+65*2,size[3]-13)
Text("y :"+"%d"%(y-n0+1))
glRasterPos2f(250-16+61+42+65*2,size[3]-13)
Text("y :"+"%d"%(y-n0+1))
TEXT=String("to:", 7 , 278+61 ,size[3]-28 , 213, 13, TEXT.val, 256, "Draw lines")
if XLIMIT.val==1:
limit='winlimit'
else:
limit='maxXlimit'
XLIMIT=Toggle(limit, 9 , 250-16+61+42 ,size[3]-15 , 60, 13, XLIMIT.val, "to save picture from x max uv limit, or x window max limit")
TEXT=String("to:", 7 , 278+61 ,size[3]-28 , 213, 13, TEXT.val, 256, "Draw lines")
if XLIMIT.val==1:
limit='winlimit'
else:
limit='maxXlimit'
XLIMIT=Toggle(limit, 9 , 250-16+61+42 ,size[3]-15 , 60, 13, XLIMIT.val, "to save picture from x max uv limit, or x window max limit")
if MMENU.val==2:
TDOCMat=Toggle("doc" ,24,250-130+35 ,size[3]-13 , 28, 13, TDOCMat.val)
if TDOCMat.val==1:
SELMat_list()
for t in range(TMATList[0]):
glCl3(TMATList[1][t][0],
TMATList[1][t][1],
TMATList[1][t][2])
glRct((293-16+61)+t*20,
size[3]-13,
(293-16+61)+t*20+20,
size[3]-30,)
TMATList[2][t]=Toggle("%s"%t , 32+t ,(293-16+61)+t*20 ,size[3]-13 ,20 , 13,TMATList[2][t].val)
if MMENU.val==2:
TDOCMat=Toggle("doc" ,24,250-130+35 ,size[3]-13 , 28, 13, TDOCMat.val)
if TDOCMat.val==1:
SELMat_list()
for t in range(TMATList[0]):
glCl3(TMATList[1][t][0],
TMATList[1][t][1],
TMATList[1][t][2])
glRct((293-16+61)+t*20,
size[3]-13,
(293-16+61)+t*20+20,
size[3]-30,)
TMATList[2][t]=Toggle("%s"%t , 32+t ,(293-16+61)+t*20 ,size[3]-13 ,20 , 13,TMATList[2][t].val)
#else:
# save()
if DOCU:
Doc(size)
def event(evt, val):
global mouse_x, x, mouse_y, y, LC
global LCOLOR, DOCU, MENU, SAVE
global mouse_xr,sel, ANC, X,Y ,NSIZE
global mouse_yr, POS, XY,size, TRANSP,EMPTY
def event(evt, val):
if (evt== QKEY and not val): Exit()
if (evt== QKEY or evt== ESCKEY and not val):
Exit()
elif (evt== TKEY and not val):
TRANSP=abs(TRANSP-1)
elif (evt== EKEY and not val):
EMPTY=abs(EMPTY-1)
elif (evt== MKEY and not val):
MENU=abs(MENU-1)
elif (evt== SKEY and not val):
SAVE=abs(MENU-1)
elif (evt== WKEY and not val):
LC=1.0
LCOLOR.val=64
elif (evt== BKEY and not val):
LC=0.0
LCOLOR.val=1
elif (evt==LEFTARROWKEY and not val) :
POS[0]-=10
elif (evt==RIGHTARROWKEY and not val) :
POS[0]+=10
elif (evt==UPARROWKEY and not val) :
POS[1]+=10
elif (evt==DOWNARROWKEY and not val) :
POS[1]-=10
elif (evt==PADMINUS and not val) :
NSIZE.val-=0.1
elif (evt==PADPLUSKEY and not val) :
NSIZE.val+=0.1
elif (evt==LKEY and not val) :
LINE.val=abs(LINE.val-1)
elif (evt==HOMEKEY and not val) :
NSIZE.val=1.0
POS=[0,0]
elif (evt==DKEY and not val) :
DOCU=abs(DOCU-1)
elif (evt == MOUSEX): mouse_xr = val
elif (evt == MOUSEY): mouse_yr = val
elif (evt == RIGHTMOUSE and val):
if (sel==0 ) :
ANC=[(mouse_xr-size[0])-POS[0],(mouse_yr-size[1])-POS[1]]
sel=1
elif (evt == RIGHTMOUSE and not val):
sel=0
ANC=0,0
Redraw(1)
def bevent(evt):
global LINE,NSIZE,n0,x0,y0,y,TEXT, loc2
global TMATList, selmatlist, TDOCMat,XLIMIT
global xlimit
global xlimit,LCOLOR,LC,SAVE
if (evt== 1):
Exit()
@@ -474,11 +656,11 @@ def bevent(evt):
elif (evt== 6):
if XLIMIT.val==1:
xi=xwin
xi=xwin
save(x0,y0,xi+2,int(y-n0))
else:
xi=xlimit
save(x0,y0,xi+2,int(y*NSIZE.val-n0))
xi=xlimit
save(x0,y0,xi+2,int(y*NSIZE.val)-n0)
elif (evt== 7):
if exist(TEXT.val):
@@ -487,7 +669,10 @@ def bevent(evt):
TEXT.val=loc2
elif (evt== 24) or (evt in [32,33,34,35,36,37,38,39,40,41,42,43,44]):
SELMat_list()
SELMat_list()
elif (evt== 50):
LC=float(LCOLOR.val)/64.0
Blender.Redraw()

View File

@@ -322,28 +322,6 @@ def write(filename):
Blender.Draw.PupMenu("Wings Export error|Unable to generate Edge Table for mesh")
return
# if 0:
# import Tkinter, tkMessageBox
# sys.argv=['wings.pyo','wings.pyc'] # ?
#
# #Tkinter.NoDefaultRoot()
# win1 = Tkinter.Tk()
# ans = tkMessageBox.showerror("Error", message)
# win1.pack()
# print ans
# if ans:
# win1.quit()
# win1.mainloop()
#
# else:
# from Tkinter import Label
# sys.argv = 'wings.py'
# widget = Label(None, text=message)
# #widget.title("Error")
# widget.pack()
# widget.mainloop()
data = generate_data(objname, edge_table, mesh)
dsize = len(data)
Blender.Window.DrawProgressBar(0.98, "Compressing Data")

View File

@@ -423,33 +423,6 @@ static PyObject *Buffer_repr(PyObject *self)
return repr;
}
/* Not a macro so that buffer dimensions are checked.
* XXX There are other places where buffers are used, so a new macro to handle
* all such cases may be a good idea. */
static PyObject *Method_DrawPixels (PyObject *self, PyObject *args)
{
int bgl_var1, bgl_var2, bgl_var3, bgl_var4;
Buffer *bgl_buffer5;
int i, bufsize;
if (!PyArg_ParseTuple(args, "i" "i" "i" "i" "O!",
&bgl_var1, &bgl_var2, &bgl_var3, &bgl_var4, &buffer_Type, &bgl_buffer5))
return NULL;
bufsize = 1;
for (i = 0; i < bgl_buffer5->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))