# Copyright (c) 2017-2026 Soft8Soft, LLC. All rights reserved.
#
# Use of this software is subject to the terms of the Verge3D license
# agreement provided at the time of installation or download, or which
# otherwise accompanies this software in either electronic or hard copy form.
import math

import pluginUtils
log = pluginUtils.log.getLogger('V3D-MY')

import maya.cmds as cmds

AA_METHODS_MAP = [
    'AUTO',
    'MSAA4',
    'MSAA8',
    'MSAA16',
    'FXAA',
    'NONE'
]

ANIM_LOOP_MAP = [
    'REPEAT',
    'PING_PONG',
    'ONCE'
]

CANVAS_FIT_X_MAP = [
    'NONE',
    'LEFT',
    'RIGHT',
    'STRETCH'
]

CANVAS_FIT_Y_MAP = [
    'NONE',
    'TOP',
    'BOTTOM',
    'STRETCH'
]

CANVAS_FIT_SHAPE_MAP = [
    'BOX',
    'SPHERE',
    'POINT',
]

CAMERA_CONTROLS_MAP = [
    'ORBIT',
    'FLYING',
    'FIRST_PERSON',
    'NONE'
]

CANVAS_BREAK_ORIENT_MAP = [
    'ALL',
    'LANDSCAPE',
    'PORTRAIT'
]

ALPHA_MODE_MAP = [
    'AUTO',
    'OPAQUE',
    'BLEND',
    'MASK',
    'COVERAGE',
    'ADD'
]

TRANSPARENCY_HACK_MAP = [
    'NONE',
    'NEAREST_LAYER',
    'TWO_PASS'
]

IBL_ENV_MAP = [
    'PMREM',
    'PROBE',
    'NONE'
]

SHADOW_FILTERING_MAP = [
    'BASIC',
    'BILINEAR',
    'PCFPOISSON',
    'ESM'
]

SHADOW_FILTERING_DESC = [
    'Basic',
    'Bilinear',
    'PCF',
    'ESM'
]

ANISOTROPY_MODES = [
    '1x',
    '2x',
    '4x',
    '8x',
    '16x'
]

COMPRESSION_METHODS = [
    'AUTO',
    'UASTC',
    'ETC1S',
    'DISABLE'
]

SUPPORTED_MATERIALS = [
    'lambert',
    'blinn',
    'openPBRSurface',
    'phong',
    'phongE',
    'standardSurface',
    'surfaceShader',
    'usdPreviewSurface',
    'aiFlat',
    'aiLambert',
    'aiMixShader',
    'aiShadowMatte',
    'aiStandardSurface',
    'aiTwoSided',
    'aiUvProjection'
]

MATERIALS_WITH_GLTF_COMPAT = [
    'standardSurface',
    'aiStandardSurface',
    'lambert'
]

LINE_RENDERING_OBJECTS = [
    'nurbsCurve',
    'bezierCurve',
    'nurbsSurface',
]

