Scale the map after the lightmap has been rendered
[xonotic/xonotic.git] / misc / tools / xonotic-map-compiler
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use POSIX;
6 use File::Temp;
7
8 # change these to match your system, or define them in ~/.xonotic-map-compiler
9 # (just copy paste this part to the file ~/.xonotic-map-compiler)
10
11         # Path to Xonotic (where the data directory is in)
12         our $XONOTICDIR  = getcwd();
13
14         # Path to your q3map2 program. You find it in your GtkRadiant/install
15         # directory.
16         our $Q3MAP2      = getcwd() . '/netradiant/install/q3map2';
17
18         # General flags for q3map2 (for example -threads 4)
19         our $Q3MAP2FLAGS = '-fs_forbiddenpath xonotic*-data*.pk3* -fs_forbiddenpath xonotic*-nexcompat*.pk3*';
20
21         # Default flags for the -bsp stage
22         our $BSPFLAGS    = '-meta -maxarea -samplesize 8 -mv 1000000 -mi 6000000';
23
24         # Default flags for the -vis stage
25         our $VISFLAGS    = '';
26
27         # Default flags for the -light stage
28         our $LIGHTFLAGS  = '-lightmapsize 1024 -lightmapsearchpower 4 -deluxe -patchshadows -randomsamples -samples 4 -fast -fastbounce -dirty -bouncegrid -fill';
29
30         # Default flags for the -minimap stage
31         our $MINIMAPFLAGS = '';
32
33         # Default order of commands
34         our $ORDER = 'vis,light,scale';
35
36 # end of user changable part
37
38 do "$ENV{HOME}/.xonotic-map-compiler";
39
40 sub Usage()
41 {
42         print <<EOF;
43 Usage:
44 $0 mapname [-bsp bspflags...] [-vis visflags...] [-light lightflags...] [-minimap minimapflags]
45 EOF
46         exit 1;
47 }
48
49 my $options =
50 {
51         bsp => [split /\s+/, $BSPFLAGS],
52         vis => [split /\s+/, $VISFLAGS],
53         light => [split /\s+/, $LIGHTFLAGS],
54         minimap => [split /\s+/, $MINIMAPFLAGS],
55         scale => [], # can't have defaults atm
56         order => [split /\s*,\s*/, $ORDER],
57         maps => [],
58         scalefactor => 1,
59         bsp_timeout => 0,
60         vis_timeout => 0,
61         light_timeout => 0,
62         minimap_timeout => 0,
63         scale_timeout => 0,
64         timeout_stealing => 0,
65 };
66
67 my $curmode = 'maps';
68
69 while(@ARGV)
70 {
71         $_ = shift @ARGV;
72         my $enterflags = undef;
73         if($_ eq '-bsp')
74         {
75                 $enterflags = 'bsp';
76         }
77         elsif($_ eq '-vis')
78         {
79                 $enterflags = 'vis';
80         }
81         elsif($_ eq '-light')
82         {
83                 $enterflags = 'light';
84         }
85         elsif($_ eq '-minimap')
86         {
87                 $enterflags = 'minimap';
88         }
89         elsif($_ eq '-map')
90         {
91                 $curmode = 'maps';
92         }
93         elsif($_ eq '-scale')
94         {
95                 $options->{scalefactor} = @ARGV ? shift(@ARGV) : 1;
96                 $enterflags = 'scale';
97         }
98         elsif($_ eq '-novis')
99         {
100                 $options->{vis} = undef;
101         }
102         elsif($_ eq '-nolight')
103         {
104                 $options->{light} = undef;
105         }
106         elsif($_ eq '-nominimap')
107         {
108                 $options->{minimap} = undef;
109         }
110         elsif($_ eq '-bsp_timeout')
111         {
112                 $options->{bsp_timeout} = shift @ARGV;
113         }
114         elsif($_ eq '-vis_timeout')
115         {
116                 $options->{vis_timeout} = shift @ARGV;
117         }
118         elsif($_ eq '-light_timeout')
119         {
120                 $options->{light_timeout} = shift @ARGV;
121         }
122         elsif($_ eq '-minimap_timeout')
123         {
124                 $options->{minimap_timeout} = shift @ARGV;
125         }
126         elsif($_ eq '-scale_timeout')
127         {
128                 $options->{scale_timeout} = shift @ARGV;
129         }
130         elsif($_ eq '-timeout_stealing')
131         {
132                 $options->{timeout_stealing} = shift @ARGV;
133         }
134         elsif($_ eq '-order')
135         {
136                 $options->{order} = [split /\s*,\s*/, shift @ARGV];
137         }
138         elsif($_ eq '-sRGB')
139         {
140                 push @{$options->{bsp}}, "-sRGBtex", "-sRGBcolor";
141                 push @{$options->{light}}, "-sRGBtex", "-sRGBcolor", "-sRGBlight"
142                         if defined $options->{light};
143         }
144         elsif($_ eq '-nosRGB')
145         {
146                 push @{$options->{bsp}}, "-nosRGBtex", "-nosRGBcolor";
147                 push @{$options->{light}}, "-nosRGBtex", "-nosRGBcolor", "-nosRGBlight"
148                         if defined $options->{light};
149         }
150         elsif($_ =~ /^--no(-.*)/)
151         {
152                 if($curmode eq 'maps')
153                 {
154                         $curmode = 'bsp';
155                 }
156                 my $flag = $1;
157                 @{$options->{$curmode}} = grep { (($_ eq $flag) ... /^-/) !~ /^[0-9]+$/ } @{$options->{$curmode}};
158                         # so, e.g. --no-samplesize removes "-samplesize" and a following "3"
159         }
160         elsif($_ =~ /^-(-.*)/)
161         {
162                 if($curmode eq 'maps')
163                 {
164                         $curmode = 'bsp';
165                 }
166                 push @{$options->{$curmode}}, $1;
167         }
168         elsif($_ =~ /^-/ and $curmode eq 'maps')
169         {
170                 $curmode = 'bsp';
171                 push @{$options->{$curmode}}, $_;
172         }
173         else
174         {
175                 push @{$options->{$curmode}}, $_;
176         }
177         if(defined $enterflags)
178         {
179                 $curmode = $enterflags;
180                 if($ARGV[0] eq '+')
181                 {
182                         shift @ARGV;
183                 }
184                 else
185                 {
186                         $options->{$curmode} = [];
187                 }
188         }
189 }
190
191 my $linkdir = File::Temp::tempdir("xonotic-map-compiler.XXXXXX", TMPDIR => 1, CLEANUP => 1);
192 my $starttime = time;
193 my $endtime = time;
194
195 sub q3map2(@)
196 {
197         my $mode = $_[0];
198         my $timeout = undef;
199         $timeout = $options->{bsp_timeout} if $mode eq '-bsp';
200         $timeout = $options->{vis_timeout} if $mode eq '-vis';
201         $timeout = $options->{light_timeout} if $mode eq '-light';
202         $timeout = $options->{minimap_timeout} if $mode eq '-minimap';
203         $timeout = $options->{scale_timeout} if $mode eq '-scale';
204         die "Invalid call: not a standard q3map2 stage" if not defined $timeout;
205         $endtime += $timeout;
206         my $stolen_timeout = $endtime - time;
207         if ($stolen_timeout > $timeout)
208         {
209                 $timeout += ($stolen_timeout - $timeout) * $options->{timeout_stealing};
210         }
211         my @args = ($Q3MAP2, split(/\s+/, $Q3MAP2FLAGS), '-game', 'xonotic', '-fs_basepath', $XONOTICDIR, '-fs_basepath', $linkdir, '-v', @_);
212         print "\$ @args\n";
213         print "Using timeout: $timeout\n";
214         defined(my $pid = fork())
215                 or die "fork: $!";
216         if($pid) # parent
217         {
218                 local $SIG{ALRM} = sub { warn "SIGALRM caught\n"; kill TERM => $pid; };
219                 alarm $timeout
220                         if $timeout;
221                 if(waitpid($pid, 0) != $pid)
222                 {
223                         die "waitpid: did not return our child process $pid: $!";
224                 }
225                 alarm 0;
226                 return ($? == 0);
227         }
228         else # child
229         {
230                 exec @args
231                         or die "exec: $!";
232         }
233 }
234
235 if ($options->{scalefactor} =~ /^([0-9.]+):([0-9.]+)$/)
236 {
237         die "Two-scale isn't supported"
238                 if $1 != 1 and $2 != 1;
239         $options->{scalefactor} = $1
240                 if $2 == 1;
241         $options->{scalefactor} = $2
242                 if $1 == 1;
243 }
244
245 my $origcwd = getcwd();
246 for my $m(@{$options->{maps}})
247 {
248         chdir $origcwd
249                 or die "chdir $origcwd: $!";
250         if($m =~ s!(.*)/!!)
251         {
252                 my $predir = $1;
253                 chdir $predir
254                         or die "chdir $predir: $!";
255         }
256         symlink getcwd() . "/..", "$linkdir/data"
257                 or die "symlink $linkdir/data: $!";
258
259         $m =~ s/\.(?:map|bsp)$//;
260
261         # never hurts, may help with rtlights
262         unshift @{$options->{bsp}}, "-keeplights";
263
264         local $SIG{INT} = sub
265         {
266                 print "SIGINT caught, cleaning up...\n";
267                 exit 0;
268         };
269
270         unlink <$m/lm_*>; # delete old external lightmaps
271         q3map2 '-bsp', @{$options->{bsp}},   "$m.map"
272                 or die "-bsp: $?";
273         my @o = @{$options->{order}};
274         push @o, qw/vis scale light/;
275         my %o = ();
276
277         for(@o)
278         {
279                 next if $o{$_}++;
280                 if($_ eq 'light')
281                 {
282                         if(defined $options->{light})
283                         {
284                                 q3map2 '-light',        @{$options->{light}}, "$m.map"
285                                         or die "-light: $?";
286                         }
287                 }
288                 if($_ eq 'vis')
289                 {
290                         if(defined $options->{vis})
291                         {
292                                 q3map2 '-vis',          @{$options->{vis}},   "$m.map"
293                                         or die "-vis: $?";
294                         }
295                 }
296                 if($_ eq 'scale')
297                 {
298                         if ($options->{scalefactor} != 1)
299                         {
300                                 q3map2 '-scale', @{$options->{scale}}, $options->{scalefactor}, "$m.bsp"
301                                         or die "-scale: $?";
302                                 rename "${m}_s.bsp", "$m.bsp"
303                                         or die "rename ${m}_s.bsp $m.bsp: $!";
304                         }
305                 }
306         }
307
308         if(defined $options->{minimap})
309         {
310                 q3map2 '-minimap',      @{$options->{minimap}}, "$m.map"
311                         or die "-minimap: $?";
312         }
313
314         unlink "$m.srf";
315         unlink "$m.prt";
316 }