# -*- coding: utf-8 -*-
import pymel.core as pm
import maya.cmds as cmds
import maya.OpenMaya as om
import mtoa.ui.ae.lightTemplate as lightTemplate
from mtoa.ui.ae.utils import aeCallback
import mtoa.ui.ae.templates as templates
import mtoa.callbacks as callbacks
import mtoa.core as core
import re
import mtoa.aovs as aovs

class ParticleTemplate(templates.ShapeTranslatorTemplate):
    def setup(self):
        self.commonShapeAttributes()
        self.addControl("aiRenderPointsAs", label="渲染点AS")
        self.addControl("aiMinParticleRadius", label="最小粒子半径")
        self.addControl("aiRadiusMultiplier", label="半径乘数")
        self.addControl("aiMaxParticleRadius", label="最大粒子半径")
        self.addControl("aiMinPixelWidth", label="最小像素宽度")
        self.addSeparator()   
        self.addControl("aiExportParticleIDs", label="导出粒子标识")
        self.addControl("aiExportAttributes", label="导出属性")
        self.addSeparator()
        self.addControl("aiDeleteDeadParticles", label="删除死亡粒子")
        self.addSeparator()
        self.addControl('aiStepSize', label="体积步长")
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")
        
templates.registerTranslatorUI(ParticleTemplate, "粒子", "<built-in>")

class NParticleTemplate(templates.ShapeTranslatorTemplate):
    def setup(self):
        self.commonShapeAttributes()
        self.addControl("aiRenderPointsAs", label="渲染点As")
        self.addControl("aiMinParticleRadius", label="最小粒子半径")
        self.addControl("aiRadiusMultiplier", label="半径乘数")
        self.addControl("aiMaxParticleRadius", label="最大粒子半径")
        self.addControl("aiMinPixelWidth", label="最小像素宽度")
        self.addSeparator()   
        self.addControl("aiExportParticleIDs", label="导出粒子标识")
        self.addControl("aiExportAttributes", label="导出属性")
        self.addSeparator()
        self.addControl("aiInterpolateBlur", label="插值运动模糊")
        self.addControl("aiEvaluateEvery", label="nCache分析间隔", annotation="Use nCache's \"Evaluate every # frame(s)\" param value")
        self.addSeparator()
        self.addControl("aiDeleteDeadParticles", label="删除死亡粒子")
        self.addSeparator()
        self.addControl('aiStepSize', label="体积步长")
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")
    
templates.registerTranslatorUI(NParticleTemplate, "nParticle", "<built-in>")

class MeshTemplate(templates.ShapeTranslatorTemplate):
    def setup(self):
        self.commonShapeAttributes()
        
        self.beginLayout("导出", collapse=False)
        self.addControl("aiExportTangents", label="导出切线")
        self.addControl("aiExportColors", label="导出顶点颜色")
        self.addControl("aiExportRefPoints", label="导出参考位置")
        self.addControl("aiExportRefNormals", label="导出参考法线")
        self.addControl("aiExportRefTangents", label="导出参考切线")
        
        self.addSeparator()
        self.addControl("aiSssSetname", label="SSS集名称")
        self.addSeparator()
        self.addControl("aiMotionVectorSource", label="运动向量源")
        self.addControl("aiMotionVectorUnit", label="运动向量单位")
        self.addControl("aiMotionVectorScale", label="运动向量缩放")

        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")
        self.endLayout()
        
        self.beginLayout('细分', collapse=True)
        self.addControl("aiSubdivType", label="类型")
        self.addControl("aiSubdivIterations", label="迭代")
        self.addControl("aiSubdivAdaptiveMetric", label="自适应度量")
        self.addControl("aiSubdivPixelError", label="自适应错误")
        self.addControl("aiSubdivAdaptiveSpace", label="自适应空间")
        # TODO: add dicing camera UI
        self.addControl("aiSubdivUvSmoothing", label="UV平滑")
        self.addControl("aiSubdivSmoothDerivs", label="平滑切线")
        self.endLayout()
        
        self.beginLayout('位移属性', collapse=True)
        self.addControl("aiDispHeight", label="高度")
        self.addControl("aiDispPadding", label="边界填充")
        self.addControl("aiDispZeroValue", label="标量零值")
        self.addControl("aiDispAutobump", label="自动碰撞")
        self.endLayout()
        self.beginLayout('体积属性', collapse=True)
        self.addControl('aiStepSize', label='步长')
        self.endLayout()
        
        # FIXME: these are not on the shape node!
#       ui.addSeparator()
#       ui.addControl("enableProcedural")
#       ui.addControl("dso")

class ProceduralTemplate(templates.ShapeTranslatorTemplate):

    def setup(self):
        self.commonShapeAttributes()
        self.addSeparator()
        self.addControl('dso', label='路径')
        self.addControl('data', label='数据')
#        self.addControl('deferStandinLoad', label='Defer Procedural Load')
        self.addControl("aiUserOptions", label="用户选项")

templates.registerTranslatorUI(MeshTemplate, "mesh", "polymesh")
templates.registerTranslatorUI(ProceduralTemplate, "mesh", "procedural")
core.registerDefaultTranslator("mesh", "polymesh")
templates.registerTranslatorUI(MeshTemplate, "nurbsSurface", "<built-in>")

