1+ import os
2+ import subprocess
3+ import re
4+ import binascii
5+ import struct
6+ from multiprocessing .pool import ThreadPool
7+
8+ # Set the path to the fxc compiler
9+ fxc = os .environ ['DXSDK_DIR' ] + 'Utilities\\ bin\\ x86\\ fxc.exe'
10+ featureLevel = '3_0'
11+ workers = 8
12+
13+ # Set the directory you want to start from
14+ shaders = 'gamedata/shaders/r2'
15+ objects = 'gamedata/shaders/r2/objects/r2'
16+
17+ # List all macro identifiers, excluding SMAP
18+ options = [
19+ 'FP16_FILTER' ,
20+ 'FP16_BLEND' ,
21+ 'USE_HWSMAP' ,
22+ 'USE_HWSMAP_PCF' ,
23+ 'USE_FETCH4' ,
24+ 'USE_SJITTER' ,
25+ 'USE_BRANCHING' ,
26+ 'USE_VTF' ,
27+ 'USE_TSHADOWS' ,
28+ 'USE_MBLUR' ,
29+ 'USE_SUNFILTER' ,
30+ 'USE_R2_STATIC_SUN' ,
31+ 'FORCE_GLOSS' ,
32+ 'SKIN_COLOR' ,
33+ 'USE_SSAO_BLUR' ,
34+ 'USE_HBAO' ,
35+ 'SSAO_OPT_DATA' ,
36+ 'SKIN_NONE' ,
37+ 'SKIN_0' ,
38+ 'SKIN_1' ,
39+ 'SKIN_2' ,
40+ 'SKIN_3' ,
41+ 'SKIN_4' ,
42+ 'USE_SOFT_WATER' ,
43+ 'USE_SOFT_PARTICLES' ,
44+ 'USE_DOF' ,
45+ 'SUN_SHAFTS_QUALITY' ,
46+ 'SSAO_QUALITY' ,
47+ 'SUN_QUALITY' ,
48+ 'ALLOW_STEEPPARALLAX'
49+ ]
50+
51+ skinOptions = [
52+ 'SKIN_NONE' ,
53+ 'SKIN_0' ,
54+ 'SKIN_1' ,
55+ 'SKIN_2' ,
56+ 'SKIN_3' ,
57+ 'SKIN_4'
58+ ]
59+
60+ forceOptions = [
61+ 'USE_HWSMAP'
62+ ]
63+
64+ disableOptions = [
65+ 'SKIN_COLOR'
66+ ]
67+
68+ qualityOptions = {
69+ 'SUN_SHAFTS_QUALITY' : 3 ,
70+ 'SSAO_QUALITY' : 3 ,
71+ 'SUN_QUALITY' : 2
72+ }
73+
74+ entrex = re .compile ('main_(vs|ps)_[0-9]_[0-9]' )
75+ inclex = re .compile ('#include\s+"(.*)"' )
76+ commex = re .compile ("//.*?\n " )
77+
78+ def addCRC (filename ):
79+ with open (filename ) as file :
80+ file = open (filename ,'r+b' )
81+ buf = file .read ()
82+ crc = (binascii .crc32 (buf ) & 0xFFFFFFFF )
83+ file .seek (0 )
84+ file .write (struct .pack ("<I" , crc ))
85+ file .write (buf )
86+
87+ def findOptions (shader ):
88+ list = []
89+ with open (shader ) as f :
90+ buffer = f .read ()
91+ buffer = re .sub (commex , "" , buffer ) # remove commented lines
92+ for option in options :
93+ if option in buffer :
94+ list .append (option )
95+ includes = inclex .findall (buffer ) # find all include files
96+ for include in includes :
97+ list += findOptions (os .path .join (os .path .dirname (shader ), include ))
98+ return list
99+
100+ def findEntry (shader ):
101+ with open (shader ) as f :
102+ buffer = f .read ()
103+ buffer = re .sub (commex , "" , buffer ) # remove commented lines
104+ match = entrex .search (buffer ) # find the entry point
105+ if match :
106+ return match .group (0 )
107+ if 'main' in buffer :
108+ return 'main'
109+ includes = inclex .findall (buffer ) # find all include files
110+ for include in includes :
111+ if include != 'common.h' :
112+ return findEntry (os .path .join (os .path .dirname (shader ), include ))
113+ return False
114+
115+ def generateIds (list , id , i ):
116+ if i >= len (options ):
117+ return [ id ]
118+ if options [i ] in list :
119+ if options [i ] in forceOptions :
120+ return generateIds (list , id + '1' , i + 1 )
121+ if options [i ] in disableOptions :
122+ return generateIds (list , id + '0' , i + 1 )
123+ if options [i ] in skinOptions :
124+ ids = []
125+ for (index , option ) in enumerate (skinOptions ):
126+ skin = '0' * len (skinOptions )
127+ skin = skin [:index ] + '1' + skin [index + 1 :]
128+ ids += generateIds (list , id + skin , i + len (skinOptions ))
129+ return ids
130+ if options [i ] in qualityOptions :
131+ ids = []
132+ for index in range (0 , qualityOptions [options [i ]]):
133+ ids += generateIds (list , id + str (index ), i + 1 )
134+ return ids
135+ return generateIds (list , id + '0' , i + 1 ) + generateIds (list , id + '1' , i + 1 )
136+ else :
137+ return generateIds (list , id + '_' , i + 1 )
138+
139+ def compileShader (name , stage , flags ):
140+ path = os .path .join (shaders , '%s.%s' % (name , stage ))
141+ entry = findEntry (path )
142+ if not entry :
143+ print ('No entry point found, skipping shader...' )
144+ return
145+
146+ profile = '%s_%s' % (stage , featureLevel )
147+ if len (entry ) > len ('main' ):
148+ profile = entry [5 :]
149+
150+ object = os .path .join (objects , '%s.%s' % (name , stage ))
151+ if os .path .isfile (object ):
152+ print ('Already compiled, skipping object %s' % flags )
153+ return
154+
155+ args = [fxc , '/nologo' , '/LD' , '/Zpr' , '/E%s' % entry , '/T%s' % profile ]
156+
157+ args .append ('/D%s=%s' % ('SMAP_size' , flags [0 :4 ]))
158+ for idx , option in enumerate (options , 4 ):
159+ if flags [idx ] != '_' and int (flags [idx ]) != 0 :
160+ args .append ('/D%s=%s' % (option , flags [idx ]))
161+ if option in skinOptions and option != 'SKIN_NONE' :
162+ object = os .path .join (objects , '%s_%s.%s' % (name , option [- 1 ], stage ))
163+ args .append ('/Fo%s' % os .path .join (object , flags ))
164+ args .append (path )
165+ subprocess .check_call (args )
166+ addCRC (os .path .join (object , flags ))
167+
168+ pool = ThreadPool (processes = workers )
169+
170+ jobs = []
171+ for file in os .listdir (shaders ):
172+ shader = os .path .splitext (file )[0 ]
173+ stage = os .path .splitext (file )[1 ][1 :]
174+ if stage == 'vs' or stage == 'ps' :
175+ print ('Found shader: %s' % shader )
176+ flags = findOptions (os .path .join (shaders , file ))
177+ outdir = os .path .join (objects , file )
178+ if not os .path .isdir (outdir ):
179+ os .makedirs (outdir )
180+ if 'SKIN_NONE' in flags :
181+ for skin in skinOptions [1 :]:
182+ outdir = os .path .join (objects , '%s_%s.%s' % (shader , skin [- 1 ], stage ))
183+ if (not os .path .isdir (outdir )):
184+ os .makedirs (outdir )
185+ print (flags )
186+ for id in generateIds (flags , "2048" , 0 ):
187+ job = pool .apply_async (compileShader , (shader , stage , id ))
188+ jobs .append (job )
189+ for job in jobs :
190+ job .get ()
191+
192+ pool .close ()
193+ pool .join ()
0 commit comments