def initSettingsNode(nodeType):

    cmds.addExtension(nodeType=nodeType, longName='copyright', dataType='string',
                      niceName='Scene Copyright')
    cmds.addExtension(nodeType=nodeType, longName='bakeText', attributeType='bool',
                      niceName='Bake Text')
    cmds.addExtension(nodeType=nodeType, longName='lzmaEnabled', attributeType='bool',
                      niceName='Enable LZMA Compression')
    cmds.addExtension(nodeType=nodeType, longName='compressTextures', attributeType='bool',
                      niceName='Compress Textures')
    cmds.addExtension(nodeType=nodeType, longName='aaMethod', attributeType='enum',
                      enumName='Auto:MSAA 4x:MSAA 8x:MSAA 16x:FXAA:None', niceName='Anti-Aliasing')
    cmds.addExtension(nodeType=nodeType, longName='useHDR', attributeType='bool',
                      niceName='Use HDR Rendering')
    cmds.addExtension(nodeType=nodeType, longName='useOIT', attributeType='bool',
                      niceName='Order-Independent Transparency')
    cmds.addExtension(nodeType=nodeType, longName='optimizeAttrs', attributeType='bool', defaultValue=True,
                      niceName='Optimize Mesh Attributes')
    cmds.addExtension(nodeType=nodeType, longName='iblEnvironment', attributeType='enum',
                      enumName='PMREM (best):Light Probe (fast):None (fastest)', niceName='IBL Environment Mode')
    cmds.addExtension(nodeType=nodeType, longName='shadowFiltering', attributeType='enum',
                      enumName=':'.join(SHADOW_FILTERING_DESC),
                      defaultValue=SHADOW_FILTERING_DESC.index('PCF'),
                      niceName='Shadow Filtering')
    cmds.addExtension(nodeType=nodeType, longName='esmDistanceScale',
                      attributeType='float', defaultValue=2.5, minValue=0,
                      softMaxValue=10, maxValue=100, niceName='ESM Distance Scale')

    cmds.addExtension(nodeType=nodeType, longName='outlineEnabled', attributeType='bool',
                      niceName='Enabled')
    cmds.addExtension(nodeType=nodeType, longName='edgeStrength', attributeType='float',
                      defaultValue=3, minValue=0, maxValue=10)
    cmds.addExtension(nodeType=nodeType, longName='edgeGlow', attributeType='float',
                      defaultValue=0, minValue=0, maxValue=10)
    cmds.addExtension(nodeType=nodeType, longName='edgeThickness', attributeType='float',
                      defaultValue=1, minValue=0, maxValue=10)
    cmds.addExtension(nodeType=nodeType, longName='pulsePeriod', attributeType='float', defaultValue=0)
    cmds.addExtension(nodeType=nodeType, longName='visibleEdgeColor', attributeType='float3', usedAsColor=True)
    cmds.addExtension(nodeType=nodeType, longName='visibleEdgeColorR', attributeType='float', parent='visibleEdgeColor')
    cmds.addExtension(nodeType=nodeType, longName='visibleEdgeColorG', attributeType='float', parent='visibleEdgeColor')
    cmds.addExtension(nodeType=nodeType, longName='visibleEdgeColorB', attributeType='float', parent='visibleEdgeColor')
    cmds.addExtension(nodeType=nodeType, longName='hiddenEdgeColor', attributeType='float3', usedAsColor=True)
    cmds.addExtension(nodeType=nodeType, longName='hiddenEdgeColorR', attributeType='float', parent='hiddenEdgeColor')
    cmds.addExtension(nodeType=nodeType, longName='hiddenEdgeColorG', attributeType='float', parent='hiddenEdgeColor')
    cmds.addExtension(nodeType=nodeType, longName='hiddenEdgeColorB', attributeType='float', parent='hiddenEdgeColor')
    cmds.addExtension(nodeType=nodeType, longName='renderHiddenEdge',
            attributeType='bool', defaultValue=True, niceName='Render Hidden Edge')

    cmds.addExtension(nodeType=nodeType, longName='animExport', attributeType='bool', defaultValue=True,
                      niceName='Export Animations')
    cmds.addExtension(nodeType=nodeType, longName='animUsePlaybackRange', attributeType='bool',
                      niceName='Export Within Playback Range')
    cmds.addExtension(nodeType=nodeType, longName='animStartWithZero', attributeType='bool', defaultValue=True,
                      niceName='Keyframes Start with 0')

    cmds.addExtension(nodeType=nodeType, longName='aoEnabled', attributeType='bool',
                      niceName='Enabled')
    cmds.addExtension(nodeType=nodeType, longName='aoDistance', attributeType='doubleLinear',
                      niceName='Distance', defaultValue=10, minValue=0, softMaxValue=1000)
    cmds.addExtension(nodeType=nodeType, longName='aoFactor', attributeType='float',
                      niceName='Factor', defaultValue=1, minValue=0, softMaxValue=1)
    cmds.addExtension(nodeType=nodeType, longName='aoTracePrecision', attributeType='float',
                      niceName='Trace Precision', defaultValue=0.25, minValue=0, maxValue=1)
    cmds.addExtension(nodeType=nodeType, longName='aoBentNormals', attributeType='bool',
                      defaultValue=False, niceName='Bent Normals')

    cmds.addExtension(nodeType=nodeType, longName='shadowFilteringType', attributeType='enum',
                      enumName='a:b:c:d:e:f', defaultValue=4)
    cmds.addExtension(nodeType=nodeType, longName='iblEnvironmentMode', attributeType='enum', enumName='a:b:c:d')