class HairSystemTemplate(templates.ShapeTranslatorTemplate):
    def shaderCreate(self, attrName):
        cmds.setUITemplate('attributeEditorPresetsTemplate', pushTemplate=True)
        cmds.attrNavigationControlGrp("HairSystemTemplateShader", attribute=attrName, label="Hair Shader")
        cmds.setUITemplate(popTemplate=True)

    def shaderUpdate(self, attrName):
        cmds.attrNavigationControlGrp("HairSystemTemplateShader", edit=True, attribute=attrName)

    def minPixelCreate(self, attrName):
        cmds.setUITemplate('attributeEditorPresetsTemplate', pushTemplate=True)
        isEnabled = not (cmds.getAttr("%s.aiMode" % (attrName.split(".")[0])) is 1)
        cmds.attrFieldSliderGrp("HairTemplateMinPixelWidth", label="Min Pixel Width",
                            attribute=attrName, enable=isEnabled)
        cmds.setUITemplate(popTemplate=True)
    
    def minPixelUpdate(self, attrName):
        isEnabled = not (cmds.getAttr("%s.aiMode" % (attrName.split(".")[0])) is 1)
        cmds.attrFieldSliderGrp("HairTemplateMinPixelWidth", edit=True,
                            attribute=attrName, enable=isEnabled)

    def indirectDiffuseCreate(self, attrName):
        cmds.setUITemplate('attributeEditorPresetsTemplate', pushTemplate=True)
        isEnabled = not (cmds.getAttr("%s.aiMode" % (attrName.split(".")[0])) is 1)
        cmds.attrFieldSliderGrp("HairTemplateIndirectDiffuse", label="Indirect Diffuse",
                            attribute=attrName, enable=isEnabled)
        cmds.setUITemplate(popTemplate=True)
    
    def indirectDiffuseUpdate(self, attrName):
        isEnabled = not (cmds.getAttr("%s.aiMode" % (attrName.split(".")[0])) is 1)
        cmds.attrFieldSliderGrp("HairTemplateIndirectDiffuse", edit=True,
                            attribute=attrName, enable=isEnabled)

    def modeChanged(self, *args):
        try:
            if cmds.getAttr(self.nodeAttr('aiMode')) == 1:
                cmds.attrFieldSliderGrp("HairTemplateMinPixelWidth", edit=True, enable=False)
            else:
                cmds.attrFieldSliderGrp("HairTemplateMinPixelWidth", edit=True, enable=True)
        except RuntimeError:
            # this callback runs immediately, before HairTemplateMinPixelWidth exists
            pass

    def setup(self):
        self.addControl("primaryVisibility")
        self.addControl("castsShadows")
        self.addSeparator()
        self.commonShapeAttributes()
        self.addSeparator()
        self.addControl("aiExportHairUVs", label="导出头发UV")
        self.addControl("aiExportHairColors", label="导出头发颜色")
        self.addControl("aiOverrideHair", label="覆盖头发")
        self.addCustom("aiHairShader", self.shaderCreate, self.shaderUpdate)
        self.addSeparator()
        self.addCustom("aiMinPixelWidth", self.minPixelCreate, self.minPixelUpdate)
        self.addCustom("aiIndirectDiffuse", self.indirectDiffuseCreate, self.indirectDiffuseUpdate)
        self.addControl("aiMode", label="模式", changeCommand=self.modeChanged)
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")
templates.registerAETemplate(HairSystemTemplate, "hairSystem")

class FLuidShapeTemplate(templates.ShapeTranslatorTemplate):
    def volumeNoiseCreate(self, attrName):
        cmds.setUITemplate('attributeEditorPresetsTemplate', pushTemplate=True)
        cmds.attrNavigationControlGrp("FluidTemplateVolumeTexture", attribute=attrName, label="Texture")
        cmds.setUITemplate(popTemplate=True)

    def volumeNoiseUpdate(self, attrName):
        cmds.attrNavigationControlGrp("FluidTemplateVolumeTexture", edit=True, attribute=attrName)
        
    def setup(self):
        self.addControl("aiStepSize", label="步长")
        self.addControl("aiEnableDeformationBlur", label="启用变形模糊")
        self.addControl("aiMotionVectorScale", label="运动向量缩放")
        self.addControl("aiFilterType", label="过滤器类型")
        self.addControl("aiPhaseFunc", label="函数各向异性")
        self.addSeparator()
        self.commonShapeAttributes()
        self.beginLayout("自定义纹理", collapse=False)
        self.addControl("aiOverrideTextures", label="覆盖流体纹理")        
        self.addControl("aiTextureAffectColor", label="纹理颜色")
        self.addControl("aiTextureAffectIncand", label="纹理白炽度")
        self.addControl("aiTextureAffectOpacity", label="纹理不透明度")
        self.addControl("aiTextureCoordinateMethod", label="坐标")
        self.addCustom("aiVolumeTexture", self.volumeNoiseCreate, self.volumeNoiseUpdate)
        self.endLayout()
        self.addControl("aiUserOptions", label="用户选项")
templates.registerAETemplate(FLuidShapeTemplate, "fluidShape")

class NurbsCurveTemplate(templates.ShapeTranslatorTemplate):
    def minPixelCreate(self, attrName):
        cmds.setUITemplate("attributeEditorPresetsTemplate", pushTemplate=True)
        isEnabled = not (cmds.getAttr("%s.aiMode" % (attrName.split(".")[0])) is 1)
        cmds.attrFieldSliderGrp("NurbsCurveTemplateMinPixelWidth", label="最小像素宽度",
                            attribute=attrName, enable=isEnabled)
        cmds.setUITemplate(popTemplate=True)
    
    def minPixelUpdate(self, attrName):
        isEnabled = not (cmds.getAttr("%s.aiMode" % (attrName.split(".")[0])) is 1)
        cmds.attrFieldSliderGrp("NurbsCurveTemplateMinPixelWidth", edit=True,
                            attribute=attrName, enable=isEnabled)
    
    def modeChanged(self, *args):
        try:
            if cmds.getAttr(self.nodeAttr('aiMode')) == 1:
                cmds.attrFieldSliderGrp("NurbsCurveTemplateMinPixelWidth", edit=True, enable=False)
            else:
                cmds.attrFieldSliderGrp("NurbsCurveTemplateMinPixelWidth", edit=True, enable=True)
        except RuntimeError:
            # this callback runs immediately, before NurbsCurveTemplateMinPixelWidth exists
            pass
            
    def setup(self):
        #pm.mel.eval('AEaddRampControl("widthProfile")')
        #pm.mel.eval('AEaddRampControl("colorTable")')
        self.addControl("aiRenderCurve")
        self.addControl("aiCurveWidth")
        self.addControl("aiSampleRate")
        self.addControl("aiCurveShader")
        self.addSeparator()
        self.addControl("primaryVisibility")
        self.addControl("castsShadows")
        self.addSeparator()
        self.addControl("aiExportRefPoints", "Export Reference Points")
        self.addSeparator()
        self.commonShapeAttributes()
        self.addSeparator()
        self.addCustom("aiMinPixelWidth", self.minPixelCreate, self.minPixelUpdate)
        self.addControl("aiMode", label="Mode", changeCommand=self.modeChanged)
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")
templates.registerTranslatorUI(NurbsCurveTemplate, "nurbsCurve", "<built-in>")


class DirectionalLightTemplate(lightTemplate.LightTemplate):
    def setup(self):
        self.setupColorTemperature("Directional")
        self.addControl("aiExposure", label="曝光")
        self.addControl("aiAngle", label="角度")
        
        self.addSeparator()
        
        self.addControl("aiSamples", label="采样")
        self.addControl("aiNormalize", label="标准化")
        
        self.addSeparator()
        
        self.addControl("aiCastShadows", label="投射阴影")
        self.addControl("aiShadowDensity", label="阴影密度")
        
        self.addSeparator()                
        self.commonLightAttributes()

