1 # SPDX-FileCopyrightText: 2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
9 def split_into_components(fname
):
11 Split filename into components
12 'WallTexture_diff_2k.002.jpg' -> ['Wall', 'Texture', 'diff', 'k']
15 fname
= path
.splitext(fname
)[0]
17 fname
= "".join(i
for i
in fname
if not i
.isdigit())
18 # Separate CamelCase by space
19 fname
= re
.sub(r
"([a-z])([A-Z])", r
"\g<1> \g<2>", fname
)
20 # Replace common separators with SPACE
21 separators
= ["_", ".", "-", "__", "--", "#"]
22 for sep
in separators
:
23 fname
= fname
.replace(sep
, " ")
25 components
= fname
.split(" ")
26 components
= [c
.lower() for c
in components
]
30 def remove_common_prefix(names_to_tag_lists
):
32 Accepts a mapping of file names to tag lists that should be used for socket
35 This function modifies the provided mapping so that any common prefix
36 between all the tag lists is removed.
38 Returns true if some prefix was removed, false otherwise.
40 if not names_to_tag_lists
:
42 sample_tags
= next(iter(names_to_tag_lists
.values()))
46 common_prefix
= sample_tags
[0]
47 for tag_list
in names_to_tag_lists
.values():
48 if tag_list
[0] != common_prefix
:
51 for name
, tag_list
in names_to_tag_lists
.items():
52 names_to_tag_lists
[name
] = tag_list
[1:]
56 def remove_common_suffix(names_to_tag_lists
):
58 Accepts a mapping of file names to tag lists that should be used for socket
61 This function modifies the provided mapping so that any common suffix
62 between all the tag lists is removed.
64 Returns true if some suffix was removed, false otherwise.
66 if not names_to_tag_lists
:
68 sample_tags
= next(iter(names_to_tag_lists
.values()))
72 common_suffix
= sample_tags
[-1]
73 for tag_list
in names_to_tag_lists
.values():
74 if tag_list
[-1] != common_suffix
:
77 for name
, tag_list
in names_to_tag_lists
.items():
78 names_to_tag_lists
[name
] = tag_list
[:-1]
82 def files_to_clean_file_names_for_sockets(files
, sockets
):
84 Accepts a list of files and a list of sockets.
86 Returns a mapping from file names to tag lists that should be used for
89 A file is something that we can do x.name on to figure out the file name.
91 A socket is a tuple containing:
94 * a None field where the selected file name will go later. Ignored by us.
97 names_to_tag_lists
= {}
99 names_to_tag_lists
[file.name
] = split_into_components(file.name
)
102 for socket
in sockets
:
103 socket_tags
= socket
[1]
104 all_tags
.update(socket_tags
)
106 while len(names_to_tag_lists
) > 1:
107 something_changed
= False
109 # Common prefixes / suffixes provide zero information about what file
110 # should go to which socket, but they can confuse the mapping. So we get
112 something_changed |
= remove_common_prefix(names_to_tag_lists
)
113 something_changed |
= remove_common_suffix(names_to_tag_lists
)
115 # Names matching zero tags provide no value, remove those
116 names_to_remove
= set()
117 for name
, tag_list
in names_to_tag_lists
.items():
124 names_to_remove
.add(name
)
126 for name_to_remove
in names_to_remove
:
127 del names_to_tag_lists
[name_to_remove
]
128 something_changed
= True
130 if not something_changed
:
133 return names_to_tag_lists
136 def match_files_to_socket_names(files
, sockets
):
138 Given a list of files and a list of sockets, match file names to sockets.
140 A file is something that you can get a file name out of using x.name.
142 After this function returns, all possible sockets have had their file names
143 filled in. Sockets without any matches will not get their file names
146 Sockets list format. Note that all file names are initially expected to be
147 None. Tags are strings, as are the socket names: [
149 socket_name, [tags], Optional[file_name]
154 names_to_tag_lists
= files_to_clean_file_names_for_sockets(files
, sockets
)
156 for sname
in sockets
:
157 for name
, tag_list
in names_to_tag_lists
.items():
158 if sname
[0] == "Normal":
159 # Blender wants GL normals, not DX (DirectX) ones:
160 # https://www.reddit.com/r/blender/comments/rbuaua/texture_contains_normaldx_and_normalgl_files/
163 if 'directx' in tag_list
:
166 matches
= set(sname
[1]).intersection(set(tag_list
))