Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Duroure <julien.duroure@gmail.com>2021-01-04 22:34:41 +0300
committerJulien Duroure <julien.duroure@gmail.com>2021-01-04 22:34:41 +0300
commit70efc485eca862b7e191d17c4b4456402dd98d9f (patch)
tree11ad653ec73d47b8fdba04c30045731c4601eca1
parent2660631657c4b38e4ee9653c6395988bb9ca8313 (diff)
glTF exporter: roundtrip all texture wrap modes
-rwxr-xr-xio_scene_gltf2/__init__.py2
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py99
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py72
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_get.py13
4 files changed, 126 insertions, 60 deletions
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index d8bee58c..127ea6e5 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, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
- "version": (1, 5, 9),
+ "version": (1, 5, 10),
'blender': (2, 91, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
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 abbd7e94..40dfdb16 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_sampler.py
@@ -17,18 +17,25 @@ from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions
from io_scene_gltf2.io.com.gltf2_io_constants import TextureFilter, TextureWrap
+from io_scene_gltf2.blender.exp.gltf2_blender_get import (
+ previous_node,
+ previous_socket,
+ get_const_from_socket,
+)
@cached
def gather_sampler(blender_shader_node: bpy.types.Node, export_settings):
+ wrap_s, wrap_t = __gather_wrap(blender_shader_node, export_settings)
+
sampler = gltf2_io.Sampler(
extensions=__gather_extensions(blender_shader_node, export_settings),
extras=__gather_extras(blender_shader_node, export_settings),
mag_filter=__gather_mag_filter(blender_shader_node, export_settings),
min_filter=__gather_min_filter(blender_shader_node, export_settings),
name=__gather_name(blender_shader_node, export_settings),
- wrap_s=__gather_wrap_s(blender_shader_node, export_settings),
- wrap_t=__gather_wrap_t(blender_shader_node, export_settings)
+ wrap_s=wrap_s,
+ wrap_t=wrap_t,
)
export_user_extensions('gather_sampler_hook', export_settings, sampler, blender_shader_node)
@@ -83,13 +90,83 @@ def __gather_name(blender_shader_node, export_settings):
return None
-def __gather_wrap_s(blender_shader_node, export_settings):
- if blender_shader_node.extension == 'EXTEND':
- return TextureWrap.ClampToEdge
- return None
-
-
-def __gather_wrap_t(blender_shader_node, export_settings):
+def __gather_wrap(blender_shader_node, export_settings):
+ # First gather from the Texture node
if blender_shader_node.extension == 'EXTEND':
- return TextureWrap.ClampToEdge
- return None
+ wrap_s = TextureWrap.ClampToEdge
+ elif blender_shader_node.extension == 'CLIP':
+ # Not possible in glTF, but ClampToEdge is closest
+ wrap_s = TextureWrap.ClampToEdge
+ else:
+ wrap_s = TextureWrap.Repeat
+ wrap_t = wrap_s
+
+ # Take manual wrapping into account
+ result = detect_manual_uv_wrapping(blender_shader_node)
+ if result:
+ if result['wrap_s'] is not None: wrap_s = result['wrap_s']
+ if result['wrap_t'] is not None: wrap_t = result['wrap_t']
+
+ # Omit if both are repeat
+ if (wrap_s, wrap_t) == (TextureWrap.Repeat, TextureWrap.Repeat):
+ wrap_s, wrap_t = None, None
+
+ return wrap_s, wrap_t
+
+
+def detect_manual_uv_wrapping(blender_shader_node):
+ # Detects UV wrapping done using math nodes. This is for emulating wrap
+ # modes Blender doesn't support. It looks like
+ #
+ # next_socket => [Sep XYZ] => [Wrap S] => [Comb XYZ] => blender_shader_node
+ # => [Wrap T] =>
+ #
+ # The [Wrap _] blocks are either math nodes (eg. PINGPONG for mirrored
+ # repeat), or can be omitted.
+ #
+ # Returns None if not detected. Otherwise a dict containing the wrap
+ # mode in each direction (or None), and next_socket.
+ result = {}
+
+ comb = previous_node(blender_shader_node.inputs['Vector'])
+ if comb is None or comb.type != 'COMBXYZ': return None
+
+ for soc in ['X', 'Y']:
+ node = previous_node(comb.inputs[soc])
+ if node is None: return None
+
+ if node.type == 'SEPXYZ':
+ # Passed through without change
+ wrap = None
+ prev_socket = previous_socket(comb.inputs[soc])
+ elif node.type == 'MATH':
+ # Math node applies a manual wrap
+ if (node.operation == 'PINGPONG' and
+ get_const_from_socket(node.inputs[1], kind='VALUE') == 1.0): # scale = 1
+ wrap = TextureWrap.MirroredRepeat
+ elif (node.operation == 'WRAP' and
+ get_const_from_socket(node.inputs[1], kind='VALUE') == 0.0 and # min = 0
+ get_const_from_socket(node.inputs[2], kind='VALUE') == 1.0): # max = 1
+ wrap = TextureWrap.Repeat
+ else:
+ return None
+
+ prev_socket = previous_socket(node.inputs[0])
+ else:
+ return None
+
+ if prev_socket is None: return None
+ prev_node = prev_socket.node
+ if prev_node.type != 'SEPXYZ': return None
+ # Make sure X goes to X, etc.
+ if prev_socket.name != soc: return None
+ # Make sure both attach to the same SeparateXYZ node
+ if soc == 'X':
+ sep = prev_node
+ else:
+ if sep != prev_node: return None
+
+ result['wrap_s' if soc == 'X' else 'wrap_t'] = wrap
+
+ result['next_socket'] = sep.inputs[0]
+ return result
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 30975a3f..6c4acb82 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
@@ -19,6 +19,8 @@ from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture
from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree
from io_scene_gltf2.blender.exp import gltf2_blender_get
+from io_scene_gltf2.blender.exp.gltf2_blender_get import previous_node
+from io_scene_gltf2.blender.exp.gltf2_blender_gather_sampler import detect_manual_uv_wrapping
from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions
@@ -47,11 +49,13 @@ def __gather_texture_info_helper(
if not __filter_texture_info(primary_socket, blender_shader_sockets, export_settings):
return None
+ tex_transform, tex_coord = __gather_texture_transform_and_tex_coord(primary_socket, export_settings)
+
fields = {
- 'extensions': __gather_extensions(primary_socket, export_settings),
+ 'extensions': __gather_extensions(tex_transform, export_settings),
'extras': __gather_extras(blender_shader_sockets, export_settings),
'index': __gather_index(blender_shader_sockets, export_settings),
- 'tex_coord': __gather_tex_coord(primary_socket, export_settings),
+ 'tex_coord': tex_coord,
}
if kind == 'DEFAULT':
@@ -89,17 +93,9 @@ def __filter_texture_info(primary_socket, blender_shader_sockets, export_setting
return True
-def __gather_extensions(primary_socket, export_settings):
- if not hasattr(primary_socket, 'links'):
- return None
-
- texture_node = __get_tex_from_socket(primary_socket).shader_node
- if texture_node is None:
- return None
- texture_transform = gltf2_blender_get.get_texture_transform_from_texture_node(texture_node)
+def __gather_extensions(texture_transform, export_settings):
if texture_transform is None:
return None
-
extension = Extension("KHR_texture_transform", texture_transform)
return {"KHR_texture_transform": extension}
@@ -144,33 +140,37 @@ def __gather_index(blender_shader_sockets, export_settings):
return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets, export_settings)
-def __gather_tex_coord(primary_socket, export_settings):
+def __gather_texture_transform_and_tex_coord(primary_socket, export_settings):
+ # We're expecting
+ #
+ # [UV Map] => [Mapping] => [UV Wrapping] => [Texture Node] => ... => primary_socket
+ #
+ # The [UV Wrapping] is for wrap modes like MIRROR that use nodes,
+ # [Mapping] is for KHR_texture_transform, and [UV Map] is for texCoord.
blender_shader_node = __get_tex_from_socket(primary_socket).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
- return 0
+ # Skip over UV wrapping stuff (it goes in the sampler)
+ result = detect_manual_uv_wrapping(blender_shader_node)
+ if result:
+ node = previous_node(result['next_socket'])
+ else:
+ node = previous_node(blender_shader_node.inputs['Vector'])
+
+ texture_transform = None
+ if node and node.type == 'MAPPING':
+ texture_transform = gltf2_blender_get.get_texture_transform_from_mapping_node(node)
+ node = previous_node(node.inputs['Vector'])
+
+ texcoord_idx = 0
+ if node and node.type == 'UVMAP' and node.uv_map:
+ # Try to gather map index.
+ for blender_mesh in bpy.data.meshes:
+ i = blender_mesh.uv_layers.find(node.uv_map)
+ if i >= 0:
+ texcoord_idx = i
+ break
+
+ return texture_transform, texcoord_idx or None
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 ee63aa7e..b7a52114 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py
@@ -132,18 +132,7 @@ def find_shader_image_from_shader_socket(shader_socket, max_hops=10):
return None
-def get_texture_transform_from_texture_node(texture_node):
- if not isinstance(texture_node, bpy.types.ShaderNodeTexImage):
- return None
-
- mapping_socket = texture_node.inputs["Vector"]
- if len(mapping_socket.links) == 0:
- return None
-
- mapping_node = mapping_socket.links[0].from_node
- if not isinstance(mapping_node, bpy.types.ShaderNodeMapping):
- return None
-
+def get_texture_transform_from_mapping_node(mapping_node):
if mapping_node.vector_type not in ["TEXTURE", "POINT", "VECTOR"]:
gltf2_io_debug.print_console("WARNING",
"Skipping exporting texture transform because it had type " +