Nuke Python 与资产管理系统和生产线集成
把资产管理系统集成到nuke里面,方法很多,不深入介绍,一切从简。 数据库用简单的目录结构替代,不过有一定的命名规范。代码中创建的工具都存在assertManager这个模块了。其他部分比如创建menu,hotkey等放在menu.py里面。因此记得导入import assertManger.
目录结构的示例:
大多数设备都用环境变量来辨识正在工作的镜头,一般会有相应的工具让制作人员设置环境变量。为了简化,仅设置SHOW, SEQ,SHOT:
os.environ['SHOW'] = 'showA'
os.environ['SEQ'] = 'seq1'
os.environ['SHOT'] = 'shot2'
注意:也可以使用nuke的用户knob来保存这些变量。
方便函数,返回项目目录:
def rootDir():
return '/Users/frank/Desktop/shows'
有了环境变量,以及rootDir()函数,就可以写各种函数来控制当前shot的输入输出。那就写一个获取当前镜头的nuke脚本吧:
def nukeDir():
nkDir = os.path.join( root.Dir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), 'nuke')
if not os.path.isdir( nkDir ):
raise ValueError, 'NUKE directory dose not exit'
return nkDir
要获取当前shot的nuke目录,基于当前镜头的环境变量,在menu.py里面设置动态文件很简单。
nuke.addFavoriteDir( name='NUKE SCRIPTS', directory = assertManager.nukeDir(), type = nuke.SCRIPT )
注意: 不要忘了在menu.py里面导入包含函数的模块,所有的代码都在assertManager模块
保存工作流的自定义脚本
有了nukeDir(),咱就写一个easySave函数,用户无需打开保存界面,也不用担心命令转换,就能完成保存。
def easySave():
nkDir = nukeDir()
让用户输入描述文字:
# GET DESCRIPTION FROM USER BUT STRIP ALL WHITE SPACES
description = nuke.getInput( 'script description', 'bashComp' ).replace( ' ', '' )
有了nuke目录和用户描述,就可以添加版本和命令规范了。此例中,用show,sequence,shot name紧跟用户描述和版本.
这就是easySave函数,构造命名规范。版本从1开始,检查中如果文件存在,版本增加:
def easySave():
nkDir = nukeDir()
description = nuke.getInput(' script description' , 'bashCom' ). replace(' ', '')
fileSaved = False
version = 1
while not fileSaved:
nkName = '%s_%s_%s_%s_v%02d.nk' % ( os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), description, version )
nkPath = os.path.join( nkDir, nkName )
if os.path.isfile( nkPath):
version += 1
coutinue
nuke.scriptSaveAs( nkPath )
fileSaved = True
return nkPath
上面代码用来第一次保存文件,以后就可以使用 菜单File 》 save new version来保存了:
添加自定义nuke菜单:
shotMenu = '%s-%s' % ( os.getenv('SEQ'), osgetenv('SHOT'))
nuke.menu('Nuke' ).addCommand( shotMenu + '/Easy Save', assertManger.easySave)
想要nuke的脚本都带版本号保存(如果用户忽略easySave),就用脚本来检测文件名,时候包含大小写的v后跟数字来简单判断:
def checkScriptName():
if not re.search( r' [vV]\d+', nuke.root().name() ):
raise NameError, 'Please include a version number and save script again.'
将此函数挂接到一个回调函数上,这最好放到menu.py里面:
nuke.addOnScriptSave( assertManager.checkScriptName )
如果文件不包含版本信息,此函数会抛出异常,存储就会终止,进而强迫输入版本信息。其函数的其他好处:
- 确保NUKE脚本仅保存在nuke目录下面
- 保存过程写到log文件
- 备份nuke脚本到其他地方
自定义脚本加载流程
现在看看如何通过自定义面板显示镜头nuke下的所有脚本并加载的。首先,写一个函数返回目录结构下的所有脚本,可以用glob来抓取nukeDir()目录:
def getNukeScripts():
nkFiles = glob( os.path.join( nukeDir(), '*.nk' ))
return nkFiles
给找到的基本弄个checkbox:
class NkPanel( nukescripts.PythonPanel ):
def __init__( self, nkScripts ):
nukescripts.PythonPanel.__init__(self, 'Open NUKE Script' )
self.checkboxs = []
self.nkScripts = nkScripts
for i, n in enumerate( self.nkScripts ):
k = nuke.boolean_Knob( ' nk_%s', % i, os.path.basename( n ))
self.addKnob( k )
k.setFlag( nuke.STARTLine)
self.checkboxs.append( k )
理想状态,我们会用单选按钮来让用户选择一个脚本,但nuke没此功能,那我们就挂载上一个knobChanged来保证仅有一个checkbox选中:
def knobChanged( self, knob ):
if knob in self.checkboxes:
for cb in self.checkboxes:
if knob == cb:
index = int( knob.name().split('_')[-1] )
self.selectedScript = self.nkScript[ index ]
continue
cb.setValue( False )
下面是完整的panel代码:
class NkPanel( nukescripts.PythonPanel ):
def __init__( self, nkScripts ):
nukescripts.PythonPanel.__init__(self, 'Open nuke script' )
self.checkboxes = []
self.nkScripts = nkScripts
self.selectedScript = ''
for i, n in enumerate( self.nkScripts ):
k = nuke.Boolean_Knob('nk_%s' % i, os.path.basename(n))
self.addKnob( k)
k.setFlag( nuke.STARTLINE )
self.checkboxes.append(k)
def knobChanged( self, knob ):
if knob in self.checkboxes:
for cb in self.checkboxes:
if konb == cb:
index = int(knob.name().split('_')[-1]
self.selectedScript = self.nkScripts[ index ]
continue
cb.setValue( False )
更多信息请看custom panel
有了panel代码,创建一个帮助函数(在menu.py)里面来打开panel,并返回check的值。如果需要打开所要求的脚本:
def nkPanelHelper():
nkScripts = assertManager.getNukeScripts(0
if not nkScirpts:
return
p = assertManager.NkPanel( nkScripts )
p.setMinimumSize( 200, 200 )
if p.showModalDialog():
if p.selectedScript:
nuke.scriptOpen( p.selectedScript )
代码运行后的界面:
自定义write节点
很多机构都用自己的write节点,或者修改后的write节点,来约束制作人员遵守命名规范和工作目录。如果这个有效,那将降低可能的人为错误,看看例子吧。
_images/assetMan_08.png
这仅仅是一个单独的write节点使用python和tcl挂载了gizmo属性面板。制作人员选择渲染类型就可以了(其将决定目标文件夹),赋给版本号和描述(用来建立正确的文件名)点击 “Render”,看看内容:
_images/assetMan_11.png
两个用户knob再加上后缀.exr就构成了file控制选项( 在这个例子里,我们总是渲染exr文件)
_images/assetMan_12.png
两个用户knob是dirname和filename,临时变量,因此我们不用将所有代码压缩到file控制里面:
_images/assetMan_13.png
driname是靠组合root目录,环境变量和knob type来组成的:
os.path.join( assertManager.rootDir(), os.getenv('SHOW', os.getenv('SEQ'), os.getenv('SHOT'), nuke.thisParent.knob('type').value() )
fileName 实际使用TCL语法来建立文件名,这种情况,tcl比python更加简洁:
[value parent.type]_[value parent.description]_v[format %02d [ value parent.version]]
如果你真想用python,如下:
[python '%s_%s_v%02d' % ( nuke.thisParent().knob('type').value(), nuke.thisParent().knob('description').value(), nuke.thisParent().knob('version').value() )]
有时好的TCL也不赖。
有时会保存成名为WriteAssert的gizmo,因为以Write开始的节点类,nuke会自动显示其文件名。
_images/assetMan_14.png
在menu.py将新的gizmo挂载上Image菜单,并赋给w快捷键:
nuke.menu('Nodes').addCommand('Image/WriteAssert', lambda: nuke.createNode(' WriteAssert' ), 'w' )
为了让新的gizmo正常工作,在输出目录不存在时,要自动创建。代码如下:
def createOutDirs():
trgDir = os.path.dirname( nuke.filename( nuke.thisNode() ) )
if not os.path.isdir( trgDir ):
os.makedirs( trgDir )
想让这段代码运行有两个法子:放到write节点的beforeRender knob里面 或者 全局回调函数,需要在menu.py里面添加如下语句:
nuke.addbeforeRender( assertManager.createOutDirs, nodeClass = 'Write' )
这让beforeRender的knob很干净,但是也有缺点,就是某些东西出错后,很难丢掉。
想用write的beforeRender,只需放到menu.py里面就好了
nuke.knobDefault( 'Write.beforeRender', 'assertManager.createOutDirs() ' )
_images/assetMan_09.png
这意味着很容易就能摆脱回调,这在出错时算是好事。
自定义读节点
现在来自定义Read节点,从数据库或者目录结构加载图像:
为此目的,在Read节点上添加自定义knob:
首先需要一个函数来分析目录中渲染的各种版本图像。现实中这应该是一个数据库调用来获取所有发布的图像序列。同时也需要图像序列的发现。但是还得先分析目录,并获取所有子目录 getFileSeq函数获取在version knob的所有字符串序列:
def getVersions():
types = ['plates', 'cg', 'comp', 'roto' ]
versionDict = {}
shotDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT') )
for t in types:
versionDict[t] = []
typeDir = os.path.join( shotDir, t)
for d in os.listdir( typeDir ):
path = os.path.join( typeDir, d)
if os.path.isdir( path):
versionDict[t].append( getFileSeq( path))
return versionDict
其中getFileSeq函数返回每个子目录的序列符号,注意这是简化版,实际中会用数据库调用取代。
def getFileSeq( dirPath ):
dirName = os.path.basename( dirPath )
files = glob( os.path.join( dirPath, '%s.*.*' % dirName ) )
firstString = re.findall( r'\d+', files[0] )[-1]
padding = len(firstString )
paddingString = '%02s' % padding
first = int( firstString )
last = int( re.findall( r'\d+', files[-1] )[-1] )
ext = os.path.splitext( files[0] )[-1]
fileName = '%s.%%%sd%s %s-%s' % ( dirName, str(padding).zfill(2), ext, first, last)
return os.path.join( dirPath, fileName)
有上面的代码,或者合适的数据库查询,我们能获取一个字典,包含所有版本信息。
getVersions()
可以把上面的东西放入Read节点里面, 创建上图的user konb,创建一个函数更新version knob来显示硬盘上的东西(数据库)
def createVersionKnobs():
# CREATE USER KNOBS
node = nuke.thisNode()
tabKnob = nuke.Tab_Knob( 'DB', 'DB' )
typeKnob = nuke.Enumeration_Knob( 'versionType', 'type', ['plates', 'cg', 'roto'] )
updateKnob = nuke.PyScript_Knob( 'update', 'update' )
updateKnob.setValue( 'assetManager.updateVersionKnob()' )
versionKnob = nuke.Enumeration_Knob( '_version', 'version', [] ) # DO NOT USE "VERSION" AS THE KNOB NAME AS THE READ NODE ALREADY HAS A "VERSION" KNOB
loadKnob = nuke.PyScript_Knob( 'load', 'load' )
# ASSIGN PYTHON SCRIPT AS ONE LARGE STRING
loadScript = '''#THIS ASSUMES NO WHITE SPACES IN FILE PATH
node = nuke.thisNode()
path, range = node['_version'].value().split()
first, last = range.split('-')
node['file'].setValue( path )
node['first'].setValue( int(first) )
node['last'].setValue( int(last) )'''
loadKnob.setValue( loadScript )
# ADD NEW KNOBS TO NODE
for k in ( tabKnob, typeKnob, updateKnob, versionKnob, loadKnob ):
node.addKnob( k )
# UPDATE THE VERSION KNOB SO IT SHOWS WHAT'S ON DISK / IN THE DATABASE
updateVersionKnob()
updateVersionKnob函数如下:
def updateVersionKnob():
node = nuke.thisNode()
knob = nuke.thisKnob()
# RUN ONLY IF THE TYPE KNOB CHANGES OR IF THE NODE PANEL IS OPENED
if not knob or knob.name() in [ 'versionType', 'showPanel' ]:
# GET THE VERSION DICTIONARY
versionDict = getVersions()
# POPULATE THE VERSION KNOB WITH THE VERSIONS REQUESTED THROUGH THE TYPE KNOB
node['_version'].setValues( versionDict[ node['versionType'].value() ] )
# SET THE A VALUE TO THE FIRST ITEM IN THE LIST
node['_version'].setValue(0)
因为createVersionKnobs和updateVersionKnob是回调函数,可以用nuke.thiNode() nuke.thisKnob()来引用对应的节点和knob。
在menu.py菜单中,当Read 节点创建时自动运行createVersionKnobs
nuke.addOnUserCreate( assertManager.createVersionKnobs, nodeClass = 'Read' )
同时添加回调更新version knob:
nuke.addKnobChanged( assertManger.updateVersionKnob, nodeClass = 'Read')
最后重写 热键r 来创建空的Read节点,并且附带新的DB,这样制作人员就可以快速选择一个版本
并点击laod,而不是一个个来浏览了。把这个mini函数添加到menu.py里面
def customRead():
n = nuke.creatNode('Read')
n['DB'].setFlag( 0 )
这将其赋给菜单项和热键:
nuke.menu('Nodes').addCommand( 'Image/Read', customRead, 'r' )
下面是整个assertManager模块代码:
import nukescripts
import nuke
import re
import os
from glob import glob
# SET UP EXAMPLE ENVIRONMENT
os.environ['SHOW'] = 'showA'
os.environ['SEQ'] = 'seq1'
os.environ['SHOT'] = 'shot2'
# DEFINE FACILITY ROOT
def rootDir():
return '/Users/frank/Desktop/shows'
# DEFINE SHOT'S NUKE DIR
def nukeDir():
nkDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), 'nuke' )
if not os.path.isdir( nkDir ):
raise ValueError, 'Nuke directory does not exist'
return nkDir
def easySave():
nkDir = nukeDir()
# GET DESCRIPTION FROM USER BUT STRIP ALL WHITE SPACES
description = nuke.getInput( 'script description', 'bashComp' ).replace( ' ', '' )
fileSaved = False
version = 1
while not fileSaved:
# CONSTRUCT FILE NAME
nkName = '%s_%s_%s_%s_v%02d.nk' % ( os.getenv( 'SHOW'), os.getenv( 'SEQ'), os.getenv( 'SHOT'), description, version )
# JOIN DIRECTORY AND NAME TO FORM FULL FILE PATH
nkPath = os.path.join( nkDir, nkName )
# IF FILE EXISTS VERSION UP
if os.path.isfile( nkPath ):
version += 1
continue
# SAVE NUKE SCRIPT
nuke.scriptSaveAs( nkPath )
fileSaved = True
return nkPath
# CHECK FOR VERSION IN SCRIPT NAME
def checkScriptName():
if not re.search( r'[vV]\d+', nuke.root().name() ):
raise NameError, 'Please include a version number and save script again.'
# GET ALL NUKE SCRIPTS FOR CURRENT SHOT
def getNukeScripts():
nukeDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT'), 'nuke' )
nkFiles = glob( os.path.join( nukeDir, '*.nk' ) )
return nkFiles
# PARSE "DATABASE" FOR AVAILABLE IMAGE SEQUENCES
def getVersions():
'''Return a dictionary of rendered versions per type'''
# DEFINE THE DIRECTORIES YOU WANT TO INCLUDE
types = [ 'plates', 'cg', 'comp', 'roto' ]
# INITIALISE THE DICTIONARY WE WILL RETURN AT THE END OF THE FUNCTION
versionDict = {}
# GET THE DIRECTORY BASED ON THE CURRENT SHOT ENVIRONMENT
shotDir = os.path.join( rootDir(), os.getenv('SHOW'), os.getenv('SEQ'), os.getenv('SHOT') )
# LOOP THROUGH THE FOLDERS INSIDE THE SHOT DIRECTORY AND COLLECT THE IMAGE SEQUENCES THEY CONTAIN
for t in types:
versionDict[t] = [] # THIS WILL HOLD THE FOUND SEQUENCES
typeDir = os.path.join( shotDir, t ) # GET THE CURRENT DIRECTORY PATH
for d in os.listdir( typeDir ): # LOOP THROUGH IT'S CONTENTS
path = os.path.join( typeDir, d)
if os.path.isdir( path ): # LOOP THROUGH SUB DIRECTORIES
versionDict[t].append( getFileSeq( path ) ) # RUN THE getFileSeq() FUNCTION AND APPEND IT'S OUTPUT TO THE LIST
return versionDict
# ONUSERCREATE CALLBACK FOR READ NODE
def createVersionKnobs():
'''
Add as callback to add user knobs in Read nodes.
In menu.py or init.py:
nuke.addOnUserCreate( assetManager.createVersionKnobs, nodeClass='Read' )
'''
# CREATE USER KNOBS
node = nuke.thisNode()
tabKnob = nuke.Tab_Knob( 'DB', 'DB' )
typeKnob = nuke.Enumeration_Knob( 'versionType', 'type', ['plates', 'cg', 'roto'] )
updateKnob = nuke.PyScript_Knob( 'update', 'update' )
updateKnob.setValue( 'assetManager.updateVersionKnob()' )
versionKnob = nuke.Enumeration_Knob( '_version', 'version', [] ) # DO NOT USE "VERSION" AS THE KNOB NAME AS THE READ NODE ALREADY HAS A "VERSION" KNOB
loadKnob = nuke.PyScript_Knob( 'load', 'load' )
# ASSIGN PYTHON SCRIPT AS ONE LARGE STRING
loadScript = '''#THIS ASSUMES NO WHITE SPACES IN FILE PATH
node = nuke.thisNode()
path, range = node['_version'].value().split()
first, last = range.split('-')
node['file'].setValue( path )
node['first'].setValue( int(first) )
node['last'].setValue( int(last) )'''
loadKnob.setValue( loadScript )
# ADD NEW KNOBS TO NODE
for k in ( tabKnob, typeKnob, updateKnob, versionKnob, loadKnob ):
node.addKnob( k )
# UPDATE THE VERSION KNOB SO IT SHOWS WHAT'S ON DISK / IN THE DATABASE
updateVersionKnob()
# KNOBCHANGED CALLBACK FOR CUSTOMISED READ NODE
def updateVersionKnob():
'''
Add as callback to list versions per type in Read node's user knob
In menu.py or init.py:
nuke.addKnobChanged( assetManager.updateVersionKnob, nodeClass='Read' )
'''
node = nuke.thisNode()
knob = nuke.thisKnob()
# RUN ONLY IF THE TYPE KNOB CHANGES OR IF THE NODE PANEL IS OPENED.
if not knob or knob.name() in [ 'versionType', 'showPanel' ]:
# GET THE VERSION DICTIONARY
versionDict = getVersions()
# POPULATE THE VERSION KNOB WITH THE VERSIONS REQUESTED THROUGH THE TYPE KNOB
node['_version'].setValues( versionDict[ node['versionType'].value() ] )
# SET THE A VALUE TO THE FIRST ITEM IN THE LIST
node['_version'].setValue(0)
# BEFORERENDER CALLBACK FOR WRITE ASSET GIZMO
def createOutDirs():
'''
Create output directory if it doesn't exist.
Add as callback to Write node's.
In menu.py or init.py:
# CALLBACK VIA KNOB DEFAULT
nuke.knobDefault( 'Write.beforeRender', 'assetManager.createOutDirs()')
OR:
nuke.addBeforeRender( assetManager.createOutDirs, nodeClass='Write' )
'''
trgDir = os.path.dirname( nuke.filename( nuke.thisNode() ) )
if not os.path.isdir( trgDir ):
os.makedirs( trgDir )
def getFileSeq( dirPath ):
'''Return file sequence with same name as the parent directory. Very loose example!!'''
dirName = os.path.basename( dirPath )
# COLLECT ALL FILES IN THE DIRECTORY THAT HVE THE SAME NAME AS THE DIRECTORY
files = glob( os.path.join( dirPath, '%s.*.*' % dirName ) )
# GRAB THE RIGHT MOST DIGIT IN THE FIRST FRAME'S FILE NAME
firstString = re.findall( r'\d+', files[0] )[-1]
# GET THE PADDING FROM THE AMOUNT OF DIGITS
padding = len( firstString )
# CREATE PADDING STRING FRO SEQUENCE NOTATION
paddingString = '%02s' % padding
# CONVERT TO INTEGER
first = int( firstString )
# GET LAST FRAME
last = int( re.findall( r'\d+', files[-1] )[-1] )
# GET EXTENSION
ext = os.path.splitext( files[0] )[-1]
# BUILD SEQUENCE NOTATION
fileName = '%s.%%%sd%s %s-%s' % ( dirName, str(padding).zfill(2), ext, first, last )
# RETURN FULL PATH AS SEQUENCE NOTATION
return os.path.join( dirPath, fileName )
# PANEL TO SHOW NUKE SCRIPS FOR CURRENT SHOT
class NkPanel( nukescripts.PythonPanel ):
def __init__( self, nkScripts ):
nukescripts.PythonPanel.__init__( self, 'Open Nuke Script' )
self.checkboxes = []
self.nkScripts = nkScripts
self.selectedScript = ''
for i, n in enumerate( self.nkScripts ):
# PUT INDEX INTO KNOB NAMES SO WE CAN IDENTIFY THEM LATER
k = nuke.Boolean_Knob( 'nk_%s' % i, os.path.basename( n ) )
self.addKnob( k )
k.setFlag( nuke.STARTLINE )
self.checkboxes.append( k )
def knobChanged( self, knob ):
if knob in self.checkboxes:
# MAKE SURE ONLY ONE KNOB IS CHECKED
for cb in self.checkboxes:
if knob == cb:
# EXTRACT THE INDEX FORM THE NAME AGAIN
index = int( knob.name().split('_')[-1] )
self.selectedScript = self.nkScripts[ index ]
continue
cb.setValue( False )
此部分中menu.py的代码:
import assetManager
# CREATE A READ NODE AND OPEN THE "DB" TAB
def customRead():
n = nuke.createNode( 'Read' )
n['DB'].setFlag( 0 )
# ADD CUSTOM READ AND WRITE TO TOOLBAR
nuke.menu( 'Nodes' ).addCommand( 'Image/WriteAsset', lambda: nuke.createNode( 'WriteAsset' ), 'w' )
nuke.menu( 'Nodes' ).addCommand( 'Image/Read', customRead, 'r' )
# ADD EASY SAVE TO SHOT MENU
shotMenu = '%s - %s' % ( os.getenv( 'SEQ' ), os.getenv('SHOT') )
nuke.menu( 'Nuke' ).addCommand( shotMenu+'/Easy Save', assetManager.easySave )
# SET FILE BROWSER FAVORITES
nuke.addFavoriteDir(
name = 'NUKE SCRIPTS',
directory = assetManager.nukeDir(),
type = nuke.SCRIPT)
# HELPER FUNCTION FOR NUKE SCRIPT PANEL
def nkPanelHelper():
# GET ALL NUKE SCRIPTS FOR CURRENT SHOT
nkScripts = assetManager.getNukeScripts()
if not nkScripts:
# IF THERE ARE NONE DON'T DO ANYTHING
return
# CREATE PANEL
p = assetManager.NkPanel( nkScripts )
# ADJUST SIZE
p.setMinimumSize( 200, 200 )
# IF PANEL WAS CONFIRMED AND A NUKE SCRIPT WAS SELECTED, OPEN IT
if p.showModalDialog():
if p.selectedScript:
nuke.scriptOpen( p.selectedScript )
# ADD CALLBACKS
nuke.addOnScriptSave( assetManager.checkScriptName )
nuke.addOnUserCreate( nkPanelHelper, nodeClass='Root')
nuke.addOnUserCreate( assetManager.createVersionKnobs, nodeClass='Read' )
nuke.addKnobChanged( assetManager.updateVersionKnob, nodeClass='Read' )
#nuke.addBeforeRender( assetManager.createOutDirs, nodeClass='Write' )
nuke.knobDefault( 'Write.beforeRender', 'assetManager.createOutDirs()')
自定义UDIM分析函数
UDIM import用的是标准分析转换。被UDIM import使用的分析函数可以重定义,使其支持其他文件名转换。想用个人UMIM分析那就运行下面代码:
nukescripts.udim_import( myParsingFunc, "UV" )
第一个参数是分析函数名。第二个参数是导入对话框中的列名,来确认标题坐标。
通过唯一值可以确定纹理(UDIM)或者数值对(u,v或者s,t)。UDIM import python脚本两者都支持。重定义的分析函数需要解码文件名字符串并返回UDIM或者u,v分块纹理或者一个元组整形数值。如果如法确认,它将返回None
下面是一个分析函数的列子, 会解码文件名: filename.1003.v10.tif:
import re
def myParsingFunc( f ):
sequences = re.split("[._]+", f )
udim = None
for s in sequences:
try:
udim = int(s)
except ValueError:
udim = None
if udim>1000 and udim <2000:
break
if udim == None:
return None
return udim
这是一个分析 filename._u00_v00.tif:
import re
def myParsingFunc(f):
sequences = re.split("[._]+", f)
u = None
v = None
for s in sequences:
try:
head = s[0]
tail = s[1: len(s) ]
if head == 'u':
u = int(tail)
if head == 'v':
v = int(tail)
except ValueError:
u = None
v = None
if u ==None or v ==None:
return None
return u,v
下面是分析filename_s04t00_00_v019.tif的例子:
import re
def myParsingFunc( f):
sequences = f.split("_")
s_value = None
t_value = None
for s in sequences:
if len(s) != 6:
continue
if s[0] != 's' or s[3] !='t':
continue
try:
s_value = int( s[1:3] )
t_value = int( s[4:6] )
except ValueError:
s_value = None
t_value = None
if s_value == None or t_value ==None:
return None
return s_value, t_value