1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
# SPDX-License-Identifier: Apache-2.0
# Copyright 2018-2021 The glTF-Blender-IO authors.
import bpy
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=wrap_s,
wrap_t=wrap_t,
)
export_user_extensions('gather_sampler_hook', export_settings, sampler, blender_shader_node)
if not sampler.extensions and not sampler.extras and not sampler.name:
return __sampler_by_value(
sampler.mag_filter,
sampler.min_filter,
sampler.wrap_s,
sampler.wrap_t,
export_settings,
)
return sampler
@cached
def __sampler_by_value(mag_filter, min_filter, wrap_s, wrap_t, export_settings):
# @cached function to dedupe samplers with the same settings.
return gltf2_io.Sampler(
extensions=None,
extras=None,
mag_filter=mag_filter,
min_filter=min_filter,
name=None,
wrap_s=wrap_s,
wrap_t=wrap_t,
)
def __gather_extensions(blender_shader_node, export_settings):
return None
def __gather_extras(blender_shader_node, export_settings):
return None
def __gather_mag_filter(blender_shader_node, export_settings):
if blender_shader_node.interpolation == 'Closest':
return TextureFilter.Nearest
return TextureFilter.Linear
def __gather_min_filter(blender_shader_node, export_settings):
if blender_shader_node.interpolation == 'Closest':
return TextureFilter.NearestMipmapNearest
return TextureFilter.LinearMipmapLinear
def __gather_name(blender_shader_node, export_settings):
return None
def __gather_wrap(blender_shader_node, export_settings):
# First gather from the Texture node
if blender_shader_node.extension == 'EXTEND':
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
|