glTF exporter: texture slots: code cleanup

This commit is contained in:
Julien Duroure 2020-06-23 19:30:58 +02:00
parent e47d2bcfad
commit 2d8c1b2c61
10 changed files with 268 additions and 346 deletions

View File

@ -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',

View File

@ -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
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
# 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
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
# 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)
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)
# 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
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()
@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

View File

@ -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):
if len(input_node.inputs['Vector'].links) == 0:
return 0
input_node = input_node.inputs['Vector'].links[0].from_node
if not isinstance(input_node, bpy.types.ShaderNodeUVMap):
return 0
if input_node.uv_map == '':
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
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
else:
raise NotImplementedError()
input_node = blender_shader_node.inputs['Vector'].links[0].from_node
def __is_socket(sockets_or_slots):
return isinstance(sockets_or_slots[0], bpy.types.NodeSocket)
if isinstance(input_node, bpy.types.ShaderNodeMapping):
if len(input_node.inputs['Vector'].links) == 0:
return 0
input_node = input_node.inputs['Vector'].links[0].from_node
if not isinstance(input_node, bpy.types.ShaderNodeUVMap):
return 0
if input_node.uv_map == '':
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
return 0
def __get_tex_from_socket(socket):

View File

@ -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)
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):
if len(input_node.inputs['Vector'].links) == 0:
return 0
input_node = input_node.inputs['Vector'].links[0].from_node
if not isinstance(input_node, bpy.types.ShaderNodeUVMap):
return 0
if input_node.uv_map == '':
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
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
else:
raise NotImplementedError()
input_node = blender_shader_node.inputs['Vector'].links[0].from_node
def __is_socket(sockets_or_slots):
return isinstance(sockets_or_slots[0], bpy.types.NodeSocket)
if isinstance(input_node, bpy.types.ShaderNodeMapping):
if len(input_node.inputs['Vector'].links) == 0:
return 0
input_node = input_node.inputs['Vector'].links[0].from_node
if not isinstance(input_node, bpy.types.ShaderNodeUVMap):
return 0
if input_node.uv_map == '':
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
return 0
def __get_tex_from_socket(socket):

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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_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_or_texture_slots, export_settings):
return gltf2_blender_gather_image.gather_image(blender_shader_sockets_or_texture_slots, export_settings)
def __gather_source(blender_shader_sockets, export_settings):
return gltf2_blender_gather_image.gather_image(blender_shader_sockets, export_settings)
# Helpers

View File

@ -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)
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 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
if isinstance(input_node, bpy.types.ShaderNodeMapping):
if len(input_node.inputs['Vector'].links) == 0:
return 0
input_node = input_node.inputs['Vector'].links[0].from_node
if not isinstance(input_node, bpy.types.ShaderNodeUVMap):
return 0
if input_node.uv_map == '':
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
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
elif isinstance(blender_shader_sockets_or_texture_slots[0], bpy.types.MaterialTextureSlot):
# TODO: implement for texture slots
input_node = blender_shader_node.inputs['Vector'].links[0].from_node
if isinstance(input_node, bpy.types.ShaderNodeMapping):
if len(input_node.inputs['Vector'].links) == 0:
return 0
input_node = input_node.inputs['Vector'].links[0].from_node
if not isinstance(input_node, bpy.types.ShaderNodeUVMap):
return 0
else:
raise NotImplementedError()
if input_node.uv_map == '':
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
return 0
def __get_tex_from_socket(socket):

View File

@ -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: