def detect_manual_uv_wrapping(blender_shader_node):
    # Detects UV wrapping done using math nodes. This is for emulating wrap
    # modes Blender doesn't support. It looks like
    #
    #     next_socket => [Sep XYZ] => [Wrap S] => [Comb XYZ] => blender_shader_node
    #                              => [Wrap T] =>
    #
    # The [Wrap _] blocks are either math nodes (eg. PINGPONG for mirrored
    # repeat), or can be omitted.
    #
    # Returns None if not detected. Otherwise a dict containing the wrap
    # mode in each direction (or None), and next_socket.
    result = {}

    comb = previous_node(blender_shader_node.inputs['Vector'])
    if comb is None or comb.type != 'COMBXYZ': return None

    for soc in ['X', 'Y']:
        node = previous_node(comb.inputs[soc])
        if node is None: return None

        if node.type == 'SEPXYZ':
            # Passed through without change
            wrap = None
            prev_socket = previous_socket(comb.inputs[soc])
        elif node.type == 'MATH':
            # Math node applies a manual wrap
            if (node.operation == 'PINGPONG' and get_const_from_socket(
                    node.inputs[1], kind='VALUE') == 1.0):  # scale = 1
                wrap = TextureWrap.MirroredRepeat
            elif (node.operation == 'WRAP' and get_const_from_socket(
                    node.inputs[1], kind='VALUE') == 0.0 and  # min = 0
                  get_const_from_socket(node.inputs[2], kind='VALUE')
                  == 1.0):  # max = 1
                wrap = TextureWrap.Repeat
            else:
                return None

            prev_socket = previous_socket(node.inputs[0])
        else:
            return None

        if prev_socket is None: return None
        prev_node = prev_socket.node
        if prev_node.type != 'SEPXYZ': return None
        # Make sure X goes to X, etc.
        if prev_socket.name != soc: return None
        # Make sure both attach to the same SeparateXYZ node
        if soc == 'X':
            sep = prev_node
        else:
            if sep != prev_node: return None

        result['wrap_s' if soc == 'X' else 'wrap_t'] = wrap

    result['next_socket'] = sep.inputs[0]
    return result
Exemple #2
0
def __detect_lightpath_trick(socket):
    # Detects this (used to prevent casting light on other objects) See ex.
    # https://blender.stackexchange.com/a/21535/88681
    #
    #                 [   Lightpath  ]    [    Mix    ]
    #                 [ Is Camera Ray] => [Factor     ] => socket
    #                     (don't care) => [Shader     ]
    #      next_socket => [ Emission ] => [Shader     ]
    #
    # The Emission node can be omitted.
    # Returns None if not detected. Otherwise, a dict containing
    # next_socket.
    prev = gltf2_blender_get.previous_node(socket)
    if prev is None or prev.type != 'MIX_SHADER': return None
    in0 = gltf2_blender_get.previous_socket(prev.inputs[0])
    if in0 is None or in0.node.type != 'LIGHT_PATH': return None
    if in0.name != 'Is Camera Ray': return None
    next_socket = prev.inputs[2]

    # Detect emission
    prev = gltf2_blender_get.previous_node(next_socket)
    if prev is not None and prev.type == 'EMISSION':
        next_socket = prev.inputs[0]

    return {'next_socket': next_socket}
Exemple #3
0
def detect_shadeless_material(blender_material, export_settings):
    """Detect if this material is "shadeless" ie. should be exported
    with KHR_materials_unlit. Returns None if not. Otherwise, returns
    a dict with info from parsing the node tree.
    """
    if not blender_material.use_nodes: return None

    # Old Background node detection (unlikely to happen)
    bg_socket = gltf2_blender_get.get_socket(blender_material, "Background")
    if bg_socket is not None:
        return {'rgb_socket': bg_socket}

    # Look for
    # * any color socket, connected to...
    # * optionally, the lightpath trick, connected to...
    # * optionally, a mix-with-transparent (for alpha), connected to...
    # * the output node

    info = {}

    for node in blender_material.node_tree.nodes:
        if node.type == 'OUTPUT_MATERIAL':
            socket = node.inputs[0]
            break
    else:
        return None

    # Be careful not to misidentify a lightpath trick as mix-alpha.
    result = __detect_lightpath_trick(socket)
    if result is not None:
        socket = result['next_socket']
    else:
        result = __detect_mix_alpha(socket)
        if result is not None:
            socket = result['next_socket']
            info['alpha_socket'] = result['alpha_socket']

        result = __detect_lightpath_trick(socket)
        if result is not None:
            socket = result['next_socket']

    # Check if a color socket, or connected to a color socket
    if socket.type != 'RGBA':
        from_socket = gltf2_blender_get.previous_socket(socket)
        if from_socket is None: return None
        if from_socket.type != 'RGBA': return None

    info['rgb_socket'] = socket
    return info