1 #!/usr/bin/perl |
1 #!/usr/bin/perl |
2 # |
2 # |
3 # Make a nice view of a luminaire |
3 # Make an interactive preview of a luminaire |
4 # Argument is the Radiance description of a luminaire, incl. distribution |
|
5 # |
4 # |
6 # This script is based on Radiance's objview.pl plus |
5 # This script is based on Radiance's objview.pl plus |
7 # Rob Guglielmetti's ltview extension to his objview.rb |
6 # Rob Guglielmetti's ltview extension to his objview.rb |
8 # |
7 # |
9 # Axel Jacobs, 25 Dec 2013 |
8 # Written by Axel Jacobs <axel@jaloxa.eu> |
10 |
9 |
11 use strict; |
10 use strict; |
12 use warnings; |
11 use warnings; |
|
12 use Math::Trig; |
|
13 use File::Copy; |
13 use File::Temp qw/ tempdir /; |
14 use File::Temp qw/ tempdir /; |
14 |
15 |
15 my $td = tempdir( CLEANUP => 1 ); |
16 my $td = tempdir( CLEANUP => 1 ); |
16 my $octree = "$td/lt$$.oct"; |
17 my $oct = "$td/ltview.oct"; |
17 my $room = "$td/rm$$.rad"; |
18 my $room = "$td/room.rad"; |
18 my $rif = "$td/lt$$.rif"; |
19 my $rdim; # Overall room dimensions |
19 my $lumi = "$td/lt$$.rad"; |
20 my $rif = "$td/ltview.rif"; |
20 my $ambf = "$td/af$$.amb"; |
21 my $lumi = "$td/lumi.rad"; |
21 my $raddev = "x11"; # default output device. Overwrite with -o |
22 my $ies = "$td/lumi.ies"; |
22 my $up = "Z"; |
23 my $raddev = "x11"; # default output device. Overwrite with -o |
23 my $vw = "XYZ"; # default view. See 'man rad' for details |
24 my $is_ies = 0; # input file is IES photometry, not a Radiance luminaire |
24 my $bubble = 0; # Box or bubble? 0..box, 1..bubble |
|
25 my $TINY = 0.01; |
|
26 |
25 |
27 my $opts = ""; # Options common to rad and glrad |
26 my $maxscale = 1; # Maximum luminiare dimension after scaling |
28 my $rendopts = "-ab 1 -ds .15 -av 0 0 0"; # For render= line in rif file |
27 my $opts = ""; # Options common to rad and glrad |
29 my $usegl = 0; # Run glrad instead of rad (Boolean). |
28 my $render = "-ab 1 -ds .15 -av 0 0 0"; # render= line in rif file |
30 my $radopt = 0; # An option specific to rad was passed (Boolean). |
29 my $radopt = 0; # An option specific to rad was passed (Boolean). |
31 my $glradopt = 0; # An option specific to glrad was passed (Boolean). |
|
32 |
30 |
33 while (@ARGV) { |
31 while (@ARGV) { |
34 $_ = $ARGV[0]; |
32 $_ = $ARGV[0]; |
35 if (m/-g/) { # OpenGL output |
33 if (m/-i/) { |
36 if ( $^O =~ /MSWin32/ ) { |
34 $is_ies = 1; |
37 # No meaningful error messages under Windows. Just ignore... |
|
38 #die("OpenGL view is not available under Windows.\n"); |
|
39 } else { |
|
40 $usegl = 1; |
|
41 } |
|
42 } elsif (m/-u/) { # up direction |
|
43 $up = $ARGV[1]; |
|
44 shift @ARGV; |
|
45 } elsif ((m/-s/) or (m/-w/)) { # silent, no warnings |
|
46 $opts .= " $_"; |
|
47 } elsif (m/-b/) { # back face visibility |
|
48 $rendopts .= ' -bv'; |
|
49 } elsif (m/-v/) { # standard view "[Xx]?[Yy]?[Zz]?[vlcahs]?" |
|
50 # Prepend view with '+' for custom view away from the luminaire, not at it. |
|
51 # This is not defined by rad's default views. |
|
52 $vw = $ARGV[1]; |
|
53 shift @ARGV; |
|
54 } elsif (m/-N/) { # No. of parallel processes |
|
55 if ( $^O =~ /MSWin32/ ) { |
|
56 # Silently ignore this under Windoze |
|
57 #die("Multi-processor support is not available under Windows.\n"); |
|
58 } else { |
|
59 $opts .= ' -N ' . $ARGV[1]; |
|
60 } |
|
61 $radopt = 1; |
|
62 shift @ARGV; |
|
63 } elsif (m/-o/) { # output device (rvu -devices) |
35 } elsif (m/-o/) { # output device (rvu -devices) |
64 $raddev = $ARGV[1]; |
36 $raddev = $ARGV[1]; |
65 $radopt = 1; |
37 $radopt = 1; |
66 shift @ARGV; |
38 shift @ARGV; |
67 } elsif ((m/-V/) or (m/-e/)) { # print view, explicate variables |
39 } elsif (m/-r/) { |
68 # Think of those two as '-verbose'. |
40 $rdim = $ARGV[1]; # Room dimensions |
69 $opts .= " $_"; |
41 shift @ARGV; |
70 $radopt = 1; |
|
71 } elsif (m/-S/) { # full-screen stereo |
|
72 $opts .= " $_"; |
|
73 $glradopt = 1; |
|
74 } elsif (m/-r/) { # spherical room rather than box |
|
75 $bubble = 1; |
|
76 } elsif (m/^-\w/) { |
42 } elsif (m/^-\w/) { |
77 die("objview: Bad option: $_\n"); |
43 die("objview: Bad option: $_\n"); |
78 } else { |
44 } else { |
79 last; |
45 last; |
80 } |
46 } |
81 shift @ARGV; |
47 shift @ARGV; |
82 } |
48 } |
83 |
49 |
84 # We need exactly one input file: a Radiance luminaires description |
50 # We need one Radiance luminaires description, or an IES file |
85 #TODO: this could be expanded to handle Radiance .dat files and IES photometry files... |
|
86 if (! $#ARGV == 0) { |
51 if (! $#ARGV == 0) { |
87 die("ltview: Need one Radiance luminaire.\n"); |
52 die("ltview: Need one Radiance luminaire or IES file.\n"); |
88 } |
|
89 |
|
90 # Make sure we don't confuse glrad and rad options. |
|
91 if ($usegl) { |
|
92 if ($radopt) { |
|
93 die("objview: glrad output requested, but rad option passed.\n"); |
|
94 } |
|
95 } else { |
|
96 if ($glradopt) { |
|
97 die("objview: rad output requested, but glrad option passed.\n"); |
|
98 } |
|
99 } |
53 } |
100 |
54 |
101 open(FH, ">$room") or |
55 open(FH, ">$room") or |
102 die("ltview: Can't write to temporary file $room\n"); |
56 die("ltview: Can't write to temporary file $room\n"); |
103 print FH "void plastic wall_mat 0 0 5 .2 .2 .2 0 0\n"; |
57 print FH "void plastic wall_mat 0 0 5 .2 .2 .2 0 0\n"; |
104 |
58 |
105 if ($bubble == 1) { |
59 my $r; |
106 # Room is a bubble, not a box |
60 if (defined $rdim) { |
107 my $radius = sprintf "%.6f", 1.1*5 * 3**(1/3); |
61 # Room dimensions are giving explicitly. Don't touch the fitting. |
108 print FH "wall_mat sphere orb 0 0 4 0 0 0 $radius\n"; |
62 $r = $rdim; |
|
63 |
|
64 copy ($ARGV[0], $lumi); |
109 } else { |
65 } else { |
110 print FH <<EndOfRoom; |
66 # Scale fitting so it fits nicely into our default test room. |
111 # Don't rely on genbox here (named genrbox under Debian/Ubuntu) |
67 $r = 5; # Default room dimension |
112 wall_mat polygon box.1540 0 0 12 5 -5 -5 5 -5 5 -5 -5 5 -5 -5 -5 |
68 |
113 wall_mat polygon box.4620 0 0 12 -5 -5 5 -5 5 5 -5 5 -5 -5 -5 -5 |
69 # Work out how large the luminaire is and scale so that the longest |
114 wall_mat polygon box.2310 0 0 12 -5 5 -5 5 5 -5 5 -5 -5 -5 -5 -5 |
70 # axis-align dimension is $maxscale |
115 wall_mat polygon box.3267 0 0 12 5 5 -5 -5 5 -5 -5 5 5 5 5 5 |
71 my $dimstr = `getbbox -h $ARGV[0]`; |
116 wall_mat polygon box.5137 0 0 12 5 -5 5 5 -5 -5 5 5 -5 5 5 5 |
72 chomp $dimstr; |
117 wall_mat polygon box.6457 0 0 12 -5 5 5 -5 -5 5 5 -5 5 5 5 5 |
73 # Values returned by getbbox are indented and delimited with multiple spaces. |
|
74 $dimstr =~ s/^\s+//; # remove leading spaces |
|
75 my @dims = split(/\s+/, $dimstr); # convert to array |
|
76 |
|
77 # Find largest axes-aligned dimension |
|
78 my @diffs = ($dims[1]-$dims[0], $dims[3]-$dims[2], $dims[5]-$dims[4]); |
|
79 @diffs = reverse sort { $a <=> $b } @diffs; |
|
80 my $size = $diffs[0]; |
|
81 |
|
82 # Move objects so centre is at origin |
|
83 my $xtrans = -1.0 * ($dims[0] + $dims[1]) / 2; |
|
84 my $ytrans = -1.0 * ($dims[2] + $dims[3]) / 2; |
|
85 my $ztrans = -1.0 * ($dims[4] + $dims[5]) / 2; |
|
86 # Scale so that largest object dimension is $maxscale |
|
87 my $scale = $maxscale / $size; |
|
88 |
|
89 system "xform -t $xtrans $ytrans $ztrans -s $scale $ARGV[0] > $lumi"; |
|
90 } |
|
91 |
|
92 print FH <<EndOfRoom; |
|
93 # Don't generate -y face so we can look into the box (could use clipping) |
|
94 #wall_mat polygon box.1540 0 0 12 $r -$r -$r $r -$r $r -$r -$r $r -$r -$r -$r |
|
95 wall_mat polygon box.4620 0 0 12 -$r -$r $r -$r $r $r -$r $r -$r -$r -$r -$r |
|
96 wall_mat polygon box.2310 0 0 12 -$r $r -$r $r $r -$r $r -$r -$r -$r -$r -$r |
|
97 wall_mat polygon box.3267 0 0 12 $r $r -$r -$r $r -$r -$r $r $r $r $r $r |
|
98 wall_mat polygon box.5137 0 0 12 $r -$r $r $r -$r -$r $r $r -$r $r $r $r |
|
99 wall_mat polygon box.6457 0 0 12 -$r $r $r -$r -$r $r $r -$r $r $r $r $r |
118 EndOfRoom |
100 EndOfRoom |
119 } |
|
120 close(FH); |
101 close(FH); |
121 |
|
122 # Work out how large the luminaire is and scale so that the longest |
|
123 # axis-align dimension is one unit |
|
124 my $dimstr = `getbbox -h $ARGV[0]`; |
|
125 chomp $dimstr; |
|
126 # Values returned by getbbox are indented and delimited with multiple spaces. |
|
127 $dimstr =~ s/^\s+//; # remove leading spaces |
|
128 my @dims = split(/\s+/, $dimstr); # convert to array |
|
129 |
|
130 # Find largest axes-aligned dimension |
|
131 my @diffs = ($dims[1]-$dims[0], $dims[3]-$dims[2], $dims[5]-$dims[4]); |
|
132 @diffs = reverse sort { $a <=> $b } @diffs; |
|
133 my $size = $diffs[0]; |
|
134 |
|
135 # Move objects so centre is at origin |
|
136 my $xtrans = -1.0 * ($dims[0] + $dims[1]) / 2; |
|
137 my $ytrans = -1.0 * ($dims[2] + $dims[3]) / 2; |
|
138 my $ztrans = -1.0 * ($dims[4] + $dims[5]) / 2; |
|
139 # Scale so that largest object dimension is unity |
|
140 my $scale = 1 / $size; |
|
141 |
|
142 my $cmd = "xform -t $xtrans $ytrans $ztrans -s $scale $ARGV[0] > $lumi"; |
|
143 system "$cmd"; |
|
144 |
102 |
145 my $scene = "$room $lumi"; |
103 my $scene = "$room $lumi"; |
146 # Make this work under Windoze |
104 # Make this work under Windoze |
147 if ( $^O =~ /MSWin32/ ) { |
105 if ( $^O =~ /MSWin32/ ) { |
148 $scene =~ s{\\}{/}g; |
106 $scene =~ s{\\}{/}g; |
149 $octree =~ s{\\}{/}g; |
107 $oct =~ s{\\}{/}g; |
150 $ambf =~ s{\\}{/}g; |
|
151 $raddev = "qt"; |
108 $raddev = "qt"; |
152 } |
109 } |
153 |
110 |
154 my $custom_vw = $vw; |
111 # Tweak bounding box so we get a nice view covering all of the box, without |
155 $vw =~ s/^\+//; |
112 # having a wasteful black border around it. Must work for arbitrary box dims. |
|
113 my $zone = 1.1 * $r * ( 1 + 1/tan(22.5*pi/180) ); |
|
114 |
156 open(FH, ">$rif") or |
115 open(FH, ">$rif") or |
157 die("ltview: Can't write to temporary file $rif\n"); |
116 die("ltview: Can't write to temporary file $rif\n"); |
158 print FH <<EndOfRif; |
117 print FH <<EndOfRif; |
159 scene= $scene |
118 scene= $scene |
160 EXPOSURE= 2 |
119 EXPOSURE= 2 |
161 ZONE= Interior -5 5 -5 5 -5 5 |
120 ZONE= Interior -$zone $zone -$zone $zone -$zone $zone |
162 UP= $up |
121 UP= Z |
163 view= $vw |
122 view= y |
164 OCTREE= $octree |
123 OCTREE= $oct |
165 oconv= -f |
124 oconv= -f |
166 AMBF= $ambf |
125 render= $render |
167 render= $rendopts |
|
168 EndOfRif |
126 EndOfRif |
169 close(FH); |
127 close(FH); |
170 |
128 |
171 # Custom view: look away from the luminaire, not at it. This is indicated |
129 system "rad -o $raddev $opts $rif"; |
172 # by a leading '+' in front of the view (-v argument, default: XYZ) |
|
173 if ($custom_vw =~ m/^\+/) { |
|
174 # Get rad to spit out the -v* options for the default view requested |
|
175 my $view = `rad -V -n -s $rif`; |
|
176 $view =~ s/\n//; |
|
177 $view =~ s/^VIEW= //; |
|
178 |
|
179 my $x = 0; |
|
180 my $y = 0; |
|
181 my $z = 0; |
|
182 if ($vw =~ m/X/) { |
|
183 $x = $dims[0] - $TINY; |
|
184 } elsif ($vw =~ m/x/) { |
|
185 $x = $dims[1] + $TINY; |
|
186 } |
|
187 if ($vw =~ m/Y/) { |
|
188 $y = $dims[2] - $TINY; |
|
189 } elsif ($vw =~ m/y/) { |
|
190 $y = $dims[3] + $TINY; |
|
191 } |
|
192 if ($vw =~ m/Z/) { |
|
193 $z = $dims[4] - $TINY; |
|
194 } elsif ($vw =~ m/z/) { |
|
195 $z = $dims[5] + $TINY; |
|
196 } |
|
197 |
|
198 # Keep rad-generated standard view, but modify -vp |
|
199 $vw = "nice $view -vp $x $y $z"; |
|
200 |
|
201 open(FH, ">$rif") or |
|
202 die("ltview: Can't write to temporary file $rif\n"); |
|
203 print FH <<EndOfRif2; |
|
204 scene= $scene |
|
205 EXPOSURE= 2 |
|
206 ZONE= Interior -5 5 -5 5 -5 5 |
|
207 UP= $up |
|
208 view= $vw |
|
209 OCTREE= $octree |
|
210 oconv= -f |
|
211 AMBF= $ambf |
|
212 render= $rendopts |
|
213 EndOfRif2 |
|
214 close(FH); |
|
215 } |
|
216 |
|
217 if ($usegl) { |
|
218 system "glrad $opts $rif"; |
|
219 } else { |
|
220 system "rad -o $raddev $opts $rif"; |
|
221 } |
|
222 |
130 |
223 #EOF |
131 #EOF |