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