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