Cycles: Fix/workaround for gcc-7.2
[blender.git] / intern / cycles / util / util_ies.cpp
1 /*
2  * Copyright 2011-2018 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "util/util_foreach.h"
18 #include "util/util_ies.h"
19 #include "util/util_math.h"
20 #include "util/util_string.h"
21
22 CCL_NAMESPACE_BEGIN
23
24 // NOTE: For some reason gcc-7.2 does not instantiate this versio of allocator
25 // gere (used in IESTextParser). Works fine for gcc-6, gcc-7.3 and gcc-8.
26 //
27 // TODO(sergey): Get to the root of this issue, or confirm this i a compiler
28 // issue.
29 template class GuardedAllocator<char>;
30
31 bool IESFile::load(ustring ies)
32 {
33         clear();
34         if(!parse(ies) || !process()) {
35                 clear();
36                 return false;
37         }
38         return true;
39 }
40
41 void IESFile::clear()
42 {
43         intensity.clear();
44         v_angles.clear();
45         h_angles.clear();
46 }
47
48 int IESFile::packed_size()
49 {
50         if(v_angles.size() && h_angles.size() > 0) {
51                 return 2 + h_angles.size() + v_angles.size() + h_angles.size()*v_angles.size();
52         }
53         return 0;
54 }
55
56 void IESFile::pack(float *data)
57 {
58         if(v_angles.size() && h_angles.size()) {
59                 *(data++) = __int_as_float(h_angles.size());
60                 *(data++) = __int_as_float(v_angles.size());
61
62                 memcpy(data, &h_angles[0], h_angles.size()*sizeof(float));
63                 data += h_angles.size();
64                 memcpy(data, &v_angles[0], v_angles.size()*sizeof(float));
65                 data += v_angles.size();
66
67                 for(int h = 0; h < intensity.size(); h++) {
68                         memcpy(data, &intensity[h][0], v_angles.size()*sizeof(float));
69                         data += v_angles.size();
70                 }
71         }
72 }
73
74 class IESTextParser {
75 public:
76         vector<char> text;
77         char *data;
78
79         IESTextParser(ustring str)
80          : text(str.begin(), str.end())
81         {
82                 std::replace(text.begin(), text.end(), ',', ' ');
83                 data = strstr(&text[0], "\nTILT=");
84         }
85
86         bool eof() {
87                 return (data == NULL) || (data[0] == '\0');
88         }
89
90         double get_double() {
91                 if(eof()) {
92                         return 0.0;
93                 }
94                 char *old_data = data;
95                 double val = strtod(data, &data);
96                 if(data == old_data) {
97                         data = NULL;
98                         return 0.0;
99                 }
100                 return val;
101         }
102
103         long get_long() {
104                 if(eof()) {
105                         return 0;
106                 }
107                 char *old_data = data;
108                 long val = strtol(data, &data, 10);
109                 if(data == old_data) {
110                         data = NULL;
111                         return 0;
112                 }
113                 return val;
114         }
115 };
116
117 bool IESFile::parse(ustring ies)
118 {
119         if(ies.empty()) {
120                 return false;
121         }
122
123         IESTextParser parser(ies);
124         if(parser.eof()) {
125                 return false;
126         }
127
128         /* Handle the tilt data block. */
129         if(strncmp(parser.data, "\nTILT=INCLUDE", 13) == 0) {
130                 parser.data += 13;
131                 parser.get_double(); /* Lamp to Luminaire geometry */
132                 int num_tilt = parser.get_long(); /* Amount of tilt angles and factors */
133                 /* Skip over angles and factors. */
134                 for(int i = 0; i < 2*num_tilt; i++) {
135                         parser.get_double();
136                 }
137         }
138         else {
139                 /* Skip to next line. */
140                 parser.data = strstr(parser.data+1, "\n");
141         }
142
143         if(parser.eof()) {
144                 return false;
145         }
146         parser.data++;
147
148         parser.get_long(); /* Number of lamps */
149         parser.get_double(); /* Lumens per lamp */
150         double factor = parser.get_double(); /* Candela multiplier */
151         int v_angles_num = parser.get_long(); /* Number of vertical angles */
152         int h_angles_num = parser.get_long(); /* Number of horizontal angles */
153         type = (IESType) parser.get_long(); /* Photometric type */
154
155         /* TODO(lukas): Test whether the current type B processing can also deal with type A files.
156          * In theory the only difference should be orientation which we ignore anyways, but with IES you never know...
157          */
158         if(type != TYPE_B && type != TYPE_C) {
159                 return false;
160         }
161
162         parser.get_long(); /* Unit of the geometry data */
163         parser.get_double(); /* Width */
164         parser.get_double(); /* Length */
165         parser.get_double(); /* Height */
166         factor *= parser.get_double(); /* Ballast factor */
167         factor *= parser.get_double(); /* Ballast-Lamp Photometric factor */
168         parser.get_double(); /* Input Watts */
169
170         /* Intensity values in IES files are specified in candela (lumen/sr), a photometric quantity.
171          * Cycles expects radiometric quantities, though, which requires a conversion.
172          * However, the Luminous efficacy (ratio of lumens per Watt) depends on the spectral distribution
173          * of the light source since lumens take human perception into account.
174          * Since this spectral distribution is not known from the IES file, a typical one must be assumed.
175          * The D65 standard illuminant has a Luminous efficacy of 177.83, which is used here to convert to Watt/sr.
176          * A more advanced approach would be to add a Blackbody Temperature input to the node and numerically
177          * integrate the Luminous efficacy from the resulting spectral distribution.
178          * Also, the Watt/sr value must be multiplied by 4*pi to get the Watt value that Cycles expects
179          * for lamp strength. Therefore, the conversion here uses 4*pi/177.83 as a Candela to Watt factor.
180          */
181         factor *= 0.0706650768394;
182
183         v_angles.reserve(v_angles_num);
184         for(int i = 0; i < v_angles_num; i++) {
185                 v_angles.push_back((float) parser.get_double());
186         }
187
188         h_angles.reserve(h_angles_num);
189         for(int i = 0; i < h_angles_num; i++) {
190                 h_angles.push_back((float) parser.get_double());
191         }
192
193         intensity.resize(h_angles_num);
194         for(int i = 0; i < h_angles_num; i++) {
195                 intensity[i].reserve(v_angles_num);
196                 for(int j = 0; j < v_angles_num; j++) {
197                         intensity[i].push_back((float) (factor * parser.get_double()));
198                 }
199         }
200
201         return !parser.eof();
202 }
203
204 bool IESFile::process_type_b()
205 {
206         vector<vector<float> > newintensity;
207         newintensity.resize(v_angles.size());
208         for(int i = 0; i < v_angles.size(); i++) {
209                 newintensity[i].reserve(h_angles.size());
210                 for(int j = 0; j < h_angles.size(); j++) {
211                         newintensity[i].push_back(intensity[j][i]);
212                 }
213         }
214         intensity.swap(newintensity);
215         h_angles.swap(v_angles);
216
217         float h_first = h_angles[0], h_last = h_angles[h_angles.size()-1];
218         if(h_last != 90.0f) {
219                 return false;
220         }
221
222         if(h_first == 0.0f) {
223                 /* The range in the file corresponds to 90°-180°, we need to mirror that to get the
224                  * full 180° range. */
225                 vector<float> new_h_angles;
226                 vector<vector<float> > new_intensity;
227                 int hnum = h_angles.size();
228                 new_h_angles.reserve(2*hnum-1);
229                 new_intensity.reserve(2*hnum-1);
230                 for(int i = hnum-1; i > 0; i--) {
231                         new_h_angles.push_back(90.0f - h_angles[i]);
232                         new_intensity.push_back(intensity[i]);
233                 }
234                 for(int i = 0; i < hnum; i++) {
235                         new_h_angles.push_back(90.0f + h_angles[i]);
236                         new_intensity.push_back(intensity[i]);
237                 }
238                 h_angles.swap(new_h_angles);
239                 intensity.swap(new_intensity);
240         }
241         else if(h_first == -90.0f) {
242                 /* We have full 180° coverage, so just shift to match the angle range convention. */
243                 for(int i = 0; i < h_angles.size(); i++) {
244                         h_angles[i] += 90.0f;
245                 }
246         }
247         /* To get correct results with the cubic interpolation in the kernel, the horizontal range
248          * has to cover all 360°. Therefore, we copy the 0° entry to 360° to ensure full coverage
249          * and seamless interpolation. */
250         h_angles.push_back(360.0f);
251         intensity.push_back(intensity[0]);
252
253         float v_first = v_angles[0], v_last = v_angles[v_angles.size()-1];
254         if(v_last != 90.0f) {
255                 return false;
256         }
257
258         if(v_first == 0.0f) {
259                 /* The range in the file corresponds to 90°-180°, we need to mirror that to get the
260                  * full 180° range. */
261                 vector<float> new_v_angles;
262                 int hnum = h_angles.size();
263                 int vnum = v_angles.size();
264                 new_v_angles.reserve(2*vnum-1);
265                 for(int i = vnum-1; i > 0; i--) {
266                         new_v_angles.push_back(90.0f - v_angles[i]);
267                 }
268                 for(int i = 0; i < vnum; i++) {
269                         new_v_angles.push_back(90.0f + v_angles[i]);
270                 }
271                 for(int i = 0; i < hnum; i++) {
272                         vector<float> new_intensity;
273                         new_intensity.reserve(2*vnum-1);
274                         for(int j = vnum-2; j >= 0; j--) {
275                                 new_intensity.push_back(intensity[i][j]);
276                         }
277                         new_intensity.insert(new_intensity.end(), intensity[i].begin(), intensity[i].end());
278                         intensity[i].swap(new_intensity);
279                 }
280                 v_angles.swap(new_v_angles);
281         }
282         else if(v_first == -90.0f) {
283                 /* We have full 180° coverage, so just shift to match the angle range convention. */
284                 for(int i = 0; i < v_angles.size(); i++) {
285                         v_angles[i] += 90.0f;
286                 }
287         }
288
289         return true;
290 }
291
292 bool IESFile::process_type_c()
293 {
294         if(h_angles[0] == 90.0f) {
295                 /* Some files are stored from 90° to 270°, so we just rotate them to the regular 0°-180° range here. */
296                 for(int i = 0; i < v_angles.size(); i++) {
297                         h_angles[i] -= 90.0f;
298                 }
299         }
300
301         if(h_angles[0] != 0.0f) {
302                 return false;
303         }
304
305         if(h_angles.size() == 1) {
306                 h_angles.push_back(360.0f);
307                 intensity.push_back(intensity[0]);
308         }
309
310         if(h_angles[h_angles.size()-1] == 90.0f) {
311                 /* Only one quadrant is defined, so we need to mirror twice (from one to two, then to four).
312                  * Since the two->four mirroring step might also be required if we get an input of two quadrants,
313                  * we only do the first mirror here and later do the second mirror in either case. */
314                 int hnum = h_angles.size();
315                 for(int i = hnum-2; i >= 0; i--) {
316                         h_angles.push_back(180.0f - h_angles[i]);
317                         intensity.push_back(intensity[i]);
318                 }
319         }
320
321         if(h_angles[h_angles.size()-1] == 180.0f) {
322                 /* Mirror half to the full range. */
323                 int hnum = h_angles.size();
324                 for(int i = hnum-2; i >= 0; i--) {
325                         h_angles.push_back(360.0f - h_angles[i]);
326                         intensity.push_back(intensity[i]);
327                 }
328         }
329
330         /* Some files skip the 360° entry (contrary to standard) because it's supposed to be identical to the 0° entry.
331          * If the file has a discernible order in its spacing, just fix this. */
332         if(h_angles[h_angles.size()-1] != 360.0f) {
333                 int hnum = h_angles.size();
334                 float last_step = h_angles[hnum-1]-h_angles[hnum-2];
335                 float first_step = h_angles[1]-h_angles[0];
336                 float difference = 360.0f - h_angles[hnum-1];
337                 if(last_step == difference || first_step == difference) {
338                         h_angles.push_back(360.0f);
339                         intensity.push_back(intensity[0]);
340                 }
341                 else {
342                         return false;
343                 }
344         }
345
346         float v_first = v_angles[0], v_last = v_angles[v_angles.size()-1];
347         if(v_first == 90.0f) {
348                 if(v_last == 180.0f) {
349                         /* Flip to ensure that vertical angles always start at 0°. */
350                         for(int i = 0; i < v_angles.size(); i++) {
351                                 v_angles[i] = 180.0f - v_angles[i];
352                         }
353                 }
354                 else {
355                         return false;
356                 }
357         }
358         else if(v_first != 0.0f) {
359                 return false;
360         }
361
362         return true;
363 }
364
365 bool IESFile::process()
366 {
367         if(h_angles.size() == 0 || v_angles.size() == 0) {
368                 return false;
369         }
370
371         if(type == TYPE_B) {
372                 if(!process_type_b()) {
373                         return false;
374                 }
375         }
376         else {
377                 assert(type == TYPE_C);
378                 if(!process_type_c()) {
379                         return false;
380                 }
381         }
382
383         assert(v_angles[0] == 0.0f);
384         assert(h_angles[0] == 0.0f);
385         assert(h_angles[h_angles.size()-1] == 360.0f);
386
387         /* Convert from deg to rad. */
388         for(int i = 0; i < v_angles.size(); i++) {
389                 v_angles[i] *= M_PI_F / 180.f;
390         }
391         for(int i = 0; i < h_angles.size(); i++) {
392                 h_angles[i] *= M_PI_F / 180.f;
393         }
394
395         return true;
396 }
397
398 IESFile::~IESFile()
399 {
400         clear();
401 }
402
403 CCL_NAMESPACE_END