def createSettingsNode():
    """
    instantiate v3dSettings node
    this method will indirectly add "requires" to the Maya scene
    """
    if len(cmds.ls(type='v3dSettings')) == 0:
        cmds.createNode('v3dSettings', shared=True, name='v3dSettings', skipSelect=True)

    iblEnvOld = cmds.getAttr('v3dSettings.iblEnvironmentMode')
    iblEnvNew = cmds.getAttr('v3dSettings.iblEnvironment')

    if iblEnvOld != 0 and iblEnvNew == 0:
        log.info('Refactoring global IBL environment mode setting')

        if iblEnvOld == 1:
            iblEnvNew = 0
        else:
            iblEnvNew = iblEnvOld - 1

        cmds.setAttr('v3dSettings.iblEnvironmentMode', 0) # to default
        cmds.setAttr('v3dSettings.iblEnvironment', iblEnvNew)

    shadowFilteringOld = cmds.getAttr('v3dSettings.shadowFilteringType')
    shadowFilteringNew = cmds.getAttr('v3dSettings.shadowFiltering')

    if shadowFilteringOld != 4 and shadowFilteringNew == 2:
        log.info('Refactoring global shadow filtering type setting')

        if shadowFilteringOld < 2:
            shadowFilteringNew = shadowFilteringOld
        elif shadowFilteringOld >= 2 and shadowFilteringOld < 4:
            shadowFilteringNew = 2
        else:
            shadowFilteringNew = shadowFilteringOld - 2

        cmds.setAttr('v3dSettings.shadowFilteringType', 4) # to default
        cmds.setAttr('v3dSettings.shadowFiltering', shadowFilteringNew)

def removeSettingsNode():
    """
    remove v3dSettings node if any
    """

    for node in cmds.ls(type='v3dSettings'):
        cmds.delete(node)

def parseSettings(output={}):

    defaultSettings = {
        'copyright': '',
        'bakeText': False,
        'lzmaEnabled': False,
        'compressTextures': False,
        'aaMethod': 'AUTO',
        'useHDR': False,
        'useOIT': False,
        'optimizeAttrs': True,
        'iblEnvironment': 'PMREM',
        'shadowFiltering': 'PCFPOISSON',
        'esmDistanceScale': 2.5,
        'outlineEnabled': False,
        'edgeStrength': 3,
        'edgeGlow': 0,
        'edgeThickness': 1,
        'pulsePeriod': 0,
        'visibleEdgeColor': [1, 1, 1],
        'hiddenEdgeColor': [0.1, 0.1, 0.1],
        'renderHiddenEdge': True,
        'animExport': True,
        'animUsePlaybackRange': False,
        'animStartWithZero': True,
        'aoEnabled': False,
        'aoDistance': 10,
        'aoFactor': 1,
        'aoTracePrecision': 0.25,
        'aoBentNormals': False
    }

    if len(cmds.ls(type='v3dSettings')) == 0:
        log.info('Using default global settings')
        output.update(defaultSettings)
        return

    for name in defaultSettings:
        value = cmds.getAttr('v3dSettings.' + name)

        if name == 'aaMethod':
            value = AA_METHODS_MAP[value]

        elif name == 'iblEnvironment':
            value = IBL_ENV_MAP[value]

        elif name == 'shadowFiltering':
            value = SHADOW_FILTERING_MAP[value]

        elif cmds.attributeQuery(name, node='v3dSettings', usedAsColor=True):
            value = list(value[0])

        output[name] = value

def assignNodeSettings():
    assignTransformSettings()
    assignCameraSettings()
    assignMeshSettings()
    assignLightSettings('directionalLight')
    assignLightSettings('pointLight')
    assignLightSettings('spotLight')
    assignLightSettings('areaLight')
    assignLightSettings('aiAreaLight')

    for objType in LINE_RENDERING_OBJECTS:
        assignLineSettings(objType)

    for matType in SUPPORTED_MATERIALS:
        assignMaterialSettings(matType)

    assignTextureSettings()