templates.registerTranslatorUI(DirectionalLightTemplate, "directionalLight")

class PointLightTemplate(lightTemplate.LightTemplate):
    def setup(self):
        self.setupColorTemperature("Point")
        self.addControl("aiExposure")
        
        self.addSeparator()
        
        self.addControl("aiSamples")
        self.addControl("aiRadius")
        self.addControl("aiNormalize")

        self.addSeparator()

        self.addControl("aiCastShadows")
        self.addControl("aiShadowDensity")

        self.addSeparator()

        self.commonLightAttributes()

templates.registerTranslatorUI(PointLightTemplate, "pointLight")

class SpotLightTemplate(lightTemplate.LightTemplate):
    def setup(self):
        self.setupColorTemperature("Spot")
        self.addControl("aiExposure")
        
        self.addSeparator()
                        
        self.addControl("aiSamples")
        self.addControl("aiRadius")
        self.addControl("aiNormalize")
        self.addControl("aiRoundness")
        
        self.addSeparator()

        self.addControl("aiCastShadows")
        self.addControl("aiShadowDensity")

        self.addSeparator()

        self.addControl("aiAspectRatio")
        self.addControl("aiLensRadius")

        self.addSeparator()

        self.commonLightAttributes()

templates.registerTranslatorUI(SpotLightTemplate, "spotLight")

class AreaLightTemplate(lightTemplate.LightTemplate):
    def setup(self):
        self.setupColorTemperature("Area")
        self.addControl("aiExposure")
        
        self.addSeparator()
        
        self.addControl("aiSamples")
        self.addControl("aiNormalize")

        self.addSeparator()

        self.addControl("aiCastShadows")
        self.addControl("aiShadowDensity")

        self.addSeparator()

        self.addControl("aiResolution")
        self.addControl("aiSpread")
                
        self.addSeparator()

        self.commonLightAttributes()

templates.registerTranslatorUI(AreaLightTemplate, "areaLight")

templates.registerAETemplate(templates.TranslatorControl, "camera", label="相机类型")

class CameraTemplate(templates.AttributeTemplate):
    def syncAttribute(self, attr, control, valueField, positionField):
        attr = self.nodeAttr('aiShutterCurve')
        values = cmds.gradientControlNoAttr( control, query=True, asString=True) 
        valuesSplit = values.split(',')

        points = []

        for i in range(0,len(valuesSplit)/3):
            points.append([valuesSplit[i*3+1],valuesSplit[i*3],0])
            
        current = cmds.gradientControlNoAttr( control, query=True, currentKey=True) 
        cmds.floatField(valueField, edit=True, value=float(points[current][1]))
        cmds.floatField(positionField, edit=True, value=float(points[current][0]))
        points[current][2] = 1
        points.sort()
        
        size = cmds.getAttr(attr, size=True)
        for i in range(0,size):
            cmds.removeMultiInstance(attr+'['+str(i)+']')
        
        curveString = ""
        for i in range(0,len(points)):
            cmds.setAttr(attr+'['+str(i)+'].aiShutterCurveX',float(points[i][0]))
            cmds.setAttr(attr+'['+str(i)+'].aiShutterCurveY',float(points[i][1]))
            if i is 0:
                curveString += points[i][1] +"," + points[i][0] +",1"
            else:
                curveString += ","+points[i][1] +"," + points[i][0] +",1"
            
        # We save the curve points sorted in the attribute, so we will also resort the points in
        #  the gradient control
        current = [x[2] for x in points].index(1)
        cmds.gradientControlNoAttr( control, edit=True, currentKey=current, asString=curveString) 
            
    def updateValue(self, attr, control, valueField, positionField):
        value = pm.floatField(valueField, query=True, value=True)
        
        values = cmds.gradientControlNoAttr( control, query=True, asString=True) 
        valuesSplit = values.split(',')
            
        current = cmds.gradientControlNoAttr( control, query=True, currentKey=True) 
        
        valuesSplit[current*3] = str(value)
        values = ",".join(valuesSplit)
        
        pm.gradientControlNoAttr( control, edit=True, asString=values)
        self.syncAttribute(attr, control, valueField, positionField)
        
    def updatePosition(self, attr, control, valueField, positionField):
        value = pm.floatField(positionField, query=True, value=True)
        
        values = cmds.gradientControlNoAttr( control, query=True, asString=True) 
        valuesSplit = values.split(',')
            
        current = cmds.gradientControlNoAttr( control, query=True, currentKey=True) 
        
        valuesSplit[current*3+1] = str(value)
        values = ",".join(valuesSplit)
        
        pm.gradientControlNoAttr( control, edit=True, asString=values)
        self.syncAttribute(attr, control, valueField, positionField)
        
    def createRamp( self, attr ):
        #Create the control fields
        pm.columnLayout( )
        
        cmds.rowLayout(nc=2, cw2=(142,220))
        pm.text("Shutter Curve", label="快门曲线");
        pm.text(" ");
        pm.cmds.setParent('..')
        
        cmds.rowLayout("ShutterCurveRowLayout",nc=2, cw2=(142,220))
        
        pm.columnLayout("ShutterCurveColumLayout")
        cmds.rowLayout("ShutterCurveValueLayout", nc=2, cw2=(60,45))
        pm.text("Value", label="值");
        valueField = pm.floatField("ShutterCurveValueField");
        pm.cmds.setParent('..')
        
        pm.rowLayout("ShutterCurvePositionLayout", nc=2, cw2=(60,45))
        pm.text("Position", label="定位");
        
        positionField = cmds.floatField("ShutterCurvePositionField");
        pm.cmds.setParent('..')
        
        '''pm.rowLayout(nc=2, cw2=(60,65))
        pm.text("Interpol.");
        pm.optionMenu(changeCommand=self.updateRamp )
        pm.menuItem( label='None' )
        pm.menuItem( label='Linear' )
        pm.menuItem( label='Smooth' )
        pm.menuItem( label='Spline' )
        pm.cmds.setParent('..')'''
        pm.cmds.setParent('..')
        
        gradient = pm.gradientControlNoAttr("ShutterCurveGradientControl", w=200, h=100 )
        pm.gradientControlNoAttr( gradient, edit=True, changeCommand=pm.Callback(self.syncAttribute,attr,gradient, valueField, positionField) )
        
        #Initialize the curve with the values in the attribute
        curveString = ""
        attr = self.nodeAttr('aiShutterCurve')
        size = cmds.getAttr(attr, size=True)
        startX = 0
        startY = 1
        if size > 0:
            x = cmds.getAttr(attr+'[0].aiShutterCurveX')
            y = cmds.getAttr(attr+'[0].aiShutterCurveY')
            startX = x
            startY = y
            curveString += str(y) +"," + str(x) +",1"
        else:
            curveString += "1,0,1"
        for i in range(1,size):
            x = cmds.getAttr(attr+'['+str(i)+'].aiShutterCurveX')
            y = cmds.getAttr(attr+'['+str(i)+'].aiShutterCurveY')
            curveString += ","+str(y) +"," + str(x) +",1"
            
        cmds.gradientControlNoAttr( gradient, edit=True, asString=curveString) 
        
        pm.floatField(valueField, edit=True, value=startY, changeCommand=pm.Callback(self.updateValue, attr, gradient, valueField, positionField))
        pm.floatField(positionField, edit=True, value=startX, changeCommand=pm.Callback(self.updatePosition, attr, gradient, valueField, positionField))
        
    def updateRamp( self, attr ):
        name = self.nodeName
        translator = cmds.getAttr(self.nodeAttr('aiTranslator'))

        uiParent = pm.setParent( q = True )
        controls = pm.columnLayout( uiParent, q=True, ca=True )
        
        curveString = ""
        attr = self.nodeAttr('aiShutterCurve')
        size = cmds.getAttr(attr, size=True)
        if size > 0:
            x = cmds.getAttr(attr+'[0].aiShutterCurveX')
            y = cmds.getAttr(attr+'[0].aiShutterCurveY')
            curveString += str(y) +"," + str(x) +",1"
        else:
            curveString += "1,0,1"
        for i in range(1,size):
            x = cmds.getAttr(attr+'['+str(i)+'].aiShutterCurveX')
            y = cmds.getAttr(attr+'['+str(i)+'].aiShutterCurveY')
            curveString += ","+str(y) +"," + str(x) +",1"
            
        valuesSplit = curveString.split(",")
        
        if controls:
            for c in controls:
                control = c +"|ShutterCurveRowLayout|ShutterCurveGradientControl"
                valueField = c +"|ShutterCurveRowLayout|ShutterCurveColumLayout|ShutterCurveValueLayout|ShutterCurveValueField"
                positionField = c +"|ShutterCurveRowLayout|ShutterCurveColumLayout|ShutterCurvePositionLayout|ShutterCurvePositionField"
                cmds.gradientControlNoAttr( control, edit=True, asString=curveString)
                current = cmds.gradientControlNoAttr( control, query=True, currentKey=True) 
                
                pm.floatField(valueField, edit=True, value=float(valuesSplit[current*3]))
                pm.floatField(positionField, edit=True, value=float(valuesSplit[current*3+1]))

    
    def addCommonAttributes(self):
        self.addControl("aiExposure", label="曝光")
        self.addControl("aiFiltermap", label="过滤图")
        self.addSeparator()
        self.addControl("aiRollingShutter", label="滚动快门")
        self.addControl("aiRollingShutterDuration", label="滚动快门时间")
        
    def addDOFAttributes(self):
        self.addSeparator()
        self.addControl("aiEnableDOF", label="启用DOF")
        self.addControl("aiFocusDistance", label="聚焦")
        self.addControl("aiApertureSize", label="光圈大小")
        self.addControl("aiApertureBlades", label="光圈叶片")
        self.addControl("aiApertureBladeCurvature", label="光圈叶片曲率")
        self.addControl("aiApertureRotation", label="光圈旋转")
        self.addControl("aiApertureAspectRatio", label="光圈长宽比")
        
    def addShutterAttributes(self):
        self.addSeparator()
        self.addControl("motionBlurOverride", label="相机运动模糊")
        self.addControl("aiShutterStart", label="快门开始")
        self.addControl("aiShutterEnd", label="快门结束")
        self.addControl("aiShutterType", label="快门类型")
        self.addCustom( "aiShutterCurve", self.createRamp, self.updateRamp )
        

