diff options
10 files changed, 257 insertions, 335 deletions
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index f4c00573..62f3382e 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 3, 22), + "version": (1, 3, 23), 'blender': (2, 90, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py index d1d4d2b9..7c1fa861 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py @@ -29,18 +29,17 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension @cached def gather_image( - blender_shader_sockets_or_texture_slots: typing.Union[typing.Tuple[bpy.types.NodeSocket], - typing.Tuple[bpy.types.Texture]], + blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], export_settings): - if not __filter_image(blender_shader_sockets_or_texture_slots, export_settings): + if not __filter_image(blender_shader_sockets, export_settings): return None - image_data = __get_image_data(blender_shader_sockets_or_texture_slots, export_settings) + image_data = __get_image_data(blender_shader_sockets, export_settings) if image_data.empty(): # The export image has no data return None - mime_type = __gather_mime_type(blender_shader_sockets_or_texture_slots, image_data, export_settings) + mime_type = __gather_mime_type(blender_shader_sockets, image_data, export_settings) name = __gather_name(image_data, export_settings) uri = __gather_uri(image_data, mime_type, name, export_settings) @@ -48,15 +47,15 @@ def gather_image( image = __make_image( buffer_view, - __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings), - __gather_extras(blender_shader_sockets_or_texture_slots, export_settings), + __gather_extensions(blender_shader_sockets, export_settings), + __gather_extras(blender_shader_sockets, export_settings), mime_type, name, uri, export_settings ) - export_user_extensions('gather_image_hook', export_settings, image, blender_shader_sockets_or_texture_slots) + export_user_extensions('gather_image_hook', export_settings, image, blender_shader_sockets) return image @@ -72,8 +71,8 @@ def __make_image(buffer_view, extensions, extras, mime_type, name, uri, export_s ) -def __filter_image(sockets_or_slots, export_settings): - if not sockets_or_slots: +def __filter_image(sockets, export_settings): + if not sockets: return False return True @@ -85,17 +84,17 @@ def __gather_buffer_view(image_data, mime_type, name, export_settings): return None -def __gather_extensions(sockets_or_slots, export_settings): +def __gather_extensions(sockets, export_settings): return None -def __gather_extras(sockets_or_slots, export_settings): +def __gather_extras(sockets, export_settings): return None -def __gather_mime_type(sockets_or_slots, export_image, export_settings): +def __gather_mime_type(sockets, export_image, export_settings): # force png if Alpha contained so we can export alpha - for socket in sockets_or_slots: + for socket in sockets: if socket.name == "Alpha": return "image/png" @@ -136,7 +135,6 @@ def __gather_name(export_image, export_settings): return name or 'Image' - @cached def __gather_uri(image_data, mime_type, name, export_settings): if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF_SEPARATE': @@ -150,77 +148,62 @@ def __gather_uri(image_data, mime_type, name, export_settings): return None -def __is_socket(sockets_or_slots): - return isinstance(sockets_or_slots[0], bpy.types.NodeSocket) - - -def __is_slot(sockets_or_slots): - return isinstance(sockets_or_slots[0], bpy.types.MaterialTextureSlot) - - -def __get_image_data(sockets_or_slots, export_settings) -> ExportImage: +def __get_image_data(sockets, export_settings) -> ExportImage: # For shared resources, such as images, we just store the portion of data that is needed in the glTF property # in a helper class. During generation of the glTF in the exporter these will then be combined to actual binary # resources. - if __is_socket(sockets_or_slots): - results = [__get_tex_from_socket(socket, export_settings) for socket in sockets_or_slots] - composed_image = ExportImage() - for result, socket in zip(results, sockets_or_slots): - if result.shader_node.image.channels == 0: - gltf2_io_debug.print_console("WARNING", - "Image '{}' has no color channels and cannot be exported.".format( - result.shader_node.image)) - continue - - # rudimentarily try follow the node tree to find the correct image data. - src_chan = Channel.R - for elem in result.path: - if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateRGB): - src_chan = { - 'R': Channel.R, - 'G': Channel.G, - 'B': Channel.B, - }[elem.from_socket.name] - if elem.from_socket.name == 'Alpha': - src_chan = Channel.A - - dst_chan = None - - # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes) - if socket.name == 'Metallic': - dst_chan = Channel.B - elif socket.name == 'Roughness': - dst_chan = Channel.G - elif socket.name == 'Occlusion': - dst_chan = Channel.R - elif socket.name == 'Alpha' and len(sockets_or_slots) > 1 and sockets_or_slots[1] is not None: - dst_chan = Channel.A - elif socket.name == 'Clearcoat': - dst_chan = Channel.R - elif socket.name == 'Clearcoat Roughness': - dst_chan = Channel.G - - if dst_chan is not None: - composed_image.fill_image(result.shader_node.image, dst_chan, src_chan) - - # Since metal/roughness are always used together, make sure - # the other channel is filled. - if socket.name == 'Metallic' and not composed_image.is_filled(Channel.G): - composed_image.fill_white(Channel.G) - elif socket.name == 'Roughness' and not composed_image.is_filled(Channel.B): - composed_image.fill_white(Channel.B) - else: - # copy full image...eventually following sockets might overwrite things - composed_image = ExportImage.from_blender_image(result.shader_node.image) - - return composed_image - - elif __is_slot(sockets_or_slots): - texture = __get_tex_from_slot(sockets_or_slots[0]) - image = ExportImage.from_blender_image(texture.image) - return image - else: - raise NotImplementedError() + results = [__get_tex_from_socket(socket, export_settings) for socket in sockets] + composed_image = ExportImage() + for result, socket in zip(results, sockets): + if result.shader_node.image.channels == 0: + gltf2_io_debug.print_console("WARNING", + "Image '{}' has no color channels and cannot be exported.".format( + result.shader_node.image)) + continue + + # rudimentarily try follow the node tree to find the correct image data. + src_chan = Channel.R + for elem in result.path: + if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateRGB): + src_chan = { + 'R': Channel.R, + 'G': Channel.G, + 'B': Channel.B, + }[elem.from_socket.name] + if elem.from_socket.name == 'Alpha': + src_chan = Channel.A + + dst_chan = None + + # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes) + if socket.name == 'Metallic': + dst_chan = Channel.B + elif socket.name == 'Roughness': + dst_chan = Channel.G + elif socket.name == 'Occlusion': + dst_chan = Channel.R + elif socket.name == 'Alpha' and len(sockets) > 1 and sockets[1] is not None: + dst_chan = Channel.A + elif socket.name == 'Clearcoat': + dst_chan = Channel.R + elif socket.name == 'Clearcoat Roughness': + dst_chan = Channel.G + + if dst_chan is not None: + composed_image.fill_image(result.shader_node.image, dst_chan, src_chan) + + # Since metal/roughness are always used together, make sure + # the other channel is filled. + if socket.name == 'Metallic' and not composed_image.is_filled(Channel.G): + composed_image.fill_white(Channel.G) + elif socket.name == 'Roughness' and not composed_image.is_filled(Channel.B): + composed_image.fill_white(Channel.B) + else: + # copy full image...eventually following sockets might overwrite things + composed_image = ExportImage.from_blender_image(result.shader_node.image) + + return composed_image + @cached def __get_tex_from_socket(blender_shader_socket: bpy.types.NodeSocket, export_settings): @@ -232,10 +215,6 @@ def __get_tex_from_socket(blender_shader_socket: bpy.types.NodeSocket, export_se return result[0] -def __get_tex_from_slot(blender_texture_slot): - return blender_texture_slot.texture - - def __is_blender_image_a_jpeg(image: bpy.types.Image) -> bool: if image.source != 'FILE': return False diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_normal_texture_info_class.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_normal_texture_info_class.py index e0b9be21..a36d923f 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_normal_texture_info_class.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_normal_texture_info_class.py @@ -24,18 +24,18 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension @cached -def gather_material_normal_texture_info_class(blender_shader_sockets_or_texture_slots: typing.Union[ - typing.Tuple[bpy.types.NodeSocket], typing.Tuple[bpy.types.Texture]], +def gather_material_normal_texture_info_class( + blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], export_settings): - if not __filter_texture_info(blender_shader_sockets_or_texture_slots, export_settings): + if not __filter_texture_info(blender_shader_sockets, export_settings): return None texture_info = gltf2_io.MaterialNormalTextureInfoClass( - extensions=__gather_extensions(blender_shader_sockets_or_texture_slots, export_settings), - extras=__gather_extras(blender_shader_sockets_or_texture_slots, export_settings), - scale=__gather_scale(blender_shader_sockets_or_texture_slots, export_settings), - index=__gather_index(blender_shader_sockets_or_texture_slots, export_settings), - tex_coord=__gather_tex_coord(blender_shader_sockets_or_texture_slots, export_settings) + extensions=__gather_extensions(blender_shader_sockets, export_settings), + extras=__gather_extras(blender_shader_sockets, export_settings), + scale=__gather_scale(blender_shader_sockets, export_settings), + index=__gather_index(blender_shader_sockets, export_settings), + tex_coord=__gather_tex_coord(blender_shader_sockets, export_settings) ) if texture_info.index is None: @@ -44,28 +44,27 @@ def gather_material_normal_texture_info_class(blender_shader_sockets_or_texture_ export_user_extensions('gather_material_normal_texture_info_class_hook', export_settings, texture_info, - blender_shader_sockets_or_texture_slots) + blender_shader_sockets) return texture_info -def __filter_texture_info(blender_shader_sockets_or_texture_slots, export_settings): - if not blender_shader_sockets_or_texture_slots: +def __filter_texture_info(blender_shader_sockets, export_settings): + if not blender_shader_sockets: return False - if not all([elem is not None for elem in blender_shader_sockets_or_texture_slots]): + if not all([elem is not None for elem in blender_shader_sockets]): + return False + if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]): + # sockets do not lead to a texture --> discard return False - if isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.NodeSocket): - if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets_or_texture_slots]): - # sockets do not lead to a texture --> discard - return False return True -def __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings): - if not hasattr(blender_shader_sockets_or_texture_slots[0], 'links'): +def __gather_extensions(blender_shader_sockets, export_settings): + if not hasattr(blender_shader_sockets[0], 'links'): return None - tex_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets_or_texture_slots] + tex_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets] texture_node = tex_nodes[0] if (tex_nodes is not None and len(tex_nodes) > 0) else None if texture_node is None: return None @@ -77,62 +76,54 @@ def __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings return {"KHR_texture_transform": extension} -def __gather_extras(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_extras(blender_shader_sockets, export_settings): return None -def __gather_scale(blender_shader_sockets_or_texture_slots, export_settings): - if __is_socket(blender_shader_sockets_or_texture_slots): - result = gltf2_blender_search_node_tree.from_socket( - blender_shader_sockets_or_texture_slots[0], - gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeNormalMap)) - if not result: - return None - strengthInput = result[0].shader_node.inputs['Strength'] - if not strengthInput.is_linked and strengthInput.default_value != 1: - return strengthInput.default_value +def __gather_scale(blender_shader_sockets, export_settings): + result = gltf2_blender_search_node_tree.from_socket( + blender_shader_sockets[0], + gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeNormalMap)) + if not result: + return None + strengthInput = result[0].shader_node.inputs['Strength'] + if not strengthInput.is_linked and strengthInput.default_value != 1: + return strengthInput.default_value return None -def __gather_index(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_index(blender_shader_sockets, export_settings): # We just put the actual shader into the 'index' member - return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets_or_texture_slots, export_settings) - + return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets, export_settings) -def __gather_tex_coord(blender_shader_sockets_or_texture_slots, export_settings): - if __is_socket(blender_shader_sockets_or_texture_slots): - blender_shader_node = __get_tex_from_socket(blender_shader_sockets_or_texture_slots[0]).shader_node - if len(blender_shader_node.inputs['Vector'].links) == 0: - return 0 - - input_node = blender_shader_node.inputs['Vector'].links[0].from_node - if isinstance(input_node, bpy.types.ShaderNodeMapping): +def __gather_tex_coord(blender_shader_sockets, export_settings): + blender_shader_node = __get_tex_from_socket(blender_shader_sockets[0]).shader_node + if len(blender_shader_node.inputs['Vector'].links) == 0: + return 0 - if len(input_node.inputs['Vector'].links) == 0: - return 0 + input_node = blender_shader_node.inputs['Vector'].links[0].from_node - input_node = input_node.inputs['Vector'].links[0].from_node + if isinstance(input_node, bpy.types.ShaderNodeMapping): - if not isinstance(input_node, bpy.types.ShaderNodeUVMap): + if len(input_node.inputs['Vector'].links) == 0: return 0 - if input_node.uv_map == '': - return 0 + input_node = input_node.inputs['Vector'].links[0].from_node - # Try to gather map index. - for blender_mesh in bpy.data.meshes: - texCoordIndex = blender_mesh.uv_layers.find(input_node.uv_map) - if texCoordIndex >= 0: - return texCoordIndex + if not isinstance(input_node, bpy.types.ShaderNodeUVMap): + return 0 + if input_node.uv_map == '': return 0 - else: - raise NotImplementedError() + # Try to gather map index. + for blender_mesh in bpy.data.meshes: + texCoordIndex = blender_mesh.uv_layers.find(input_node.uv_map) + if texCoordIndex >= 0: + return texCoordIndex -def __is_socket(sockets_or_slots): - return isinstance(sockets_or_slots[0], bpy.types.NodeSocket) + return 0 def __get_tex_from_socket(socket): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_occlusion_texture_info_class.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_occlusion_texture_info_class.py index 671a41f4..cdbce3dd 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_occlusion_texture_info_class.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_material_occlusion_texture_info_class.py @@ -24,18 +24,18 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension @cached -def gather_material_occlusion_texture_info_class(blender_shader_sockets_or_texture_slots: typing.Union[ - typing.Tuple[bpy.types.NodeSocket], typing.Tuple[bpy.types.Texture]], +def gather_material_occlusion_texture_info_class( + blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], export_settings): - if not __filter_texture_info(blender_shader_sockets_or_texture_slots, export_settings): + if not __filter_texture_info(blender_shader_sockets, export_settings): return None texture_info = gltf2_io.MaterialOcclusionTextureInfoClass( - extensions=__gather_extensions(blender_shader_sockets_or_texture_slots, export_settings), - extras=__gather_extras(blender_shader_sockets_or_texture_slots, export_settings), - strength=__gather_scale(blender_shader_sockets_or_texture_slots, export_settings), - index=__gather_index(blender_shader_sockets_or_texture_slots, export_settings), - tex_coord=__gather_tex_coord(blender_shader_sockets_or_texture_slots, export_settings) + extensions=__gather_extensions(blender_shader_sockets, export_settings), + extras=__gather_extras(blender_shader_sockets, export_settings), + strength=__gather_scale(blender_shader_sockets, export_settings), + index=__gather_index(blender_shader_sockets, export_settings), + tex_coord=__gather_tex_coord(blender_shader_sockets, export_settings) ) if texture_info.index is None: @@ -44,28 +44,27 @@ def gather_material_occlusion_texture_info_class(blender_shader_sockets_or_textu export_user_extensions('gather_material_occlusion_texture_info_class_hook', export_settings, texture_info, - blender_shader_sockets_or_texture_slots) + blender_shader_sockets) return texture_info -def __filter_texture_info(blender_shader_sockets_or_texture_slots, export_settings): - if not blender_shader_sockets_or_texture_slots: +def __filter_texture_info(blender_shader_sockets, export_settings): + if not blender_shader_sockets: return False - if not all([elem is not None for elem in blender_shader_sockets_or_texture_slots]): + if not all([elem is not None for elem in blender_shader_sockets]): + return False + if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]): + # sockets do not lead to a texture --> discard return False - if isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.NodeSocket): - if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets_or_texture_slots]): - # sockets do not lead to a texture --> discard - return False return True -def __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings): - if not hasattr(blender_shader_sockets_or_texture_slots[0], 'links'): +def __gather_extensions(blender_shader_sockets, export_settings): + if not hasattr(blender_shader_sockets[0], 'links'): return None - tex_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets_or_texture_slots] + tex_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets] texture_node = tex_nodes[0] if (tex_nodes is not None and len(tex_nodes) > 0) else None if texture_node is None: return None @@ -77,53 +76,46 @@ def __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings return {"KHR_texture_transform": extension} -def __gather_extras(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_extras(blender_shader_sockets, export_settings): return None -def __gather_scale(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_scale(blender_shader_sockets, export_settings): return None -def __gather_index(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_index(blender_shader_sockets, export_settings): # We just put the actual shader into the 'index' member - return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets_or_texture_slots, export_settings) - - -def __gather_tex_coord(blender_shader_sockets_or_texture_slots, export_settings): - if __is_socket(blender_shader_sockets_or_texture_slots): - blender_shader_node = __get_tex_from_socket(blender_shader_sockets_or_texture_slots[0]).shader_node - if len(blender_shader_node.inputs['Vector'].links) == 0: - return 0 + return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets, export_settings) - input_node = blender_shader_node.inputs['Vector'].links[0].from_node - if isinstance(input_node, bpy.types.ShaderNodeMapping): +def __gather_tex_coord(blender_shader_sockets, export_settings): + blender_shader_node = __get_tex_from_socket(blender_shader_sockets[0]).shader_node + if len(blender_shader_node.inputs['Vector'].links) == 0: + return 0 - if len(input_node.inputs['Vector'].links) == 0: - return 0 + input_node = blender_shader_node.inputs['Vector'].links[0].from_node - input_node = input_node.inputs['Vector'].links[0].from_node + if isinstance(input_node, bpy.types.ShaderNodeMapping): - if not isinstance(input_node, bpy.types.ShaderNodeUVMap): + if len(input_node.inputs['Vector'].links) == 0: return 0 - if input_node.uv_map == '': - return 0 + input_node = input_node.inputs['Vector'].links[0].from_node - # Try to gather map index. - for blender_mesh in bpy.data.meshes: - texCoordIndex = blender_mesh.uv_layers.find(input_node.uv_map) - if texCoordIndex >= 0: - return texCoordIndex + if not isinstance(input_node, bpy.types.ShaderNodeUVMap): + return 0 + if input_node.uv_map == '': return 0 - else: - raise NotImplementedError() + # Try to gather map index. + for blender_mesh in bpy.data.meshes: + texCoordIndex = blender_mesh.uv_layers.find(input_node.uv_map) + if texCoordIndex >= 0: + return texCoordIndex -def __is_socket(sockets_or_slots): - return isinstance(sockets_or_slots[0], bpy.types.NodeSocket) + return 0 def __get_tex_from_socket(socket): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py index f55d9440..ea597f90 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py @@ -98,7 +98,7 @@ def __gather_double_sided(blender_material, mesh_double_sided, export_settings): if mesh_double_sided: return True - old_double_sided_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "DoubleSided") + old_double_sided_socket = gltf2_blender_get.get_socket_old(blender_material, "DoubleSided") if old_double_sided_socket is not None and\ not old_double_sided_socket.is_linked and\ old_double_sided_socket.default_value > 0.5: @@ -107,9 +107,9 @@ def __gather_double_sided(blender_material, mesh_double_sided, export_settings): def __gather_emissive_factor(blender_material, export_settings): - emissive_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Emissive") + emissive_socket = gltf2_blender_get.get_socket(blender_material, "Emissive") if emissive_socket is None: - emissive_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "EmissiveFactor") + emissive_socket = gltf2_blender_get.get_socket_old(blender_material, "EmissiveFactor") if isinstance(emissive_socket, bpy.types.NodeSocket): if emissive_socket.is_linked: # In glTF, the default emissiveFactor is all zeros, so if an emission texture is connected, @@ -121,9 +121,9 @@ def __gather_emissive_factor(blender_material, export_settings): def __gather_emissive_texture(blender_material, export_settings): - emissive = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Emissive") + emissive = gltf2_blender_get.get_socket(blender_material, "Emissive") if emissive is None: - emissive = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Emissive") + emissive = gltf2_blender_get.get_socket_old(blender_material, "Emissive") return gltf2_blender_gather_texture_info.gather_texture_info((emissive,), export_settings) @@ -132,7 +132,7 @@ def __gather_extensions(blender_material, export_settings): # KHR_materials_unlit - if gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Background") is not None: + if gltf2_blender_get.get_socket(blender_material, "Background") is not None: extensions["KHR_materials_unlit"] = Extension("KHR_materials_unlit", {}, False) # KHR_materials_clearcoat @@ -157,9 +157,9 @@ def __gather_name(blender_material, export_settings): def __gather_normal_texture(blender_material, export_settings): - normal = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Normal") + normal = gltf2_blender_get.get_socket(blender_material, "Normal") if normal is None: - normal = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Normal") + normal = gltf2_blender_get.get_socket_old(blender_material, "Normal") return gltf2_blender_gather_material_normal_texture_info_class.gather_material_normal_texture_info_class( (normal,), export_settings) @@ -169,20 +169,20 @@ def __gather_orm_texture(blender_material, export_settings): # Check for the presence of Occlusion, Roughness, Metallic sharing a single image. # If not fully shared, return None, so the images will be cached and processed separately. - occlusion = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Occlusion") + occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion") if occlusion is None or not __has_image_node_from_socket(occlusion): - occlusion = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Occlusion") + occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion") if occlusion is None or not __has_image_node_from_socket(occlusion): return None - metallic_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Metallic") - roughness_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Roughness") + metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic") + roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness") hasMetal = metallic_socket is not None and __has_image_node_from_socket(metallic_socket) hasRough = roughness_socket is not None and __has_image_node_from_socket(roughness_socket) if not hasMetal and not hasRough: - metallic_roughness = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "MetallicRoughness") + metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness") if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness): return None result = (occlusion, metallic_roughness) @@ -211,9 +211,9 @@ def __gather_occlusion_texture(blender_material, orm_texture, export_settings): return gltf2_blender_gather_material_occlusion_texture_info_class.gather_material_occlusion_texture_info_class( orm_texture, export_settings) - occlusion = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Occlusion") + occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion") if occlusion is None: - occlusion = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "Occlusion") + occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion") return gltf2_blender_gather_material_occlusion_texture_info_class.gather_material_occlusion_texture_info_class( (occlusion,), export_settings) @@ -241,9 +241,9 @@ def __gather_clearcoat_extension(blender_material, export_settings): clearcoat_extension = {} clearcoat_roughness_slots = () - clearcoat_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, 'Clearcoat') - clearcoat_roughness_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, 'Clearcoat Roughness') - clearcoat_normal_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, 'Clearcoat Normal') + clearcoat_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat') + clearcoat_roughness_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Roughness') + clearcoat_normal_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Normal') if isinstance(clearcoat_socket, bpy.types.NodeSocket) and not clearcoat_socket.is_linked: clearcoat_extension['clearcoatFactor'] = clearcoat_socket.default_value diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py index ebef6597..54493799 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py @@ -47,13 +47,13 @@ def __filter_pbr_material(blender_material, export_settings): def __gather_base_color_factor(blender_material, export_settings): - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Base Color") + base_color_socket = gltf2_blender_get.get_socket(blender_material, "Base Color") if base_color_socket is None: - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor") + base_color_socket = gltf2_blender_get.get_socket(blender_material, "BaseColor") if base_color_socket is None: - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "BaseColorFactor") + base_color_socket = gltf2_blender_get.get_socket_old(blender_material, "BaseColorFactor") if base_color_socket is None: - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Background") + base_color_socket = gltf2_blender_get.get_socket(blender_material, "Background") if not isinstance(base_color_socket, bpy.types.NodeSocket): return None if not base_color_socket.is_linked: @@ -89,15 +89,15 @@ def __gather_base_color_factor(blender_material, export_settings): def __gather_base_color_texture(blender_material, export_settings): - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Base Color") + base_color_socket = gltf2_blender_get.get_socket(blender_material, "Base Color") if base_color_socket is None: - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor") + base_color_socket = gltf2_blender_get.get_socket(blender_material, "BaseColor") if base_color_socket is None: - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "BaseColor") + base_color_socket = gltf2_blender_get.get_socket_old(blender_material, "BaseColor") if base_color_socket is None: - base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Background") + base_color_socket = gltf2_blender_get.get_socket(blender_material, "Background") - alpha_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Alpha") + alpha_socket = gltf2_blender_get.get_socket(blender_material, "Alpha") if alpha_socket is not None and alpha_socket.is_linked: inputs = (base_color_socket, alpha_socket, ) else: @@ -124,9 +124,9 @@ def __gather_extras(blender_material, export_settings): def __gather_metallic_factor(blender_material, export_settings): - metallic_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Metallic") + metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic") if metallic_socket is None: - metallic_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "MetallicFactor") + metallic_socket = gltf2_blender_get.get_socket_old(blender_material, "MetallicFactor") if isinstance(metallic_socket, bpy.types.NodeSocket) and not metallic_socket.is_linked: return metallic_socket.default_value return None @@ -136,14 +136,14 @@ def __gather_metallic_roughness_texture(blender_material, orm_texture, export_se if orm_texture is not None: texture_input = orm_texture else: - metallic_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Metallic") - roughness_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Roughness") + metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic") + roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness") hasMetal = metallic_socket is not None and __has_image_node_from_socket(metallic_socket) hasRough = roughness_socket is not None and __has_image_node_from_socket(roughness_socket) if not hasMetal and not hasRough: - metallic_roughness = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "MetallicRoughness") + metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness") if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness): return None texture_input = (metallic_roughness,) @@ -158,9 +158,9 @@ def __gather_metallic_roughness_texture(blender_material, orm_texture, export_se def __gather_roughness_factor(blender_material, export_settings): - roughness_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Roughness") + roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness") if roughness_socket is None: - roughness_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "RoughnessFactor") + roughness_socket = gltf2_blender_get.get_socket_old(blender_material, "RoughnessFactor") if isinstance(roughness_socket, bpy.types.NodeSocket) and not roughness_socket.is_linked: return roughness_socket.default_value return None diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py index a7d44d71..00fad3bf 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py @@ -78,25 +78,3 @@ def __gather_wrap_t(blender_shader_node, export_settings): if blender_shader_node.extension == 'EXTEND': return 33071 return None - - -@cached -def gather_sampler_from_texture_slot(blender_texture: bpy.types.TextureSlot, export_settings): - magFilter = 9729 - wrap = 10497 - if blender_texture.texture.extension == 'EXTEND': - wrap = 33071 - - minFilter = 9986 - if magFilter == 9728: - minFilter = 9984 - - return gltf2_io.Sampler( - extensions=None, - extras=None, - mag_filter=magFilter, - min_filter=minFilter, - name=None, - wrap_s=wrap, - wrap_t=wrap - ) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py index 13292a5a..52c2e8b3 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py @@ -26,8 +26,7 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension @cached def gather_texture( - blender_shader_sockets_or_texture_slots: typing.Union[ - typing.Tuple[bpy.types.NodeSocket], typing.Tuple[typing.Any]], + blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], export_settings): """ Gather texture sampling information and image channels from a blender shader texture attached to a shader socket. @@ -36,28 +35,27 @@ def gather_texture( :param export_settings: configuration of the export :return: a glTF 2.0 texture with sampler and source embedded (will be converted to references by the exporter) """ - # TODO: extend to texture slots - if not __filter_texture(blender_shader_sockets_or_texture_slots, export_settings): + if not __filter_texture(blender_shader_sockets, export_settings): return None texture = gltf2_io.Texture( - extensions=__gather_extensions(blender_shader_sockets_or_texture_slots, export_settings), - extras=__gather_extras(blender_shader_sockets_or_texture_slots, export_settings), - name=__gather_name(blender_shader_sockets_or_texture_slots, export_settings), - sampler=__gather_sampler(blender_shader_sockets_or_texture_slots, export_settings), - source=__gather_source(blender_shader_sockets_or_texture_slots, export_settings) + extensions=__gather_extensions(blender_shader_sockets, export_settings), + extras=__gather_extras(blender_shader_sockets, export_settings), + name=__gather_name(blender_shader_sockets, export_settings), + sampler=__gather_sampler(blender_shader_sockets, export_settings), + source=__gather_source(blender_shader_sockets, export_settings) ) # although valid, most viewers can't handle missing source properties if texture.source is None: return None - export_user_extensions('gather_texture_hook', export_settings, texture, blender_shader_sockets_or_texture_slots) + export_user_extensions('gather_texture_hook', export_settings, texture, blender_shader_sockets) return texture -def __filter_texture(blender_shader_sockets_or_texture_slots, export_settings): +def __filter_texture(blender_shader_sockets, export_settings): return True @@ -73,28 +71,19 @@ def __gather_name(blender_shader_sockets, export_settings): return None -def __gather_sampler(blender_shader_sockets_or_texture_slots, export_settings): - if isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.NodeSocket): - shader_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets_or_texture_slots] - if len(shader_nodes) > 1: - gltf2_io_debug.print_console("WARNING", - "More than one shader node tex image used for a texture. " - "The resulting glTF sampler will behave like the first shader node tex image.") - return gltf2_blender_gather_sampler.gather_sampler( - shader_nodes[0], - export_settings) - elif isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.MaterialTextureSlot): - return gltf2_blender_gather_sampler.gather_sampler_from_texture_slot( - blender_shader_sockets_or_texture_slots[0], - export_settings - ) - else: - # TODO: implement texture slot sampler - raise NotImplementedError() - - -def __gather_source(blender_shader_sockets_or_texture_slots, export_settings): - return gltf2_blender_gather_image.gather_image(blender_shader_sockets_or_texture_slots, export_settings) +def __gather_sampler(blender_shader_sockets, export_settings): + shader_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets] + if len(shader_nodes) > 1: + gltf2_io_debug.print_console("WARNING", + "More than one shader node tex image used for a texture. " + "The resulting glTF sampler will behave like the first shader node tex image.") + return gltf2_blender_gather_sampler.gather_sampler( + shader_nodes[0], + export_settings) + + +def __gather_source(blender_shader_sockets, export_settings): + return gltf2_blender_gather_image.gather_image(blender_shader_sockets, export_settings) # Helpers diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py index 57b0b5a2..f42ee036 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py @@ -24,45 +24,44 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension @cached -def gather_texture_info(blender_shader_sockets_or_texture_slots: typing.Union[ - typing.Tuple[bpy.types.NodeSocket], typing.Tuple[bpy.types.Texture]], +def gather_texture_info( + blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], export_settings): - if not __filter_texture_info(blender_shader_sockets_or_texture_slots, export_settings): + if not __filter_texture_info(blender_shader_sockets, export_settings): return None texture_info = gltf2_io.TextureInfo( - extensions=__gather_extensions(blender_shader_sockets_or_texture_slots, export_settings), - extras=__gather_extras(blender_shader_sockets_or_texture_slots, export_settings), - index=__gather_index(blender_shader_sockets_or_texture_slots, export_settings), - tex_coord=__gather_tex_coord(blender_shader_sockets_or_texture_slots, export_settings) + extensions=__gather_extensions(blender_shader_sockets, export_settings), + extras=__gather_extras(blender_shader_sockets, export_settings), + index=__gather_index(blender_shader_sockets, export_settings), + tex_coord=__gather_tex_coord(blender_shader_sockets, export_settings) ) if texture_info.index is None: return None - export_user_extensions('gather_texture_info_hook', export_settings, texture_info, blender_shader_sockets_or_texture_slots) + export_user_extensions('gather_texture_info_hook', export_settings, texture_info, blender_shader_sockets) return texture_info -def __filter_texture_info(blender_shader_sockets_or_texture_slots, export_settings): - if not blender_shader_sockets_or_texture_slots: +def __filter_texture_info(blender_shader_sockets, export_settings): + if not blender_shader_sockets: return False - if not all([elem is not None for elem in blender_shader_sockets_or_texture_slots]): + if not all([elem is not None for elem in blender_shader_sockets]): + return False + if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]): + # sockets do not lead to a texture --> discard return False - if isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.NodeSocket): - if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets_or_texture_slots]): - # sockets do not lead to a texture --> discard - return False return True -def __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings): - if not hasattr(blender_shader_sockets_or_texture_slots[0], 'links'): +def __gather_extensions(blender_shader_sockets, export_settings): + if not hasattr(blender_shader_sockets[0], 'links'): return None - tex_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets_or_texture_slots] + tex_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets] texture_node = tex_nodes[0] if (tex_nodes is not None and len(tex_nodes) > 0) else None if texture_node is None: return None @@ -74,48 +73,42 @@ def __gather_extensions(blender_shader_sockets_or_texture_slots, export_settings return {"KHR_texture_transform": extension} -def __gather_extras(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_extras(blender_shader_sockets, export_settings): return None -def __gather_index(blender_shader_sockets_or_texture_slots, export_settings): +def __gather_index(blender_shader_sockets, export_settings): # We just put the actual shader into the 'index' member - return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets_or_texture_slots, export_settings) - - -def __gather_tex_coord(blender_shader_sockets_or_texture_slots, export_settings): - if isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.NodeSocket): - blender_shader_node = __get_tex_from_socket(blender_shader_sockets_or_texture_slots[0]).shader_node - if len(blender_shader_node.inputs['Vector'].links) == 0: - return 0 - - input_node = blender_shader_node.inputs['Vector'].links[0].from_node + return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets, export_settings) - if isinstance(input_node, bpy.types.ShaderNodeMapping): - if len(input_node.inputs['Vector'].links) == 0: - return 0 +def __gather_tex_coord(blender_shader_sockets, export_settings): + blender_shader_node = __get_tex_from_socket(blender_shader_sockets[0]).shader_node + if len(blender_shader_node.inputs['Vector'].links) == 0: + return 0 - input_node = input_node.inputs['Vector'].links[0].from_node + input_node = blender_shader_node.inputs['Vector'].links[0].from_node - if not isinstance(input_node, bpy.types.ShaderNodeUVMap): - return 0 + if isinstance(input_node, bpy.types.ShaderNodeMapping): - if input_node.uv_map == '': + if len(input_node.inputs['Vector'].links) == 0: return 0 - # Try to gather map index. - for blender_mesh in bpy.data.meshes: - texCoordIndex = blender_mesh.uv_layers.find(input_node.uv_map) - if texCoordIndex >= 0: - return texCoordIndex + input_node = input_node.inputs['Vector'].links[0].from_node + if not isinstance(input_node, bpy.types.ShaderNodeUVMap): return 0 - elif isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.MaterialTextureSlot): - # TODO: implement for texture slots + + if input_node.uv_map == '': return 0 - else: - raise NotImplementedError() + + # Try to gather map index. + for blender_mesh in bpy.data.meshes: + texCoordIndex = blender_mesh.uv_layers.find(input_node.uv_map) + if texCoordIndex >= 0: + return texCoordIndex + + return 0 def __get_tex_from_socket(socket): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_get.py b/io_scene_gltf2/blender/exp/gltf2_blender_get.py index 3cee76b4..4f66aa6a 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py @@ -43,13 +43,13 @@ def get_object_from_datapath(blender_object, data_path: str): return prop -def get_socket_or_texture_slot(blender_material: bpy.types.Material, name: str): +def get_socket(blender_material: bpy.types.Material, name: str): """ - For a given material input name, retrieve the corresponding node tree socket or blender render texture slot. + For a given material input name, retrieve the corresponding node tree socket. - :param blender_material: a blender material for which to get the socket/slot - :param name: the name of the socket/slot - :return: either a blender NodeSocket, if the material is a node tree or a blender Texture otherwise + :param blender_material: a blender material for which to get the socket + :param name: the name of the socket + :return: a blender NodeSocket """ if blender_material.node_tree and blender_material.use_nodes: #i = [input for input in blender_material.node_tree.inputs] @@ -79,13 +79,13 @@ def get_socket_or_texture_slot(blender_material: bpy.types.Material, name: str): return None -def get_socket_or_texture_slot_old(blender_material: bpy.types.Material, name: str): +def get_socket_old(blender_material: bpy.types.Material, name: str): """ For a given material input name, retrieve the corresponding node tree socket in the special glTF node group. - :param blender_material: a blender material for which to get the socket/slot - :param name: the name of the socket/slot - :return: either a blender NodeSocket, if the material is a node tree or a blender Texture otherwise + :param blender_material: a blender material for which to get the socket + :param name: the name of the socket + :return: a blender NodeSocket """ gltf_node_group_name = get_gltf_node_name().lower() if blender_material.node_tree and blender_material.use_nodes: |