def assignTransformSettings():
    if cmds.attributeQuery('v3d', type='transform', exists=True):
        return

    cmds.addExtension(nodeType='transform', longName='v3d', attributeType='compound',
                      numberOfChildren=21, niceName='Verge3D')

    cmds.addExtension(nodeType='transform', longName='animAuto', attributeType='bool',
                      defaultValue=True, parent='v3d', niceName='Auto Start')

    cmds.addExtension(nodeType='transform', longName='animLoop', attributeType='enum',
                      enumName='Repeat:Ping Pong:Once', parent='v3d', niceName='Loop Mode')

    cmds.addExtension(nodeType='transform', longName='animRepeatInfinite', attributeType='bool',
                      defaultValue=True, parent='v3d', niceName='Repeat Infinitely')
    cmds.addExtension(nodeType='transform', longName='animRepeatCount', attributeType='float',
                      defaultValue=1, minValue=0, softMaxValue=1000, parent='v3d', niceName='Repeat Count')
    cmds.addExtension(nodeType='transform', longName='animOffset', attributeType='float',
                      defaultValue=0, softMinValue=-1000, softMaxValue=1000, parent='v3d', niceName='Offset')

    cmds.addExtension(nodeType='transform', longName='animUseCustomRange', attributeType='bool',
                      defaultValue=False, parent='v3d', niceName='Custom Frame Range')
    cmds.addExtension(nodeType='transform', longName='animCustomRangeFrom', attributeType='float',
                      defaultValue=0, softMinValue=-1000, softMaxValue=1000, parent='v3d', niceName='From')
    cmds.addExtension(nodeType='transform', longName='animCustomRangeTo', attributeType='float',
                      defaultValue=100, softMinValue=-1000, softMaxValue=1000, parent='v3d', niceName='To')

    cmds.addExtension(nodeType='transform', longName='animSkeletonRoot', attributeType='bool',
                      defaultValue=False, parent='v3d', niceName='Skeleton Root')

    cmds.addExtension(nodeType='transform', longName='hidpiCompositing', attributeType='bool',
                      defaultValue=False, parent='v3d', niceName='HiDPI Compositing')
    cmds.addExtension(nodeType='transform', longName='fixOrthoZoom', attributeType='bool',
                      defaultValue=False, parent='v3d', niceName='Fix Ortho Zoom')

    cmds.addExtension(nodeType='transform', longName='canvasFitX', attributeType='enum',
                      enumName='None:Left:Right:Stretch', parent='v3d', niceName='Horizontal')
    cmds.addExtension(nodeType='transform', longName='canvasFitY', attributeType='enum',
                      enumName='None:Top:Bottom:Stretch', parent='v3d', niceName='Vertical')
    cmds.addExtension(nodeType='transform', longName='canvasFitShape', attributeType='enum',
                      enumName='Box:Sphere:Point', parent='v3d', niceName='Shape')
    cmds.addExtension(nodeType='transform', longName='canvasFitOffset', attributeType='float',
                      defaultValue=0, softMinValue=-10, softMaxValue=10, parent='v3d', niceName='Fit Offset')

    cmds.addExtension(nodeType='transform', longName='canvasBreakEnabled', attributeType='bool',
                      defaultValue=False, parent='v3d', niceName='Visibility Breakpoints')
    cmds.addExtension(nodeType='transform', longName='canvasBreakMinWidth', attributeType='long',
                      defaultValue=0, minValue=0, softMaxValue=1000, parent='v3d', niceName='Min Width')
    cmds.addExtension(nodeType='transform', longName='canvasBreakMaxWidth', attributeType='long',
                      defaultValue=10000, minValue=0, softMaxValue=10000, parent='v3d', niceName='Max Width')
    cmds.addExtension(nodeType='transform', longName='canvasBreakMinHeight', attributeType='long',
                      defaultValue=0, minValue=0, softMaxValue=1000, parent='v3d', niceName='Min Height')
    cmds.addExtension(nodeType='transform', longName='canvasBreakMaxHeight', attributeType='long',
                      defaultValue=10000, minValue=0, softMaxValue=10000, parent='v3d', niceName='Max Height')
    cmds.addExtension(nodeType='transform', longName='canvasBreakOrientation', attributeType='enum',
                      enumName='All:Landscape:Portrait', parent='v3d', niceName='Orientation')