class PerspCameraTemplate(CameraTemplate):
    def setup(self):
        self.addCommonAttributes()
        self.addDOFAttributes()
        self.addSeparator()
        self.addControl('aiUvRemap', label="UV重新映射")
        self.addShutterAttributes()
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")

templates.registerTranslatorUI(PerspCameraTemplate, "camera", "perspective")
templates.registerTranslatorUI(PerspCameraTemplate, "stereoRigCamera", "perspective")


class OrthographicTemplate(CameraTemplate):
    def setup(self):
        self.addCommonAttributes()
        self.addShutterAttributes()
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")

templates.registerTranslatorUI(OrthographicTemplate, "camera", "orthographic")
templates.registerTranslatorUI(OrthographicTemplate, "stereoRigCamera", "orthographic")

class FisheyeCameraTemplate(CameraTemplate):
    def setup(self):
        self.addCommonAttributes()
        self.addDOFAttributes()
        self.addSeparator()
        self.addControl('aiFov')
        self.addControl('aiAutocrop')
        self.addShutterAttributes()
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")

templates.registerTranslatorUI(FisheyeCameraTemplate, "camera", "fisheye")
templates.registerTranslatorUI(FisheyeCameraTemplate, "stereoRigCamera", "fisheye")

class CylCameraTemplate(CameraTemplate):
    def setup(self):
        self.addCommonAttributes()
        self.addControl('aiHorizontalFov', label="水平Fov")
        self.addControl('aiVerticalFov', label="垂直Fov")
        self.addControl('aiProjective', label="投影")
        self.addShutterAttributes()
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")

templates.registerTranslatorUI(CylCameraTemplate, "camera", "cylindrical")
templates.registerTranslatorUI(CylCameraTemplate, "stereoRigCamera", "cylindrical")

class SphericalCameraTemplate(CameraTemplate):
    def setup(self):
        self.addCommonAttributes()
        self.addShutterAttributes()
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")

templates.registerTranslatorUI(SphericalCameraTemplate, "camera", "spherical")
templates.registerTranslatorUI(SphericalCameraTemplate, "stereoRigCamera", "spherical")

