16
16
# see VTFLibWrapper/README.md for links to Linux version
17
17
18
18
19
- __version__ = "1.0 .0"
19
+ __version__ = "1.1 .0"
20
20
# NOTE: cannot be bothered to convert .vmt proxies
21
21
# TODO: Titanfall 2 rpak .json materials -> .shader
22
22
23
23
24
24
patterns = {"alphatest" : re .compile (r'\s"\$alphatest"\s1' ),
25
25
"basetexture" : re .compile (r'\s"\$basetexture"\s"(.*)"' ),
26
- "tooltexture" : re .compile (r'\s%tooltexture\s"(.*)"' )}
26
+ "tooltexture" : re .compile (r'\s%tooltexture\s"(.*)"' ),
27
+ "translucent" : re .compile (r'\s"\$translucent"\s"1"' )}
27
28
28
29
29
30
class VMT :
@@ -36,14 +37,18 @@ def from_file(cls, filename: str) -> VMT:
36
37
out = cls ()
37
38
with open (filename ) as vmt_file :
38
39
for line in vmt_file :
39
- if re .match (patterns ["alphatest" ], line ) is not None :
40
- out .is_trans = True
41
- match = re .match (patterns ["basetexture" ], line )
42
- if match is not None :
43
- out .basetexture = match .groups ()[0 ]
44
- match = re .match (patterns ["tooltexture" ], line )
45
- if match is not None :
46
- out .tooltexture = match .groups ()[0 ]
40
+ for check , pattern in patterns .items ():
41
+ match = re .match (pattern , line )
42
+ if match is None :
43
+ continue
44
+ if check in ("alphatest" , "translucent" ):
45
+ out .is_trans = True
46
+ elif check == "basetexture" :
47
+ out .basetexture = match .groups ()[0 ]
48
+ elif check == "tooltexture" :
49
+ out .tooltexture = match .groups ()[0 ]
50
+ else :
51
+ raise NotImplementedError (f"i forgor { check } " )
47
52
return out
48
53
49
54
@@ -60,17 +65,15 @@ def filename(folder: str, base: str, ext: str) -> str:
60
65
return os .path .join (folder , f"{ base } .{ ext } " )
61
66
62
67
63
- # TODO: textures -> shader_names for TrenchBroom
64
- def convert_folder (materials_dir : str , MRVN_dir : str , MRVN_game : str ,
65
- recursive = False , subfolder : str = "" , verbose = False ):
66
- # e.g. "../titanfall1_extracted_textures", "C:/MRVN-radiant", "titanfallonline"
67
- materials_dir = os .path .realpath (materials_dir )
68
- MRVN_dir = os .path .realpath (MRVN_dir )
69
- scripts_dir = os .path .join (MRVN_dir , MRVN_game , "scripts" )
70
- textures_dir = os .path .join (MRVN_dir , MRVN_game , "textures" )
71
- # NOTE: just directly grab all the world textures
72
- top_folder = os .path .join (materials_dir , "world" , subfolder )
73
- for folder in os .listdir (os .path .join (materials_dir , "world" , subfolder )):
68
+ # MRVN-radiant
69
+ def convert_folder (materials_dir : str , titanfall_dir : str , subfolder : str = "" , recurse = False , verbose = False ):
70
+ # e.g. "../titanfall1_extracted_textures", "C:/MRVN-radiant/titanfallonline"
71
+ materials_dir = os .path .realpath (materials_dir ) # src
72
+ titanfall_dir = os .path .realpath (titanfall_dir ) # dest
73
+ scripts_dir = os .path .join (titanfall_dir , "scripts" )
74
+ textures_dir = os .path .join (titanfall_dir , "textures" )
75
+ top_folder = os .path .join (materials_dir , subfolder )
76
+ for folder in os .listdir (top_folder ):
74
77
vmt_folder = os .path .join (top_folder , folder )
75
78
if not os .path .isdir (vmt_folder ):
76
79
continue
@@ -87,17 +90,15 @@ def convert_folder(materials_dir: str, MRVN_dir: str, MRVN_game: str,
87
90
if verbose :
88
91
print (f"!!! { vmt_filename } has no texture !!!" )
89
92
continue
90
- shader_name = os .path .join ("textures" , "world" , subfolder , folder , vmt_filename [:- 4 ])
93
+ shader_name = os .path .join ("textures" , subfolder , folder , vmt_filename [:- 4 ])
91
94
shader_file .write (f"\n { shader_name } \n " + "{\n " )
92
- # shader_file.write(f"\tqer_editorimage {shader_name}.tga\n")
93
95
texture = vmt .basetexture if vmt .tooltexture is None else vmt .tooltexture
94
96
shader_file .write (f"\t qer_editorimage { os .path .join ('textures' , texture )} .tga\n " )
95
97
# TODO: convert as much of the shader as possible
96
98
if vmt .is_trans :
97
- # NOTE: should really target basetexture, but we rename it later
98
- shader_file .write ("\t {\n \t \t map " + os .path .join ("textures" , shader_name ) + ".tga\n " )
99
+ shader_file .write ("\t qer_trans 1.00\n " ) # use texture alpha
99
100
# shader_file.write("\t{\n\t\tmap " + os.path.join("textures", vmt.basetexture) + ".tga\n")
100
- shader_file .write ("\t \t blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA\n \t }\n " )
101
+ # shader_file.write("\t\tblendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA\n\t}\n")
101
102
shader_file .write ("}\n " )
102
103
# .vtf
103
104
vtf_filename = filename (materials_dir , texture , "vtf" )
@@ -117,24 +118,62 @@ def convert_folder(materials_dir: str, MRVN_dir: str, MRVN_game: str,
117
118
print (f"--- { count } materials converted" )
118
119
if count == 0 :
119
120
os .remove (os .path .realpath (shader_file .name ))
120
- if recursive :
121
- convert_folder (materials_dir , MRVN_dir , MRVN_game , True , os .path .join (subfolder , folder ), verbose = verbose )
121
+ if recurse :
122
+ convert_folder (materials_dir , titanfall_dir , os .path .join (subfolder , folder ), True , verbose = verbose )
123
+
124
+
125
+ # TrenchBroom
126
+ def convert_folder_trenchbroom (materials_dir : str , tga_dir : str , subfolder : str = "" , recurse = False , verbose = False ):
127
+ # e.g. "../titanfall1_extracted_textures", ".../TrenchBroom maps/Titanfall/textures"
128
+ # NOTE: subfolder should be a subfolder of materials
129
+ # NOTE: no .shader; straight to 1 .tga per material name
130
+ materials_dir = os .path .realpath (materials_dir ) # src
131
+ tga_dir = os .path .realpath (tga_dir ) # dest
132
+ top_folder = os .path .join (materials_dir , subfolder )
133
+ for folder in os .listdir (top_folder ):
134
+ vmt_folder = os .path .join (top_folder , folder )
135
+ if not os .path .isdir (vmt_folder ):
136
+ continue
137
+ count = 0
138
+ for vmt_filename in fnmatch .filter (os .listdir (vmt_folder ), "*.vmt" ):
139
+ # .vmt
140
+ vmt = VMT .from_file (os .path .join (vmt_folder , vmt_filename ))
141
+ if vmt .basetexture == "" and vmt .tooltexture is None :
142
+ if verbose :
143
+ print (f"!!! { vmt_filename } has no texture !!!" )
144
+ continue
145
+ shader_name = os .path .join ("textures" , "world" , subfolder , folder , vmt_filename [:- 4 ])
146
+ texture = vmt .basetexture if vmt .tooltexture is None else vmt .tooltexture
147
+ # .vtf
148
+ vtf_filename = filename (materials_dir , texture , "vtf" )
149
+ if not os .path .exists (vtf_filename ):
150
+ if verbose :
151
+ print (f"!!! { vmt_filename } texture { texture } not found !!!" )
152
+ continue
153
+ vtf_to_tga (vtf_filename , filename (tga_dir , shader_name , "tga" ))
154
+ count += 1
155
+ if verbose :
156
+ print (f"--- { count } materials converted" )
157
+ if recurse :
158
+ convert_folder_trenchbroom (materials_dir , tga_dir , True , os .path .join (subfolder , folder ), verbose = verbose )
122
159
123
160
124
161
if __name__ == "__main__" :
125
162
import sys
126
163
127
164
# materials_dir = "E:/Mod/TitanfallOnline/TitanFallOnline/assets_dump/materials"
128
- # MRVN_dir = "E:/Mod/_tools/Source Engine - Respawn/MRVN-radiant-3db242a-Windows-x86_64"
165
+ # titanfall_dir = "E:/Mod/_tools/Source Engine - Respawn/MRVN-radiant-3db242a-Windows-x86_64/titanfallonline "
129
166
if len (sys .argv ) == 3 :
130
- materials_dir , MRVN_dir = sys .argv [1 :]
167
+ materials_dir , titanfall_dir = sys .argv [1 :]
131
168
# elif len(sys.argv) == 1:
132
169
# pass # use defaults
133
170
else :
134
- print (f'Usage: { sys .argv [0 ]} "Titanfall materials dir" "MRVN-radiant install dir"' )
171
+ print (f'Usage: { sys .argv [0 ]} "Titanfall materials dir" "MRVN-radiant working titanfall dir"' )
135
172
print ('\t if you can\' t find your "Titanfall/materials" folder, extract it from the .vpks' )
136
173
sys .exit ()
137
174
138
175
sys .stdout .reconfigure (line_buffering = True ) # for piping print to logfile
139
- convert_folder (materials_dir , MRVN_dir , "titanfallonline" , True , verbose = True )
176
+ convert_folder (materials_dir , titanfall_dir , subfolder = "dev" , recurse = True , verbose = True )
177
+ convert_folder (materials_dir , titanfall_dir , subfolder = "world" , recurse = True , verbose = True )
140
178
# TODO: concatenate .shader files together into a top-level .shader for each world/ folder
179
+ # TODO: generate shadertags .xml for easier navigation
0 commit comments