def parseAnimationSettings(mNode):

    output = {}

    defaultSettings = {
        'animAuto': True,
        'animLoop': None,
        'animRepeatInfinite': True,
        'animRepeatCount': None,
        'animOffset': None,
        'animUseCustomRange': False,
        'animCustomRangeFrom': None,
        'animCustomRangeTo': None,
        'animSkeletonRoot': False
    }

    for name in defaultSettings:
        value = cmds.getAttr(mNode + '.v3d.' + name)

        if name == 'animLoop':
            value = ANIM_LOOP_MAP[value]

        output[name] = value

    return output

def parseAdvRenderSettings(mNode):

    output = {}

    defaultSettings = {
        'hidpiCompositing': False,
        'fixOrthoZoom': False,

        'canvasFitX': 'NONE',
        'canvasFitY': 'NONE',
        'canvasFitShape': 'BOX',
        'canvasFitOffset': 0,

        'canvasBreakEnabled': False,
        'canvasBreakMinWidth': 0,
        'canvasBreakMaxWidth': 10000,
        'canvasBreakMinHeight': 0,
        'canvasBreakMaxHeight': 10000,
        'canvasBreakOrientation': 'ALL'
    }

    for name in defaultSettings:
        value = cmds.getAttr(mNode + '.v3d.' + name)

        if name == 'canvasFitX':
            value = CANVAS_FIT_X_MAP[value]
        elif name == 'canvasFitY':
            value = CANVAS_FIT_Y_MAP[value]
        elif name == 'canvasFitShape':
            value = CANVAS_FIT_SHAPE_MAP[value]
        elif name == 'canvasBreakOrientation':
            value = CANVAS_BREAK_ORIENT_MAP[value]

        output[name] = value

    return output

def assignCameraSettings():
    if cmds.attributeQuery('v3d', type='camera', exists=True):
        return

    PI2 = 2 * math.pi

    cmds.addExtension(nodeType='camera', longName='v3d', attributeType='compound',
                      numberOfChildren=17, niceName='Verge3D')

    cmds.addExtension(nodeType='camera', longName='controls', attributeType='enum',
                      enumName='Orbit:Flying:First-Person:Disabled', parent='v3d',
                      niceName='Controls')

    cmds.addExtension(nodeType='camera', longName='panningEnabled', attributeType='bool',
                      defaultValue=True, parent='v3d')

    cmds.addExtension(nodeType='camera', longName='rotateSpeed', attributeType='float',
                      defaultValue=1, minValue=0, softMaxValue=10, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='moveSpeed', attributeType='float',
                      defaultValue=1, minValue=0, softMaxValue=10, parent='v3d')

    cmds.addExtension(nodeType='camera', longName='minDist', attributeType='float',
                      defaultValue=0, minValue=0, softMaxValue=100, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='maxDist', attributeType='float',
                      defaultValue=1000, minValue=0, softMaxValue=1000, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='minZoom', attributeType='float',
                      defaultValue=0.01, minValue=0, softMaxValue=100, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='maxZoom', attributeType='float',
                      defaultValue=100, minValue=0, softMaxValue=100, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='minAngle', attributeType='doubleAngle',
                      defaultValue=-math.pi/2, softMinValue=-math.pi/2, softMaxValue=math.pi/2,
                      minValue=-math.pi, maxValue=math.pi, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='maxAngle', attributeType='doubleAngle',
                      defaultValue=math.pi/2, softMinValue=-math.pi/2, softMaxValue=math.pi/2,
                      minValue=-math.pi, maxValue=math.pi, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='minAzimuthAngle', attributeType='doubleAngle',
                      defaultValue=0, minValue=0, maxValue=PI2, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='maxAzimuthAngle', attributeType='doubleAngle',
                      defaultValue=PI2, minValue=0, maxValue=PI2, parent='v3d')

    cmds.addExtension(nodeType='camera', longName='renderBackground', attributeType='bool',
                      defaultValue=False, parent='v3d')

    cmds.addExtension(nodeType='camera', longName='fpsCollisionMaterial', attributeType='message',
                      parent='v3d')
    cmds.addExtension(nodeType='camera', longName='fpsGazeLevel', attributeType='float',
                      defaultValue=1.8, minValue=0, softMaxValue=10, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='fpsStoryHeight', attributeType='float',
                      defaultValue=5, minValue=0, softMaxValue=100, parent='v3d')
    cmds.addExtension(nodeType='camera', longName='enablePointerLock', attributeType='bool',
                      defaultValue=False, parent='v3d')