class VrCameraTemplate(CameraTemplate):
    def setup(self):
        self.beginLayout("主要属性", collapse=False)
        self.addControl("aiMode", label="模式")
        self.addControl("aiProjection", label="投影")
        self.addControl("aiEyeSeparation", label="眼睛距离")
        self.addControl("aiEyeToNeck", label="用户选项")
        self.endLayout()

        self.beginLayout("合并", collapse=False)
        self.addControl("aiTopMergeMode", label="顶端合并模式")
        self.addControl("aiTopMergeAngle", label="顶端合并角度")
        self.addSeparator()
        self.addControl("aiBottomMergeMode", label="底部合并模式")
        self.addControl("aiBottomMergeAngle", label="底部合并角度")
        self.addSeparator()
        self.addControl("aiMergeShader", label="合并着色器")
        self.endLayout()

        self.beginLayout("公共属性", collapse=False)
        self.addCommonAttributes()
        self.addShutterAttributes()
        self.addSeparator()
        self.addControl("aiUserOptions", label="用户选项")
        self.endLayout()

templates.registerTranslatorUI(VrCameraTemplate, "camera", "vr_camera")

def cameraOrthographicChanged(orthoPlug, *args):
    "called to sync .aiTranslator when .orthographic changes"
    if not core.arnoldIsCurrentRenderer(): return
    fnCam = om.MFnCamera(orthoPlug.node())
    transPlug = fnCam.findPlug('aiTranslator')
    if not transPlug.isNull():
        isOrtho = orthoPlug.asBool()
        
        currTrans = transPlug.asString()
        #print "cameraOrthographicChanged", fnCam.name(), currTrans, isOrtho
        newTrans = None
        if isOrtho and currTrans != 'orthographic':
            newTrans = 'orthographic'
        elif not isOrtho and currTrans == 'orthographic':
            newTrans = 'perspective'
        #print "newTrans", newTrans
        if newTrans:
            transPlug.setString(newTrans)

def cameraTranslatorChanged(transPlug, *args):
    "called to sync .orthographic when .aiTranslator changes"
    if not core.arnoldIsCurrentRenderer(): return
    fnCam = om.MFnCamera(transPlug.node())
    currTrans = transPlug.asString()
    orthoPlug = fnCam.findPlug('orthographic')
    isOrtho = orthoPlug.asBool()
    #print "cameraTranslatorChanged", fnCam.name(), currTrans, isOrtho
    # when a file is opening, we need to choose one attribute to lead, because
    # the order that attributes are set is unpredictable. This fixes a case
    # where translators may have gotten out of sync
    if om.MFileIO.isOpeningFile():
        if isOrtho and currTrans != 'orthographic':
            orthoPlug.setBool(True)
    else:
        if not isOrtho and currTrans == 'orthographic':
            orthoPlug.setBool(True)
        elif isOrtho and currTrans != 'orthographic':
            orthoPlug.setBool(False)

def getCameraDefault(obj):
    isOrtho = pm.api.MFnDependencyNode(obj).findPlug("orthographic").asBool()
    default = 'orthographic' if isOrtho else 'perspective'
    return default

templates.registerDefaultTranslator('camera', getCameraDefault)
templates.registerDefaultTranslator('stereoRigCamera', getCameraDefault)

callbacks.addAttributeChangedCallbacks('camera',
                                       [('aiTranslator', cameraTranslatorChanged),
                                        ('orthographic', cameraOrthographicChanged)])

callbacks.addAttributeChangedCallbacks('stereoRigCamera',
                                       [('aiTranslator', cameraTranslatorChanged),
                                        ('orthographic', cameraOrthographicChanged)])

def registerDriverTemplates():
    skipDrivers = ['exr', 'deepexr']
    # register driver templates
    for transName, arnoldNode in core.listTranslators("aiAOVDriver"):
        if not (transName in skipDrivers): # we want to use a custom ui for the EXR translator
            templates.registerAutoTranslatorUI(arnoldNode, "aiAOVDriver", transName, skipEmpty=True)

    templates.registerDefaultTranslator('aiAOVDriver', 'exr')

templatesNames = []
    
