-
Notifications
You must be signed in to change notification settings - Fork 0
/
batch_vmt.py
153 lines (129 loc) · 6.33 KB
/
batch_vmt.py
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
# batch_vmt (c) by Bikkie / snake-biscuits [b!scuit#3659]
#
# batch_vmt is licensed under a
# Creative Commons Attribution-ShareAlike 4.0 International License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-sa/4.0/>.
"""generate .vmt files from a folder of .vtf files"""
from __future__ import annotations
import fnmatch
import os
from typing import Dict, List
from colour import Color
from gooey import Gooey, GooeyParser
# import VTFLibWrapper
__version__ = "1.0.0"
def from_template(vtf_filename: str, template: str, substitutions: Dict[str, str] = dict()):
"""Generate .vmts from `template` for every .vtf in `folder`"""
for keyword, replacement in substitutions.items():
template = template.replace(f"<{keyword}>", replacement)
filename = os.path.splitext(vtf_filename)[0]
with open(f"{filename}.vmt", "w") as vmt_file:
vmt_file.write(template.replace("<filename>", filename))
# TODO: maybe separate file filtering from .vmt writing?
def from_metadata(vtf_filename: str, shader: str = "LightmappedGeneric",
colour=Color("White"), hue_range: float = 0,
defaults={"colour": ("%keywords", "white")}):
"""generate an appropriate .vmt from .vmt flags"""
raise NotImplementedError()
filename = os.path.splitext(vtf_filename)[0]
vtf = ... # TODO: load f"{filename}.vtf" with VTFLibWrapper
# check flags
check: Dict[str, bool]
check = {"colour": fuzzy_colour_match(vtf.reflectivity, colour, hue_range),
"has_alpha": has_alpha(vtf)}
# ^ {"flag": True or False}
# compose the .vmt text
lines = [shader, "{"]
for condition in check:
parameter, value = defaults[condition]
if check[condition] is True:
value = value.replace("<filename>", filename)
lines.append('\t"{paramater}" "{value}"')
lines.append("}")
# write to file
with open(f"{filename}.vmt", "w") as vmt_file:
vmt_file.write("\n".join(lines))
# check functions for from_metadata
def fuzzy_colour_match(a: Color, b: Color, hue_range: float) -> bool:
# TODO: accept a hue_range or rgb_range 3-ple
return abs(a.hsl[0] - b.hsl[0]) <= hue_range
def has_alpha(vtf) -> bool:
raise NotImplementedError()
return "A" in vtf.image_format.name # NOTE: untested
# TODO: split for both modes? or is it still easier to process folder recursion here?
def parse_folder(method: str, folders: List[str], template=None, **kwargs):
# pre-processing
if method == "template":
# kwargs["substutions"]: Dict[str, str] = {"keyword": "replacement"}
template = open(template).read()
# NOTE: the "template" kwarg must be supplied! [process_folder(..., template="base.vmt")]
# template substitutions
for keyword, replacement in kwargs.pop("substitutions", dict()).items():
template = template.replace(f"<{keyword}>", replacement)
elif method == "metadata":
# TODO: figure out what can be pre-processes for metadata batches
from_metadata(method) # NotImplemented!
else:
raise RuntimeError("Invalid method: '{method}', only 'template' & 'metadata' are accepted")
# parse all folders
for folder in folders:
folder_contents = [os.path.join(f) for f in os.listdir(folder)]
folders.extend([d for d in folder_contents if os.path.isdir(d)])
# NOTE: this isn't recursing folders like I thought it would...
for vtf_filename in fnmatch.filter(folder_contents, "*.vtf"):
filename = os.path.join(folder, os.path.splitext(vtf_filename)[0])
# process file
print(f"Writing {filename}.vmt... ", end="")
if method == "template":
from_template(filename, template)
elif method == "metadata":
# TODO: do a keyword substitution pass on flags.values
from_metadata(filename, **kwargs)
print("Done!")
@Gooey
def main():
# NOTES:
# --template (default: base.vmt next to batch_vmt.py must have a <filename> keyword!
# --replace KEYWORD:REPLACEMENT will replace any keyword
# However, if no replacement is given, lines with <keyword> will remain and break .vmt
# Also, You should find a displacement_base.vmt included with batch_vmt.py
# --ignore .*_a .*_bump .*_bm .*_editor should be used when generating displacements
parser = GooeyParser(description=__doc__)
parser.add_argument("filenames", nargs="*", metavar="FILE", widget="MultiFileChooser", default="tests/materials",
help="filename(s) / folder(s) to generate .vmts for\nFolders must be typed in manually")
parser.add_argument("-t", "--template", default="base.vmt", widget="FileChooser",
help="generate vmts from the supplied template\ndefault: base.vmt")
# subparsers = parser.add_subparsers(title="mode", description="mode to run in")
# template_mode = subparsers.add_parser("template")
parser.add_argument("-s", "--substitute", nargs="*", default="tags:generated",
help="substitute <keyword> in template with replacement\n(e.g. `bumpmap:<filename>_bump`)")
# ...
# generate_mode = subparsers.add_parser("generate")
# TODO: --surfaceprop choice to add a surfaceprop
# TODO: --hsv_range H:S:V
# TODO: --rgb_range R:G:B
# TODO: --colour "White":"$parameter":"<keyword>"
# ...
args = parser.parse_args()
folders = list(filter(os.path.isdir, args.filenames))
filenames = set(args.filenames).difference(set(folders))
# TODO: handle any wildcards in filenames
# TODO: handle both modes here
# if args.generate:
# process_folder("metadata", folders, ...)
# for filename in filenames:
# from_metadata(filename, ...)
# else: # template mode
replacements = dict()
for kr in args.substitute:
k, r = kr.split(":")
replacements[k] = r
# replacements are colon separated; < & > around the keyword are optional
# setting <filename> will give all make all .vmts generated identical! even the basetexture!
parse_folder("template", folders, template=args.template, substitutions=replacements)
for filename in filenames:
from_template(filename, args.template, substitutions=replacements)
if __name__ == "__main__":
main() # run the Gooey UI