def parseCameraSettings(mNode):

    output = {}

    defaultSettings = {
        'panningEnabled': True,
        'rotateSpeed': None,
        'moveSpeed': None,
        'controls': None,
        'minDist': None,
        'maxDist': None,
        'minZoom': 0.01,
        'maxZoom': 100,
        'minAngle': None,
        'maxAngle': None,
        'minAzimuthAngle': None,
        'maxAzimuthAngle': None,
        'renderBackground': False,
        'fpsCollisionMaterial': None,
        'fpsGazeLevel': 1.8,
        'fpsStoryHeight': 5,
        'enablePointerLock': False
    }

    for name in defaultSettings:

        value = None

        if name == 'fpsCollisionMaterial':
            conn = cmds.listConnections(mNode + '.v3d.' + name)
            if conn:
                value = conn[0]
        else:
            value = cmds.getAttr(mNode + '.v3d.' + name)

            if name == 'controls':
                value = CAMERA_CONTROLS_MAP[value]
            elif name in ['minAngle', 'maxAngle', 'minAzimuthAngle', 'maxAzimuthAngle']:
                value = math.radians(value)

        output[name] = value

    return output

def assignMeshSettings():
    if cmds.attributeQuery('v3d', type='mesh', exists=True):
        return

    cmds.addExtension(nodeType='mesh', longName='v3d', attributeType='compound',
                      numberOfChildren=6, niceName='Verge3D')

    cmds.addExtension(nodeType='mesh', longName='renderOrder', attributeType='long',
                      defaultValue=0, minValue=-100, maxValue=100, parent='v3d')

    cmds.addExtension(nodeType='mesh', longName='frustumCulling', attributeType='bool',
                      defaultValue=True, parent='v3d')

    cmds.addExtension(nodeType='mesh', longName='enableLineRendering', attributeType='bool',
                      defaultValue=False, parent='v3d')
    cmds.addExtension(nodeType='mesh', longName='lineColor', attributeType='float3',
                      usedAsColor=True, parent='v3d')
    cmds.addExtension(nodeType='mesh', longName='lineColorR', attributeType='float', defaultValue=1, parent='lineColor')
    cmds.addExtension(nodeType='mesh', longName='lineColorG', attributeType='float', defaultValue=1, parent='lineColor')
    cmds.addExtension(nodeType='mesh', longName='lineColorB', attributeType='float', defaultValue=1, parent='lineColor')

    cmds.addExtension(nodeType='mesh', longName='lineWidth', attributeType='float',
                      defaultValue=1, minValue=0, softMaxValue=100, parent='v3d')
    cmds.addExtension(nodeType='mesh', longName='lineResolutionSteps', attributeType='long',
                      defaultValue=5, minValue=1, softMaxValue=100, parent='v3d')

def parseMeshSettings(mNode):

    output = {}

    defaultSettings = {
        'renderOrder': 0,
        'frustumCulling': True
    }

    for name in defaultSettings:
        value = cmds.getAttr(mNode + '.v3d.' + name)

        output[name] = value

    return output