class EXRDriverTranslatorUI(templates.AttributeTemplate):

    def selectedAttrName(self, nodeName):
        # unfortunately the functions below might be called 
        # with NodeNames corresponding to a previously selected node
        # (maya doesn't rebuild the UI but just updates the param values so the callbacks remain)
        tokens = nodeName.split('.')

        # same object, nothing to change
        if (tokens[0] == self.nodeName):
            return nodeName

        #replace the first token by the actual self name
        result = self.nodeName
        tokens.pop(0)
        
        for token in tokens:
            result += "." + token

        return result


    def changeAttrName(self, nodeName, attrNameText, index):
        # Get the attribute name, type and value
        attrName = nodeName+'['+str(index)+']'
        metadata = cmds.getAttr(attrName)
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))
        
        # Get the new name
        name = cmds.textField(attrNameText, query=True, text=True)
        
        # Update the name in all the templates
        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.textField(templateName+"|mtoa_exrMetadataRow_"+str(index)+"|MtoA_exrMAttributeName", edit=True, text=name.replace(" ", ""))
        
        # Update the metadata value
        metadata = result[0]+" "+name.replace(" ", "")+" "+result[2]
        cmds.setAttr(attrName, metadata, type="string")
    
    def changeAttrType(self, nodeName, menu, index):
        # Get the attribute name, type and value
        attrName = nodeName+'['+str(index)+']'
        metadata = cmds.getAttr(attrName)
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))
        
        # Get the new type
        typeNumber = cmds.optionMenu(menu, query=True, select=True)
        type = cmds.optionMenu(menu, query=True, value=True)
        
        # Update the type in all the templates
        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.optionMenu(templateName+"|mtoa_exrMetadataRow_"+str(index)+"|MtoA_exrMAttributeType", edit=True, select=typeNumber)
            
        # Update the metadata value
        metadata = type+" "+result[1]+" "+result[2]
        cmds.setAttr(attrName, metadata, type="string")
        
    def changeAttrValue(self, nodeName, attrValueText, index):
        # Get the attribute name, type and value
        attrName = nodeName+'['+str(index)+']'
        metadata = cmds.getAttr(attrName)
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))

        # Get the new value
        value = cmds.textField(attrValueText, query=True, text=True)
        
        # Update the value in all the templates
        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.textField(templateName+"|mtoa_exrMetadataRow_"+str(index)+"|MtoA_exrMAttributeValue", edit=True, text=value)
        
        # Update the metadata value
        metadata = result[0]+" "+result[1]+" "+value
        cmds.setAttr(attrName, metadata, type="string")
        
    def removeAttribute(self, nodeName, index):
        nodeName = self.selectedAttrName(nodeName)
        cmds.removeMultiInstance(nodeName+'['+str(index)+']')
        self.updatedMetadata(nodeName)
        
    def addAttribute(self, nodeName):
        
        nodeName = self.selectedAttrName(nodeName)
        next = 0
        if cmds.getAttr(nodeName, multiIndices=True):
            next = cmds.getAttr(nodeName, multiIndices=True)[-1] + 1
        cmds.setAttr(nodeName+'['+str(next)+']', "INT", type="string")
        self.updatedMetadata(nodeName)
        
    def updateLine(self, nodeName, metadata, index):
        # Attribute controls will be created with the current metadata content
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))
        
        nodeName = self.selectedAttrName(nodeName)
        # Attribute Name
        attrNameText = cmds.textField("MtoA_exrMAttributeName", text=result[1])
        cmds.textField(attrNameText, edit=True, changeCommand=pm.Callback(self.changeAttrName, nodeName, attrNameText, index))
        
        # Attribute Type
        menu = cmds.optionMenu("MtoA_exrMAttributeType")
        cmds.menuItem( label='INT', data=0)
        cmds.menuItem( label='FLOAT', data=1)
        cmds.menuItem( label='VECTOR2', data=2)
        cmds.menuItem( label='MATRIX', data=3)
        cmds.menuItem( label='STRING', data=4)
        if result[0] == 'INT':
            cmds.optionMenu(menu, edit=True, select=1)
        elif result[0] == 'FLOAT':
            cmds.optionMenu(menu, edit=True, select=2)
        elif result[0] == 'VECTOR2':
            cmds.optionMenu(menu, edit=True, select=3)
        elif result[0] == 'MATRIX':
            cmds.optionMenu(menu, edit=True, select=4)
        elif result[0] == 'STRING':
            cmds.optionMenu(menu, edit=True, select=5)
        cmds.optionMenu(menu, edit=True, changeCommand=pm.Callback(self.changeAttrType, nodeName, menu, index))
        
        # Attribute Value
        attrValueText = cmds.textField("MtoA_exrMAttributeValue", text=result[2])
        cmds.textField(attrValueText, edit=True, changeCommand=pm.Callback(self.changeAttrValue, nodeName, attrValueText, index))
        
        # Remove button
        cmds.symbolButton(image="SP_TrashIcon.png", command=pm.Callback(self.removeAttribute, nodeName, index))
        
    def updatedMetadata(self, nodeName):

        nodeName = self.selectedAttrName(nodeName)

        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.setParent(templateName)
            #Remove all attributes controls and rebuild them again with the metadata updated content
            for child in cmds.columnLayout(templateName, query=True, childArray=True) or []:
                cmds.deleteUI(child)
            for index in cmds.getAttr(nodeName, multiIndices=True) or []:
                attrName = nodeName+'['+str(index)+']'
                metadata = cmds.getAttr(attrName)
                if metadata:
                    cmds.rowLayout('mtoa_exrMetadataRow_'+str(index),nc=4, cw4=(120,80,120,20), cl4=('center', 'center', 'center', 'right'))
                    self.updateLine(nodeName, metadata, index)
                    cmds.setParent('..')
        
    def metadataNew(self, nodeName):
        cmds.rowLayout(nc=2, cw2=(200,140), cl2=('center', 'center'))

        nodeName = self.selectedAttrName(nodeName)
        cmds.button( label='Add New Attribute', command=pm.Callback(self.addAttribute, nodeName))
        cmds.setParent( '..' )
        layout = cmds.columnLayout(rowSpacing=5, columnWidth=340)
        # This template could be created more than once in different panels
        templatesNames.append(layout)
        self.updatedMetadata(nodeName)
        cmds.setParent( '..' )

    def metadataReplace(self, nodeName):
        pass

    def setup(self):
        self.addControl('exrCompression', label='压缩')
        self.addControl('halfPrecision', label='一半精度')
        self.addControl('preserveLayerName', label='保留图层名称')
        self.addControl('tiled', label='平铺')
        self.addControl('autocrop', label='自动裁剪')
        self.addControl('append', label='附加')
        self.beginLayout("元数据 (名称，类型，值)", collapse=True)
        self.addCustom('custom_attributes', self.metadataNew, self.metadataReplace)
        self.endLayout()

templates.registerTranslatorUI(EXRDriverTranslatorUI, 'aiAOVDriver', 'exr')


deepexrToleranceTemplates = []
deepexrHalfPrecisionTemplates = []
deepexrEnableFilteringTemplates = []

