# SPDX-License-Identifier: Apache-2.0 # Copyright 2018-2022 The glTF-Blender-IO authors. from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info from io_scene_gltf2.blender.exp import gltf2_blender_get from io_scene_gltf2.io.com.gltf2_io_extensions import Extension 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' and node.is_active_output: 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 def __detect_mix_alpha(socket): # Detects this (used for an alpha hookup) # # [ Mix ] # alpha_socket => [Factor ] => socket # [Transparent] => [Shader ] # next_socket => [Shader ] # # Returns None if not detected. Otherwise, a dict containing alpha_socket # and next_socket. prev = gltf2_blender_get.previous_node(socket) if prev is None or prev.type != 'MIX_SHADER': return None in1 = gltf2_blender_get.previous_node(prev.inputs[1]) if in1 is None or in1.type != 'BSDF_TRANSPARENT': return None return { 'alpha_socket': prev.inputs[0], 'next_socket': prev.inputs[2], } 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} def gather_base_color_factor(info, export_settings): rgb, alpha = None, None if 'rgb_socket' in info: rgb = gltf2_blender_get.get_factor_from_socket(info['rgb_socket'], kind='RGB') if 'alpha_socket' in info: alpha = gltf2_blender_get.get_factor_from_socket(info['alpha_socket'], kind='VALUE') if rgb is None: rgb = [1.0, 1.0, 1.0] if alpha is None: alpha = 1.0 rgba = [*rgb, alpha] if rgba == [1, 1, 1, 1]: return None return rgba def gather_base_color_texture(info, export_settings): sockets = (info.get('rgb_socket'), info.get('alpha_socket')) sockets = tuple(s for s in sockets if s is not None) if sockets: # NOTE: separate RGB and Alpha textures will not get combined # because gather_image determines how to pack images based on the # names of sockets, and the names are hard-coded to a Principled # style graph. unlit_texture, unlit_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( sockets[0], sockets, export_settings, ) return unlit_texture, ["unlitTexture"] if unlit_use_active_uvmap else None return None, None