def assignLightSettings(nodeType):

    if not (nodeType in cmds.listNodeTypes('light')):
        return

    if cmds.attributeQuery('v3d', type=nodeType, exists=True):
        return

    isDirLight = nodeType == 'directionalLight'

    cmds.addExtension(nodeType=nodeType, longName='v3d', attributeType='compound',
                      numberOfChildren=(5 if isDirLight else 1), niceName='Verge3D')

    cmds.addExtension(nodeType=nodeType, longName='esmExponent',
                      attributeType='float', defaultValue=4, minValue=1,
                      maxValue=10000, softMaxValue=100, parent='v3d',
                      niceName='ESM Bias')

    if isDirLight:
        cmds.addExtension(nodeType=nodeType, longName='csmCount', attributeType='long',
                          defaultValue=1, minValue=1, maxValue=4, parent='v3d',
                          niceName='Count')

        cmds.addExtension(nodeType=nodeType, longName='csmFade', attributeType='float',
                          defaultValue=0, minValue=0, maxValue=1, parent='v3d',
                          niceName='Fade')

        cmds.addExtension(nodeType=nodeType, longName='csmDistribution', attributeType='float',
                          defaultValue=0.5, minValue=0.0, maxValue=1.0, parent='v3d',
                          niceName='Distribution')

        cmds.addExtension(nodeType=nodeType, longName='csmLightMargin', attributeType='float',
                          defaultValue=1, minValue=0, maxValue=10000, softMaxValue=1000,
                          parent='v3d', niceName='Cascade Margin')

def parseLightSettings(mNode):

    output = {}

    defaultSettings = {
        'esmExponent': 4
    }

    for name in defaultSettings:
        value = cmds.getAttr(mNode + '.v3d.' + name)
        output[name] = value

    if cmds.attributeQuery('csmCount', node=mNode, exists=True):
        csmSettings = {
            'csmCount': 1,
            'csmFade': 0.0,
            'csmDistribution': 0.5,
            'csmLightMargin': 1
        }

        for name in csmSettings:
            value = cmds.getAttr(mNode + '.v3d.' + name)
            output[name] = value

    return output

def assignLineSettings(nodeType):
    if cmds.attributeQuery('v3d', type=nodeType, exists=True):
        return

    cmds.addExtension(nodeType=nodeType, longName='v3d', attributeType='compound',
                      numberOfChildren=4, niceName='Line Rendering')
    cmds.addExtension(nodeType=nodeType, longName='enableLineRendering', attributeType='bool',
                      defaultValue=False, parent='v3d', niceName='Enable Line Rendering')
    cmds.addExtension(nodeType=nodeType, longName='lineColor', attributeType='float3',
                      usedAsColor=True, parent='v3d')
    cmds.addExtension(nodeType=nodeType, longName='lineColorR', attributeType='float', defaultValue=1, parent='lineColor')
    cmds.addExtension(nodeType=nodeType, longName='lineColorG', attributeType='float', defaultValue=1, parent='lineColor')
    cmds.addExtension(nodeType=nodeType, longName='lineColorB', attributeType='float', defaultValue=1, parent='lineColor')
    cmds.addExtension(nodeType=nodeType, longName='lineWidthV3D', attributeType='float',
                      defaultValue=1, minValue=0, softMaxValue=100, parent='v3d', niceName='Line Width')
    cmds.addExtension(nodeType=nodeType, longName='lineResolutionSteps', attributeType='long',
                      defaultValue=5, minValue=1, softMaxValue=100, parent='v3d')

def parseLineSettings(mNode):

    output = {}

    defaultSettings = {
        'enableLineRendering': False,
        'lineColor': [1, 1, 1],
        'lineWidth': 0,
        'lineResolutionSteps': 5
    }

    for name in defaultSettings:
        if cmds.attributeQuery(name + 'V3D', node=mNode, exists=True):
            nameAttr = name + 'V3D'
        else:
            nameAttr = name

        value = cmds.getAttr(mNode + '.v3d.' + nameAttr)

        if cmds.attributeQuery(nameAttr, node=mNode, usedAsColor=True):
            value = list(value[0])

        output[name] = value

    return output