class DeepEXRDriverTranslatorUI(templates.AttributeTemplate):
    def __init__(self, nodeType):
        
        aovs.addAOVChangedCallback(self.updateLayerTolerance, 'DeepEXRDriverTranslatorUITolerance')
        aovs.addAOVChangedCallback(self.updateLayerHalfPrecision, 'DeepEXRDriverTranslatorUIHalfPrecision')
        aovs.addAOVChangedCallback(self.updateLayerEnableFiltering, 'DeepEXRDriverTranslatorUIEnableFiltering')
        super(DeepEXRDriverTranslatorUI, self).__init__(nodeType)

    def selectedAttrName(self, nodeName):
        # unfortunately the functions below might be called 
        # with NodeNames corresponding to a previously selected node
        # (maya doesn't rebuild the UI but just updates the param values so the callbacks remain)
        tokens = nodeName.split('.')

        # same object, nothing to change
        if (tokens[0] == self.nodeName):
            return nodeName

        #replace the first token by the actual self name
        result = self.nodeName
        tokens.pop(0)

        for token in tokens:
            result += "." + token

        return result

    def updateLayerTolerance(self):

        aovList = aovs.getAOVs(enabled=True)

        deepexrToleranceTemplates[:] = [tup for tup in deepexrToleranceTemplates if cmds.columnLayout(tup, exists=True)]
        for templateName in deepexrToleranceTemplates:
            
            driverName = self.nodeName

            # note that this function may be called to fill the defaultArnoldDriver exposed params
            # but with self != defaultArnoldDriver
            # this is because we might want to add this aov name in the default layers* list
            
            # in the render settings window I only want to display the defaultArnoldDriver params
            if templateName[:26] == "unifiedRenderGlobalsWindow":
                driverName = "defaultArnoldDriver"

            cmds.setParent(templateName)
            for child in cmds.columnLayout(templateName, query=True, childArray=True) or []:
                cmds.deleteUI(child)
                
            cmds.attrFieldSliderGrp(label='alpha' , at=driverName + '.alphaTolerance' )
            cmds.attrFieldSliderGrp(label='depth' , at=driverName + '.depthTolerance' )

            if driverName == "defaultArnoldDriver":
                cmds.attrFieldSliderGrp(label='beauty' , at='defaultArnoldDriver.layerTolerance[0]')
                for i in range(0,len(aovList)):
                    if aovList[i].node.attr('outputs')[0].driver.inputs()[0].name() == 'defaultArnoldDriver':
                        labelStr = aovList[i].name
                        attrStr = 'defaultArnoldDriver.layerTolerance['+str(i+1)+']'
                        cmds.attrFieldSliderGrp(label=labelStr , at=attrStr )
            else:
                cmds.attrFieldSliderGrp(label='layer' , at=driverName + '.layerTolerance[0]' )
            
    def updateLayerHalfPrecision(self):
        aovList = aovs.getAOVs(enabled=True)
        
        deepexrHalfPrecisionTemplates[:] = [tup for tup in deepexrHalfPrecisionTemplates if cmds.columnLayout(tup, exists=True)]
        for templateName in deepexrHalfPrecisionTemplates:
            cmds.setParent(templateName)

            driverName = self.nodeName

            # note that this function may be called to fill the defaultArnoldDriver exposed params
            # but with self != defaultArnoldDriver
            # this is because we might want to add this aov name in the default layers* list
            
            # in the render settings window I only want to display the defaultArnoldDriver params
            if templateName[:26] == "unifiedRenderGlobalsWindow":
                driverName = "defaultArnoldDriver"

            for child in cmds.columnLayout(templateName, query=True, childArray=True) or []:
                cmds.deleteUI(child)

            cmds.attrControlGrp(label='alpha' , a=driverName+'.alphaHalfPrecision' )
            cmds.attrControlGrp(label='depth' , a=driverName+'.depthHalfPrecision' )

            if driverName == "defaultArnoldDriver":
                cmds.attrControlGrp(label='beauty' , a='defaultArnoldDriver.layerHalfPrecision[0]' )
           
                for i in range(0,len(aovList)):
                    if aovList[i].node.attr('outputs')[0].driver.inputs()[0].name() == 'defaultArnoldDriver':
                        labelStr = aovList[i].name
                        attrStr = 'defaultArnoldDriver.layerHalfPrecision['+str(i+1)+']'
                        cmds.attrControlGrp(label=labelStr , a=attrStr )
            else:
                cmds.attrControlGrp(label='layer' , a=driverName+'.layerHalfPrecision[0]' )


    def updateLayerEnableFiltering(self):
        aovList = aovs.getAOVs(enabled=True)
        
        deepexrEnableFilteringTemplates[:] = [tup for tup in deepexrEnableFilteringTemplates if cmds.columnLayout(tup, exists=True)]
        for templateName in deepexrEnableFilteringTemplates:
            cmds.setParent(templateName)

            # note that this function may be called to fill the defaultArnoldDriver exposed params
            # but with self != defaultArnoldDriver
            # this is because we might want to add this aov name in the default layers* list
            
            # in the render settings window I only want to display the defaultArnoldDriver params
            driverName = self.nodeName
            if templateName[:26] == "unifiedRenderGlobalsWindow":
                driverName = "defaultArnoldDriver"

            for child in cmds.columnLayout(templateName, query=True, childArray=True) or []:
                cmds.deleteUI(child)

            if driverName == "defaultArnoldDriver":
                cmds.attrControlGrp(label='beauty' , a='defaultArnoldDriver.layerEnableFiltering[0]' )
                for i in range(0,len(aovList)):
                    if aovList[i].node.attr('outputs')[0].driver.inputs()[0].name() == 'defaultArnoldDriver':
                        labelStr = aovList[i].name
                        attrStr = 'defaultArnoldDriver.layerEnableFiltering['+str(i+1)+']'
                        cmds.attrControlGrp(label=labelStr , a=attrStr )
            else:
                cmds.attrControlGrp(label='layer' , a=driverName +'.layerEnableFiltering[0]' )
     
    def layerToleranceNew(self, nodeName):
        layout = cmds.columnLayout(rowSpacing=5, columnWidth=340)
        deepexrToleranceTemplates.append(layout)
        
        self.updateLayerTolerance()
        cmds.setParent( '..' )
        
    def layerToleranceReplace(self, nodeName):

        self.updateLayerTolerance()
        cmds.setParent( '..' )
        
    def layerHalfPrecisionNew(self, nodeName):
        layout = cmds.columnLayout(rowSpacing=5, columnWidth=340)
        deepexrHalfPrecisionTemplates.append(layout)
        self.updateLayerHalfPrecision()
        cmds.setParent( '..' )
        
    def layerHalfPrecisionReplace(self, nodeName):
        self.updateLayerHalfPrecision()
        cmds.setParent( '..' )
        
    def layerEnableFilteringNew(self, nodeName):
        layout = cmds.columnLayout(rowSpacing=5, columnWidth=340)
        deepexrEnableFilteringTemplates.append(layout)
        self.updateLayerEnableFiltering()
        cmds.setParent( '..' )
        
    def layerEnableFilteringReplace(self, nodeName):
        self.updateLayerEnableFiltering()
        cmds.setParent( '..' )

    def changeAttrName(self, nodeName, attrNameText, index):
        # Get the attribute name, type and value
        attrName = nodeName+'['+str(index)+']'
        metadata = cmds.getAttr(attrName)
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))
        
        # Get the new name
        name = cmds.textField(attrNameText, query=True, text=True)
        
        # Update the name in all the templates
        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.textField(templateName+"|mtoa_exrMetadataRow_"+str(index)+"|MtoA_exrMAttributeName", edit=True, text=name.replace(" ", ""))
        
        # Update the metadata value
        metadata = result[0]+" "+name.replace(" ", "")+" "+result[2]
        cmds.setAttr(attrName, metadata, type="string")
    
    def changeAttrType(self, nodeName, menu, index):
        # Get the attribute name, type and value
        attrName = nodeName+'['+str(index)+']'
        metadata = cmds.getAttr(attrName)
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))
        
        # Get the new type
        typeNumber = cmds.optionMenu(menu, query=True, select=True)
        type = cmds.optionMenu(menu, query=True, value=True)
        
        # Update the type in all the templates
        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.optionMenu(templateName+"|mtoa_exrMetadataRow_"+str(index)+"|MtoA_exrMAttributeType", edit=True, select=typeNumber)
            
        # Update the metadata value
        metadata = type+" "+result[1]+" "+result[2]
        cmds.setAttr(attrName, metadata, type="string")
        
    def changeAttrValue(self, nodeName, attrValueText, index):
        # Get the attribute name, type and value
        attrName = nodeName+'['+str(index)+']'
        metadata = cmds.getAttr(attrName)
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))

        # Get the new value
        value = cmds.textField(attrValueText, query=True, text=True)
        
        # Update the value in all the templates
        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.textField(templateName+"|mtoa_exrMetadataRow_"+str(index)+"|MtoA_exrMAttributeValue", edit=True, text=value)
        
        # Update the metadata value
        metadata = result[0]+" "+result[1]+" "+value
        cmds.setAttr(attrName, metadata, type="string")
        
    def removeAttribute(self, nodeName, index):
        nodeName = self.selectedAttrName(nodeName)
        cmds.removeMultiInstance(nodeName+'['+str(index)+']')
        self.updatedMetadata(nodeName)
        
    def addAttribute(self, nodeName):

        nodeName = self.selectedAttrName(nodeName)
        next = 0
        if cmds.getAttr(nodeName, multiIndices=True):
            next = cmds.getAttr(nodeName, multiIndices=True)[-1] + 1
        cmds.setAttr(nodeName+'['+str(next)+']', "INT", type="string")
        self.updatedMetadata(nodeName)
        
    def updateLine(self, nodeName, metadata, index):
        # Attribute controls will be created with the current metadata content
        result = metadata.split(' ', 2 )
        result += [""] * (3-len(result))
        
        nodeName = self.selectedAttrName(nodeName)

        # Attribute Name
        attrNameText = cmds.textField("MtoA_exrMAttributeName", text=result[1])
        cmds.textField(attrNameText, edit=True, changeCommand=pm.Callback(self.changeAttrName, nodeName, attrNameText, index))
        
        # Attribute Type
        menu = cmds.optionMenu("MtoA_exrMAttributeType")
        cmds.menuItem( label='INT', data=0)
        cmds.menuItem( label='FLOAT', data=1)
        cmds.menuItem( label='VECTOR2', data=2)
        cmds.menuItem( label='MATRIX', data=3)
        cmds.menuItem( label='STRING', data=4)
        if result[0] == 'INT':
            cmds.optionMenu(menu, edit=True, select=1)
        elif result[0] == 'FLOAT':
            cmds.optionMenu(menu, edit=True, select=2)
        elif result[0] == 'VECTOR2':
            cmds.optionMenu(menu, edit=True, select=3)
        elif result[0] == 'MATRIX':
            cmds.optionMenu(menu, edit=True, select=4)
        elif result[0] == 'STRING':
            cmds.optionMenu(menu, edit=True, select=5)
        cmds.optionMenu(menu, edit=True, changeCommand=pm.Callback(self.changeAttrType, nodeName, menu, index))
        
        # Attribute Value
        attrValueText = cmds.textField("MtoA_exrMAttributeValue", text=result[2])
        cmds.textField(attrValueText, edit=True, changeCommand=pm.Callback(self.changeAttrValue, nodeName, attrValueText, index))
        
        # Remove button
        cmds.symbolButton(image="SP_TrashIcon.png", command=pm.Callback(self.removeAttribute, nodeName, index))
        
    def updatedMetadata(self, nodeName):
        
        nodeName = self.selectedAttrName(nodeName)

        templatesNames[:] = [tup for tup in templatesNames if cmds.columnLayout(tup, exists=True)]
        for templateName in templatesNames:
            cmds.setParent(templateName)
            #Remove all attributes controls and rebuild them again with the metadata updated content
            for child in cmds.columnLayout(templateName, query=True, childArray=True) or []:
                cmds.deleteUI(child)
            for index in cmds.getAttr(nodeName, multiIndices=True) or []:
                attrName = nodeName+'['+str(index)+']'
                metadata = cmds.getAttr(attrName)
                if metadata:
                    cmds.rowLayout('mtoa_exrMetadataRow_'+str(index),nc=4, cw4=(120,80,120,20), cl4=('center', 'center', 'center', 'right'))
                    self.updateLine(nodeName, metadata, index)
                    cmds.setParent('..')
        
    def metadataNew(self, nodeName):

        nodeName = self.selectedAttrName(nodeName)

        cmds.rowLayout(nc=2, cw2=(200,140), cl2=('center', 'center'))
        cmds.button( label='Add New Attribute', command=pm.Callback(self.addAttribute, nodeName))
        cmds.setParent( '..' )
        layout = cmds.columnLayout(rowSpacing=5, columnWidth=340)
        # This template could be created more than once in different panels
        templatesNames.append(layout)
        self.updatedMetadata(nodeName)
        cmds.setParent( '..' )

    def metadataReplace(self, nodeName):
        pass
        
    def setup(self):
        self.addControl('tiled', label='Tiled')
        self.addControl('append', label='Append')
        self.addControl('subpixelMerge', label='Subpixel Merge')
        self.addControl('useRGBOpacity', label='Use RGB Opacity')
        self.beginLayout("Tolerance Values", collapse=False)
        self.addCustom('layerToleranceSection', self.layerToleranceNew, self.layerToleranceReplace)
        self.endLayout()
        
        self.beginLayout("Half Precision", collapse=False)
        self.addCustom('layerHalfPrecisionSection', self.layerHalfPrecisionNew, self.layerHalfPrecisionReplace)
        self.endLayout()
        
        self.beginLayout("Enable Filtering", collapse=False)
        self.addCustom('layerEnableFilteringSection', self.layerEnableFilteringNew, self.layerEnableFilteringReplace)
        self.endLayout()
        
        self.beginLayout("Metadata (name, type, value)", collapse=True)
        self.addCustom('custom_attributes', self.metadataNew, self.metadataReplace)
        self.endLayout()

templates.registerTranslatorUI(DeepEXRDriverTranslatorUI, 'aiAOVDriver', 'deepexr')

def registerFilterTemplates():
    # register driver templates
    for transName, arnoldNode in core.listTranslators("aiAOVFilter"):
        templates.registerAutoTranslatorUI(arnoldNode, "aiAOVFilter", transName, skipEmpty=True)

    templates.registerDefaultTranslator('aiAOVFilter', 'gaussian')

registerDriverTemplates()
registerFilterTemplates()