def assignMaterialSettings(nodeType):

    if not (nodeType in cmds.listNodeTypes('shader')):
        return

    if cmds.attributeQuery('v3d', type=nodeType, exists=True):
        return

    numSettings = 7 if (nodeType in MATERIALS_WITH_GLTF_COMPAT) else 6

    cmds.addExtension(nodeType=nodeType, longName='v3d', attributeType='compound',
                      numberOfChildren=numSettings, niceName='Verge3D')

    cmds.addExtension(nodeType=nodeType, longName='alphaMode', attributeType='enum',
                      enumName='Auto:Opaque:Blend:Mask:Coverage:Add', parent='v3d',
                      niceName='Alpha Mode')
    cmds.addExtension(nodeType=nodeType, longName='transparencyHack', attributeType='enum',
                      enumName='None:Nearest Layer:Two-Pass', parent='v3d', niceName='Transparency Hack')
    cmds.addExtension(nodeType=nodeType, longName='twoSided', attributeType='bool',
                      defaultValue=False, parent='v3d')
    cmds.addExtension(nodeType=nodeType, longName='depthWrite', attributeType='bool',
                      defaultValue=True, parent='v3d')
    cmds.addExtension(nodeType=nodeType, longName='depthTest', attributeType='bool',
                      defaultValue=True, parent='v3d')
    cmds.addExtension(nodeType=nodeType, longName='dithering', attributeType='bool',
                      defaultValue=False, parent='v3d')

    if nodeType in MATERIALS_WITH_GLTF_COMPAT:
        cmds.addExtension(nodeType=nodeType, longName='gltfCompat', attributeType='bool',
                          defaultValue=False, parent='v3d', niceName='glTF 2.0 Compatible')

def checkVergeAttrs(mNode):
    if 'v3d' in cmds.listAttr(mNode):
        return True
    else:
        return False

def parseMaterialSettings(mMat):

    defaultSettings = {
        'alphaMode': 'AUTO',
        'transparencyHack': 'NONE',
        'twoSided': False,
        'depthWrite': True,
        'depthTest': True,
        'dithering': False,
        'gltfCompat': True
    }

    matType = cmds.objectType(mMat)

    if matType not in SUPPORTED_MATERIALS:
        return defaultSettings

    if not checkVergeAttrs(mMat):
        log.error('Settings not found on {}'.format(mMat))
        return defaultSettings

    output = {}

    for name in defaultSettings:
        if name == 'gltfCompat' and (matType not in MATERIALS_WITH_GLTF_COMPAT):
            if matType == 'usdPreviewSurface':
                output['gltfCompat'] = True
            else:
                output['gltfCompat'] = False

            continue

        value = cmds.getAttr(mMat + '.v3d.' + name)

        if name == 'alphaMode':
            value = ALPHA_MODE_MAP[value]
        elif name == 'transparencyHack':
            value = TRANSPARENCY_HACK_MAP[value]

        output[name] = value

    return output

def assignTextureSettings():
    if cmds.attributeQuery('v3d', type='file', exists=True):
        return

    cmds.addExtension(nodeType='file', longName='v3d', attributeType='compound',
                      numberOfChildren=2, niceName='Verge3D')

    cmds.addExtension(nodeType='file', longName='anisotropy', attributeType='enum',
                      enumName=':'.join(ANISOTROPY_MODES), parent='v3d', niceName='Anisotropic Filtering')

    cmds.addExtension(nodeType='file', longName='compressionMethod', attributeType='enum',
                      enumName=':'.join(COMPRESSION_METHODS), parent='v3d', niceName='Compression')

def parseTextureSettings(mTex):

    defaultSettings = {
        'anisotropy': 1,
        'compressionMethod': COMPRESSION_METHODS[0]
    }

    if cmds.objectType(mTex) == 'aiImage':
        return defaultSettings

    if not checkVergeAttrs(mTex):
        log.error('Settings not found on {}'.format(mTex))
        return defaultSettings

    output = {}

    for name in defaultSettings:
        value = cmds.getAttr(mTex + '.v3d.' + name)

        if name == 'anisotropy':
            value = int(ANISOTROPY_MODES[value].rstrip('x'))

        elif name == 'compressionMethod':
            value = COMPRESSION_METHODS[value]

        output[name] = value

    return output
