1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 // Interface header.
31 #include "projectfileupdater.h"
32 
33 // appleseed.renderer headers.
34 #include "renderer/global/globallogger.h"
35 #include "renderer/modeling/bsdf/bsdf.h"
36 #include "renderer/modeling/bsdf/disneybrdf.h"
37 #include "renderer/modeling/bsdf/glassbsdf.h"
38 #include "renderer/modeling/bsdf/glossybrdf.h"
39 #include "renderer/modeling/bsdf/metalbrdf.h"
40 #include "renderer/modeling/bsdf/specularbtdf.h"
41 #include "renderer/modeling/bssrdf/bssrdf.h"
42 #include "renderer/modeling/bssrdf/gaussianbssrdf.h"
43 #include "renderer/modeling/camera/camera.h"
44 #include "renderer/modeling/color/colorentity.h"
45 #include "renderer/modeling/edf/diffuseedf.h"
46 #include "renderer/modeling/edf/edf.h"
47 #include "renderer/modeling/entity/entity.h"
48 #include "renderer/modeling/environmentedf/constantenvironmentedf.h"
49 #include "renderer/modeling/environmentedf/constanthemisphereenvironmentedf.h"
50 #include "renderer/modeling/environmentedf/environmentedf.h"
51 #include "renderer/modeling/environmentedf/gradientenvironmentedf.h"
52 #include "renderer/modeling/environmentedf/latlongmapenvironmentedf.h"
53 #include "renderer/modeling/environmentedf/mirrorballmapenvironmentedf.h"
54 #include "renderer/modeling/environmentshader/environmentshader.h"
55 #include "renderer/modeling/frame/frame.h"
56 #include "renderer/modeling/input/colorsource.h"
57 #include "renderer/modeling/light/directionallight.h"
58 #include "renderer/modeling/light/light.h"
59 #include "renderer/modeling/light/pointlight.h"
60 #include "renderer/modeling/light/spotlight.h"
61 #include "renderer/modeling/light/sunlight.h"
62 #include "renderer/modeling/material/material.h"
63 #include "renderer/modeling/object/object.h"
64 #include "renderer/modeling/postprocessingstage/renderstamppostprocessingstage.h"
65 #include "renderer/modeling/project/configuration.h"
66 #include "renderer/modeling/project/eventcounters.h"
67 #include "renderer/modeling/project/project.h"
68 #include "renderer/modeling/project/projectformatrevision.h"
69 #include "renderer/modeling/scene/assembly.h"
70 #include "renderer/modeling/scene/assemblyinstance.h"
71 #include "renderer/modeling/scene/containers.h"
72 #include "renderer/modeling/scene/objectinstance.h"
73 #include "renderer/modeling/scene/scene.h"
74 #include "renderer/modeling/surfaceshader/physicalsurfaceshader.h"
75 #include "renderer/modeling/surfaceshader/surfaceshader.h"
76 #include "renderer/utility/paramarray.h"
77 
78 // appleseed.foundation headers.
79 #include "foundation/core/concepts/noncopyable.h"
80 #include "foundation/math/root.h"
81 #include "foundation/math/scalar.h"
82 #include "foundation/platform/compiler.h"
83 #include "foundation/platform/types.h"
84 #include "foundation/utility/api/apistring.h"
85 #include "foundation/utility/autoreleaseptr.h"
86 #include "foundation/utility/containers/dictionary.h"
87 #include "foundation/utility/foreach.h"
88 #include "foundation/utility/iterators.h"
89 #include "foundation/utility/string.h"
90 
91 // Standard headers.
92 #include <algorithm>
93 #include <cassert>
94 #include <cmath>
95 #include <cstring>
96 #include <limits>
97 #include <string>
98 #include <vector>
99 
100 using namespace foundation;
101 using namespace std;
102 
103 namespace renderer
104 {
105 
106 namespace
107 {
108     //
109     // Base updater class.
110     //
111 
112     class Updater
113       : public NonCopyable
114     {
115       public:
Updater(Project & project,const size_t from_revision)116         Updater(Project& project, const size_t from_revision)
117           : m_project(project)
118           , m_from_revision(from_revision)
119           , m_to_revision(from_revision + 1)
120         {
121             assert(m_project.get_format_revision() == m_from_revision);
122 
123             RENDERER_LOG_INFO(
124                 "migrating project format from revision " FMT_SIZE_T " to revision " FMT_SIZE_T "...",
125                 m_from_revision,
126                 m_to_revision);
127         }
128 
~Updater()129         virtual ~Updater()
130         {
131             m_project.set_format_revision(m_to_revision);
132         }
133 
134         virtual void update() = 0;
135 
136       protected:
137         Project&                m_project;
138         const size_t            m_from_revision;
139         const size_t            m_to_revision;
140 
141         // Copy a key from one dictionary to another.
copy_if_exist(Dictionary & dest,const Dictionary & src,const char * key)142         static void copy_if_exist(
143             Dictionary&         dest,
144             const Dictionary&   src,
145             const char*         key)
146         {
147             if (src.strings().exist(key))
148                 dest.strings().insert(key, src.strings().get(key));
149         }
150 
151         // Copy a key from one dictionary to another, and rename the key.
copy_if_exist(Dictionary & dest,const char * dest_key,const Dictionary & src,const char * src_key)152         static void copy_if_exist(
153             Dictionary&         dest,
154             const char*         dest_key,
155             const Dictionary&   src,
156             const char*         src_key)
157         {
158             if (src.strings().exist(src_key))
159                 dest.strings().insert(dest_key, src.strings().get(src_key));
160         }
161 
162         // Copy a key from one dictionary to same dictionary.
copy_if_exist_no_overwrite(Dictionary & dict,const char * dest_key,const char * src_key)163         static void copy_if_exist_no_overwrite(
164             Dictionary&         dict,
165             const char*         dest_key,
166             const char*         src_key)
167         {
168             if (!dict.strings().exist(dest_key))
169                 copy_if_exist(dict, dest_key, dict, src_key);
170         }
171 
172         // Move a key from one dictionary to another at a given key.
move_if_exist(Dictionary & dest,const char * dest_key,Dictionary & src,const char * src_key)173         static void move_if_exist(
174             Dictionary&         dest,
175             const char*         dest_key,
176             Dictionary&         src,
177             const char*         src_key)
178         {
179             if (src.strings().exist(src_key))
180             {
181                 dest.strings().insert(dest_key, src.strings().get(src_key));
182                 src.strings().remove(src_key);
183             }
184         }
185 
186         // Move a key from one dictionary to another at a given path.
move_if_exist(ParamArray & dest,const char * dest_path,Dictionary & src,const char * src_key)187         static void move_if_exist(
188             ParamArray&         dest,
189             const char*         dest_path,
190             Dictionary&         src,
191             const char*         src_key)
192         {
193             if (src.strings().exist(src_key))
194             {
195                 dest.insert_path(dest_path, src.strings().get(src_key));
196                 src.strings().remove(src_key);
197             }
198         }
199 
200         // Move a key to a new path in the same parameter array.
move_if_exist(ParamArray & params,const char * dest_path,const char * src_key)201         static void move_if_exist(
202             ParamArray&         params,
203             const char*         dest_path,
204             const char*         src_key)
205         {
206             move_if_exist(params, dest_path, params, src_key);
207         }
208 
209         // Helper function, same functionality as above.
move_if_exist(Entity & entity,const char * dest_path,const char * src_key)210         static void move_if_exist(
211             Entity&             entity,
212             const char*         dest_path,
213             const char*         src_key)
214         {
215             move_if_exist(entity.get_parameters(), dest_path, src_key);
216         }
217     };
218 
219 
220     //
221     // Update from revision 0 to revision 1.
222     //
223 
224     class UpdateFromRevision_0
225       : public Updater
226     {
227       public:
UpdateFromRevision_0(Project & project)228         explicit UpdateFromRevision_0(Project& project)
229           : Updater(project, 0)
230         {
231         }
232 
update()233         void update() override
234         {
235             // Nothing to do.
236         }
237     };
238 
239 
240     //
241     // Update from revision 1 to revision 2.
242     //
243 
244     class UpdateFromRevision_1
245       : public Updater
246     {
247       public:
UpdateFromRevision_1(Project & project)248         explicit UpdateFromRevision_1(Project& project)
249           : Updater(project, 1)
250         {
251         }
252 
update()253         void update() override
254         {
255             // Nothing to do.
256         }
257     };
258 
259 
260     //
261     // Update from revision 2 to revision 3.
262     //
263 
264     class UpdateFromRevision_2
265       : public Updater
266     {
267       public:
UpdateFromRevision_2(Project & project)268         explicit UpdateFromRevision_2(Project& project)
269           : Updater(project, 2)
270         {
271         }
272 
update()273         void update() override
274         {
275             introduce_pixel_renderers();
276             move_filter_parameters_from_configurations_to_frame();
277         }
278 
279       private:
max_variation_to_quality(const float variation)280         static float max_variation_to_quality(const float variation)
281         {
282             const float q = -log(variation, 10.0f);
283             const int n = static_cast<int>(q * 10.0f);
284             return static_cast<float>(n) / 10.0f;
285         }
286 
introduce_pixel_renderers()287         void introduce_pixel_renderers()
288         {
289             for (each<ConfigurationContainer> i = m_project.configurations(); i; ++i)
290             {
291                 Dictionary& root = i->get_parameters();
292 
293                 if (!root.dictionaries().exist("generic_tile_renderer"))
294                     continue;
295 
296                 Dictionary& gtr = root.dictionary("generic_tile_renderer");
297 
298                 copy_if_exist(root, "pixel_renderer", gtr, "sampler");
299 
300                 {
301                     Dictionary upr;
302                     copy_if_exist(upr, "samples", gtr, "max_samples");
303                     root.insert("uniform_pixel_renderer", upr);
304                 }
305 
306                 {
307                     Dictionary apr;
308                     copy_if_exist(apr, gtr, "min_samples");
309                     copy_if_exist(apr, gtr, "max_samples");
310                     if (gtr.strings().exist("max_variation"))
311                         apr.insert("quality", max_variation_to_quality(gtr.get<float>("max_variation")));
312                     copy_if_exist(apr, "enable_diagnostics", gtr, "enable_adaptive_sampler_diagnostics");
313                     root.insert("adaptive_pixel_renderer", apr);
314                 }
315 
316                 gtr.strings().remove("sampler");
317                 gtr.strings().remove("min_samples");
318                 gtr.strings().remove("max_samples");
319                 gtr.strings().remove("max_contrast");
320                 gtr.strings().remove("max_variation");
321                 gtr.strings().remove("enable_adaptive_sampler_diagnostics");
322             }
323         }
324 
move_filter_parameters_from_configurations_to_frame()325         void move_filter_parameters_from_configurations_to_frame()
326         {
327             Frame* frame = m_project.get_frame();
328 
329             for (each<ConfigurationContainer> i = m_project.configurations(); i; ++i)
330             {
331                 Dictionary& root = i->get_parameters();
332 
333                 if (!root.dictionaries().exist("generic_tile_renderer"))
334                     continue;
335 
336                 Dictionary& gtr = root.dictionary("generic_tile_renderer");
337 
338                 if (frame && strcmp(i->get_name(), "final") == 0)
339                 {
340                     copy_if_exist(frame->get_parameters(), gtr, "filter");
341                     copy_if_exist(frame->get_parameters(), gtr, "filter_size");
342                 }
343 
344                 gtr.strings().remove("filter");
345                 gtr.strings().remove("filter_size");
346             }
347         }
348     };
349 
350 
351     //
352     // Update from revision 3 to revision 4.
353     //
354 
355     class UpdateFromRevision_3
356       : public Updater
357     {
358       public:
UpdateFromRevision_3(Project & project)359         explicit UpdateFromRevision_3(Project& project)
360           : Updater(project, 3)
361         {
362         }
363 
update()364         void update() override
365         {
366             if (Scene* scene = m_project.get_scene())
367             {
368                 for (each<EnvironmentEDFContainer> i = scene->environment_edfs(); i; ++i)
369                     rename_exitance_inputs(*i);
370 
371                 rename_exitance_inputs(scene->assemblies());
372             }
373         }
374 
375       private:
rename_exitance_inputs(AssemblyContainer & assemblies)376         static void rename_exitance_inputs(AssemblyContainer& assemblies)
377         {
378             for (each<AssemblyContainer> i = assemblies; i; ++i)
379             {
380                 rename_exitance_inputs(*i);
381                 rename_exitance_inputs(i->assemblies());
382             }
383         }
384 
rename_exitance_inputs(Assembly & assembly)385         static void rename_exitance_inputs(Assembly& assembly)
386         {
387             for (each<EDFContainer> i = assembly.edfs(); i; ++i)
388                 rename_exitance_inputs(*i);
389 
390             for (each<LightContainer> i = assembly.lights(); i; ++i)
391                 rename_exitance_inputs(*i);
392         }
393 
rename_exitance_inputs(EDF & edf)394         static void rename_exitance_inputs(EDF& edf)
395         {
396             if (strcmp(edf.get_model(), DiffuseEDFFactory().get_model()) == 0)
397             {
398                 move_if_exist(edf, "radiance", "exitance");
399                 move_if_exist(edf, "radiance_multiplier", "exitance_multiplier");
400             }
401         }
402 
rename_exitance_inputs(Light & light)403         static void rename_exitance_inputs(Light& light)
404         {
405             if (strcmp(light.get_model(), DirectionalLightFactory().get_model()) == 0 ||
406                 strcmp(light.get_model(), PointLightFactory().get_model()) == 0 ||
407                 strcmp(light.get_model(), SpotLightFactory().get_model()) == 0)
408             {
409                 move_if_exist(light, "radiance", "exitance");
410                 move_if_exist(light, "radiance_multiplier", "exitance_multiplier");
411             }
412             else if (strcmp(light.get_model(), SunLightFactory().get_model()) == 0)
413             {
414                 move_if_exist(light, "radiance_multiplier", "exitance_multiplier");
415             }
416         }
417 
rename_exitance_inputs(EnvironmentEDF & edf)418         static void rename_exitance_inputs(EnvironmentEDF& edf)
419         {
420             if (strcmp(edf.get_model(), ConstantEnvironmentEDFFactory().get_model()) == 0)
421             {
422                 move_if_exist(edf, "radiance", "exitance");
423             }
424             else if (strcmp(edf.get_model(), ConstantHemisphereEnvironmentEDFFactory().get_model()) == 0)
425             {
426                 move_if_exist(edf, "upper_hemi_radiance", "upper_hemi_exitance");
427                 move_if_exist(edf, "lower_hemi_radiance", "lower_hemi_exitance");
428             }
429             else if (strcmp(edf.get_model(), GradientEnvironmentEDFFactory().get_model()) == 0)
430             {
431                 move_if_exist(edf, "horizon_radiance", "horizon_exitance");
432                 move_if_exist(edf, "zenith_radiance", "zenith_exitance");
433             }
434             else if (strcmp(edf.get_model(), LatLongMapEnvironmentEDFFactory().get_model()) == 0 ||
435                      strcmp(edf.get_model(), MirrorBallMapEnvironmentEDFFactory().get_model()) == 0)
436             {
437                 move_if_exist(edf, "radiance", "exitance");
438                 move_if_exist(edf, "radiance_multiplier", "exitance_multiplier");
439             }
440         }
441     };
442 
443 
444     //
445     // Update from revision 4 to revision 5.
446     //
447 
448     class UpdateFromRevision_4
449       : public Updater
450     {
451       public:
UpdateFromRevision_4(Project & project)452         explicit UpdateFromRevision_4(Project& project)
453           : Updater(project, 4)
454         {
455         }
456 
update()457         void update() override
458         {
459             for (each<ConfigurationContainer> i = m_project.configurations(); i; ++i)
460             {
461                 ParamArray& root = i->get_parameters();
462                 move_if_exist(root, "texture_store.max_size", "texture_cache_size");
463             }
464         }
465     };
466 
467 
468     //
469     // Update from revision 5 to revision 6.
470     //
471 
472     class UpdateFromRevision_5
473       : public Updater
474     {
475       public:
UpdateFromRevision_5(Project & project)476         explicit UpdateFromRevision_5(Project& project)
477           : Updater(project, 5)
478         {
479         }
480 
update()481         void update() override
482         {
483             if (Scene* scene = m_project.get_scene())
484                 update_assemblies(scene->assemblies());
485         }
486 
487       private:
488         class BlinnExponentFunction
489         {
490           public:
BlinnExponentFunction(const float e)491             explicit BlinnExponentFunction(const float e)
492               : m_e(e)
493             {
494             }
495 
operator ()(const float x) const496             float operator()(const float x) const
497             {
498                 return 100.0f * pow_int<3>(x) + 9900.0f * pow_int<30>(x) - m_e;
499             }
500 
501           private:
502             const float m_e;
503         };
504 
update_assemblies(const AssemblyContainer & assemblies)505         static void update_assemblies(const AssemblyContainer& assemblies)
506         {
507             for (const_each<AssemblyContainer> i = assemblies; i; ++i)
508             {
509                 update_bsdfs(*i, i->bsdfs());
510                 update_assemblies(i->assemblies());
511             }
512         }
513 
update_bsdfs(const Assembly & assembly,BSDFContainer & bsdfs)514         static void update_bsdfs(const Assembly& assembly, BSDFContainer& bsdfs)
515         {
516             for (each<BSDFContainer> i = bsdfs; i; ++i)
517             {
518                 BSDF& bsdf = *i;
519 
520                 if (strcmp(bsdf.get_model(), "microfacet_brdf"))
521                     continue;
522 
523                 ParamArray& params = bsdf.get_parameters();
524 
525                 const string mdf = params.get_optional<string>("mdf", "");
526                 const string mdf_param = params.get_optional<string>("mdf_parameter", "");
527 
528                 if (mdf_param.empty())
529                     continue;
530 
531                 float mdf_param_value;
532                 if (try_parse_scalar(mdf_param, mdf_param_value))
533                 {
534                     float glossiness;
535                     if (!mdf_param_to_glossiness(mdf, mdf_param_value, glossiness))
536                     {
537                         RENDERER_LOG_ERROR(
538                             "while updating bsdf \"%s\", failed to convert mdf parameter %f.",
539                             bsdf.get_path().c_str(),
540                             mdf_param_value);
541                         continue;
542                     }
543 
544                     params.insert("glossiness", glossiness);
545                 }
546                 else
547                 {
548                     ColorEntity* color = find_color_entity(assembly, mdf_param);
549 
550                     if (color)
551                     {
552                         const ColorSource source(*color);
553 
554                         float mdf_param_value;
555                         source.evaluate_uniform(mdf_param_value);
556 
557                         float glossiness;
558                         if (!mdf_param_to_glossiness(mdf, mdf_param_value, glossiness))
559                         {
560                             RENDERER_LOG_ERROR(
561                                 "while updating bsdf \"%s\", failed to convert mdf parameter %f in color entity \"%s\".",
562                                 bsdf.get_path().c_str(),
563                                 mdf_param_value,
564                                 color->get_path().c_str());
565                             continue;
566                         }
567 
568                         ParamArray new_color_params = color->get_parameters();
569                         new_color_params.remove_path("multiplier");
570 
571                         ColorValueArray new_color_values;
572                         new_color_values.push_back(glossiness);
573 
574                         auto_release_ptr<ColorEntity> new_color_entity(
575                             ColorEntityFactory::create(
576                                 color->get_name(),
577                                 new_color_params,
578                                 new_color_values));
579 
580                         Assembly* parent_assembly = dynamic_cast<Assembly*>(color->get_parent());
581 
582                         if (parent_assembly)
583                         {
584                             parent_assembly->colors().remove(color);
585                             parent_assembly->colors().insert(new_color_entity);
586                         }
587                         else
588                         {
589                             Scene* parent_scene = dynamic_cast<Scene*>(color->get_parent());
590                             assert(parent_scene);
591 
592                             parent_scene->colors().remove(color);
593                             parent_scene->colors().insert(new_color_entity);
594                         }
595                     }
596 
597                     move_if_exist(params, "glossiness", "mdf_parameter");
598                 }
599             }
600         }
601 
try_parse_scalar(const string & s,float & value)602         static bool try_parse_scalar(const string& s, float& value)
603         {
604             try
605             {
606                 value = from_string<float>(s);
607                 return true;
608             }
609             catch (const ExceptionStringConversionError&)
610             {
611                 return false;
612             }
613         }
614 
find_color_entity(const Assembly & assembly,const string & name)615         static ColorEntity* find_color_entity(const Assembly& assembly, const string& name)
616         {
617             for (each<ColorContainer> i = assembly.colors(); i; ++i)
618             {
619                 if (i->get_name() == name)
620                     return &*i;
621             }
622 
623             Assembly* parent_assembly = dynamic_cast<Assembly*>(assembly.get_parent());
624 
625             if (parent_assembly)
626                 return find_color_entity(*parent_assembly, name);
627 
628             Scene* parent_scene = dynamic_cast<Scene*>(assembly.get_parent());
629             assert(parent_scene);
630 
631             return find_color_entity(*parent_scene, name);
632         }
633 
find_color_entity(const Scene & scene,const string & name)634         static ColorEntity* find_color_entity(const Scene& scene, const string& name)
635         {
636             for (each<ColorContainer> i = scene.colors(); i; ++i)
637             {
638                 if (i->get_name() == name)
639                     return &*i;
640             }
641 
642             return nullptr;
643         }
644 
mdf_param_to_glossiness(const string & mdf,const float mdf_param,float & glossiness)645         static bool mdf_param_to_glossiness(const string& mdf, const float mdf_param, float& glossiness)
646         {
647             if (mdf == "blinn")
648             {
649                 const BlinnExponentFunction f(mdf_param);
650                 return find_root_bisection(f, 0.0f, 1.0f, 1.0e-6f, 100, glossiness);
651             }
652             else
653             {
654                 glossiness = saturate(1.0f - mdf_param);
655                 return true;
656             }
657         }
658     };
659 
660 
661     //
662     // Update from revision 6 to revision 7.
663     //
664 
665     class UpdateFromRevision_6
666       : public Updater
667     {
668       public:
UpdateFromRevision_6(Project & project)669         explicit UpdateFromRevision_6(Project& project)
670           : Updater(project, 6)
671         {
672         }
673 
update()674         void update() override
675         {
676             // Here, we used to update render layer rules
677             // but render layers were removed in appleseed 1.7.0-beta.
678         }
679     };
680 
681 
682     //
683     // Update from revision 7 to revision 8.
684     //
685 
686     class UpdateFromRevision_7
687       : public Updater
688     {
689       public:
UpdateFromRevision_7(Project & project)690         explicit UpdateFromRevision_7(Project& project)
691           : Updater(project, 7)
692         {
693         }
694 
update()695         void update() override
696         {
697             if (Scene* scene = m_project.get_scene())
698                 update_collection(scene->assemblies());
699         }
700 
701       private:
702         template <typename Collection>
update_collection(Collection & collection)703         static void update_collection(Collection& collection)
704         {
705             for (each<Collection> i = collection; i; ++i)
706                 update_entity(*i);
707         }
708 
update_entity(Assembly & assembly)709         static void update_entity(Assembly& assembly)
710         {
711             update_collection(assembly.object_instances());
712             update_collection(assembly.assemblies());
713         }
714 
update_entity(ObjectInstance & object_instance)715         static void update_entity(ObjectInstance& object_instance)
716         {
717             const Object* object = object_instance.find_object();
718 
719             if (object && object->get_material_slot_count() == 1)
720             {
721                 const string slot_name = object->get_material_slot(0);
722 
723                 rebuild_material_mappings(object_instance.get_front_material_mappings(), slot_name);
724                 rebuild_material_mappings(object_instance.get_back_material_mappings(), slot_name);
725             }
726         }
727 
rebuild_material_mappings(StringDictionary & mappings,const string & slot_name)728         static void rebuild_material_mappings(StringDictionary& mappings, const string& slot_name)
729         {
730             if (!mappings.empty())
731             {
732                 const string material_name = mappings.begin().value();
733 
734                 mappings.clear();
735                 mappings.insert(slot_name, material_name);
736             }
737         }
738     };
739 
740 
741     //
742     // Update from revision 8 to revision 9.
743     //
744 
745     class UpdateFromRevision_8
746       : public Updater
747     {
748       public:
UpdateFromRevision_8(Project & project)749         explicit UpdateFromRevision_8(Project& project)
750           : Updater(project, 8)
751         {
752         }
753 
update()754         void update() override
755         {
756             if (Scene* scene = m_project.get_scene())
757                 rename_radiance_inputs(scene->assemblies());
758         }
759 
760       private:
rename_radiance_inputs(AssemblyContainer & assemblies)761         static void rename_radiance_inputs(AssemblyContainer& assemblies)
762         {
763             for (each<AssemblyContainer> i = assemblies; i; ++i)
764             {
765                 rename_radiance_inputs(*i);
766                 rename_radiance_inputs(i->assemblies());
767             }
768         }
769 
rename_radiance_inputs(Assembly & assembly)770         static void rename_radiance_inputs(Assembly& assembly)
771         {
772             for (each<LightContainer> i = assembly.lights(); i; ++i)
773                 rename_radiance_inputs(*i);
774         }
775 
rename_radiance_inputs(Light & light)776         static void rename_radiance_inputs(Light& light)
777         {
778             if (strcmp(light.get_model(), DirectionalLightFactory().get_model()) == 0)
779             {
780                 move_if_exist(light, "irradiance", "radiance");
781                 move_if_exist(light, "irradiance_multiplier", "radiance_multiplier");
782             }
783             else if (strcmp(light.get_model(), PointLightFactory().get_model()) == 0 ||
784                      strcmp(light.get_model(), SpotLightFactory().get_model()) == 0)
785             {
786                 move_if_exist(light, "intensity", "radiance");
787                 move_if_exist(light, "intensity_multiplier", "radiance_multiplier");
788             }
789         }
790     };
791 
792 
793     //
794     // Update from revision 9 to revision 10.
795     //
796 
797     class UpdateFromRevision_9
798       : public Updater
799     {
800       public:
UpdateFromRevision_9(Project & project)801         explicit UpdateFromRevision_9(Project& project)
802           : Updater(project, 9)
803         {
804         }
805 
update()806         void update() override
807         {
808             if (Scene* scene = m_project.get_scene())
809                 visit(scene->assemblies());
810         }
811 
812       private:
813         struct MaterialInfo
814         {
815             bool        m_updated;
816             Material*   m_material;
817             BSDF*       m_bsdf;
818             string      m_bsdf_reflectance;
819             string      m_bsdf_reflectance_multiplier;
820             string      m_bsdf_transmittance;
821             string      m_bsdf_transmittance_multiplier;
822             string      m_bsdf_fresnel_multiplier;
823             float       m_bsdf_from_ior;
824             float       m_bsdf_to_ior;
825             string      m_bssrdf;
826             string      m_edf;
827             string      m_alpha_map;
828             string      m_displacement_map;
829             string      m_displacement_method;
830             string      m_bump_amplitude;
831             string      m_normal_map_up;
832         };
833 
visit(AssemblyContainer & assemblies)834         static void visit(AssemblyContainer& assemblies)
835         {
836             for (each<AssemblyContainer> i = assemblies; i; ++i)
837                 visit(*i);
838         }
839 
visit(Assembly & assembly)840         static void visit(Assembly& assembly)
841         {
842             visit(assembly.assemblies());
843             update(assembly);
844         }
845 
update(Assembly & assembly)846         static void update(Assembly& assembly)
847         {
848             vector<MaterialInfo> materials;
849             collect_refractive_materials(assembly, materials);
850 
851             for (each<vector<MaterialInfo>> i = materials; i; ++i)
852             {
853                 if (i->m_updated)
854                     continue;
855 
856                 for (each<vector<MaterialInfo>> j = succ(i); j; ++j)
857                 {
858                     if (j->m_updated)
859                         continue;
860 
861                     MaterialInfo& mat1 = *i;
862                     MaterialInfo& mat2 = *j;
863 
864                     if (are_paired(mat1, mat2))
865                     {
866                         // Extract mat1 from the assembly.
867                         auto_release_ptr<Material> mat1_owner = assembly.materials().remove(mat1.m_material);
868 
869                         // Extract mat1's BSDF from the assembly.
870                         auto_release_ptr<BSDF> bsdf_owner = assembly.bsdfs().remove(mat1.m_bsdf);
871 
872                         // Update mat1's BSDF.
873                         // todo: make sure the BSDF is not used in another material.
874                         update_bsdf(mat1);
875                         assert(assembly.bsdfs().get_by_name(bsdf_owner->get_name()) == nullptr);
876 
877                         // Insert mat1's BSDF back into the assembly.
878                         assembly.bsdfs().insert(bsdf_owner);
879 
880                         // Rename mat1.
881                         const string old_mat1_name = mat1.m_material->get_name();
882                         cleanup_entity_name(*mat1.m_material);
883                         update_material_mappings(
884                             assembly.object_instances(),
885                             old_mat1_name.c_str(),
886                             mat1.m_material->get_name());
887                         update_material_mappings(
888                             assembly.object_instances(),
889                             mat2.m_material->get_name(),
890                             mat1.m_material->get_name());
891 
892                         // Insert mat1 back into the assembly.
893                         assembly.materials().insert(mat1_owner);
894 
895                         // Remove mat2.
896                         assembly.bsdfs().remove(mat2.m_bsdf);
897                         assembly.materials().remove(mat2.m_material);
898 
899                         mat1.m_updated = true;
900                         mat2.m_updated = true;
901                         break;
902                     }
903                 }
904 
905                 if (!i->m_updated)
906                 {
907                     // Extract the material's BSDF from the assembly.
908                     auto_release_ptr<BSDF> bsdf_owner = assembly.bsdfs().remove(i->m_bsdf);
909 
910                     // Update the material's BSDF.
911                     // todo: make sure the BSDF is not used in another material.
912                     update_bsdf(*i);
913 
914                     // Insert the material's BSDF back into the assembly.
915                     if (assembly.bsdfs().get_by_name(bsdf_owner->get_name()) == nullptr)
916                         assembly.bsdfs().insert(bsdf_owner);
917 
918                     i->m_updated = true;
919                 }
920             }
921         }
922 
collect_refractive_materials(Assembly & assembly,vector<MaterialInfo> & materials)923         static void collect_refractive_materials(Assembly& assembly, vector<MaterialInfo>& materials)
924         {
925             for (each<MaterialContainer> i = assembly.materials(); i; ++i)
926             {
927                 Material& material = *i;
928 
929                 if (strcmp(material.get_model(), "generic_material"))
930                     continue;
931 
932                 const ParamArray& material_params = material.get_parameters();
933 
934                 if (!material_params.strings().exist("bsdf"))
935                     continue;
936 
937                 const string bsdf_name = material_params.get<string>("bsdf");
938                 BSDF* bsdf = assembly.bsdfs().get_by_name(bsdf_name.c_str());
939 
940                 if (bsdf == nullptr)
941                     continue;
942 
943                 if (strcmp(bsdf->get_model(), "specular_btdf"))
944                     continue;
945 
946                 const ParamArray& bsdf_params = bsdf->get_parameters();
947 
948                 if (!bsdf_params.strings().exist("from_ior") ||
949                     !bsdf_params.strings().exist("to_ior") ||
950                     !bsdf_params.strings().exist("reflectance") ||
951                     !bsdf_params.strings().exist("transmittance"))
952                     continue;
953 
954                 // At this point we have found a material that needs to be updated.
955 
956                 MaterialInfo info;
957                 info.m_updated = false;
958                 info.m_material = &material;
959                 info.m_bsdf = bsdf;
960                 info.m_bsdf_reflectance = bsdf_params.get<string>("reflectance");
961                 info.m_bsdf_reflectance_multiplier = bsdf_params.get_optional<string>("reflectance_multiplier", "1.0");
962                 info.m_bsdf_transmittance = bsdf_params.get<string>("transmittance");
963                 info.m_bsdf_transmittance_multiplier = bsdf_params.get_optional<string>("transmittance_multiplier", "1.0");
964                 info.m_bsdf_fresnel_multiplier = bsdf_params.get_optional<string>("fresnel_multiplier", "1.0");
965                 info.m_bsdf_from_ior = bsdf_params.get<float>("from_ior");
966                 info.m_bsdf_to_ior = bsdf_params.get<float>("to_ior");
967                 info.m_bssrdf = material_params.get_optional<string>("bssrdf", "");
968                 info.m_edf = material_params.get_optional<string>("edf", "");
969                 info.m_alpha_map = material_params.get_optional<string>("alpha_map", "");
970                 info.m_displacement_map = material_params.get_optional<string>("displacement_map", "");
971                 info.m_displacement_method = material_params.get_optional<string>("displacement_method", "");
972                 info.m_bump_amplitude = material_params.get_optional<string>("bump_amplitude", "");
973                 info.m_normal_map_up = material_params.get_optional<string>("normal_map_up", "");
974 
975                 materials.push_back(info);
976             }
977         }
978 
are_paired(const MaterialInfo & lhs,const MaterialInfo & rhs)979         static bool are_paired(const MaterialInfo& lhs, const MaterialInfo& rhs)
980         {
981             assert(!lhs.m_updated && !rhs.m_updated);
982 
983             return
984                 lhs.m_bsdf_reflectance == rhs.m_bsdf_reflectance &&
985                 lhs.m_bsdf_reflectance_multiplier == rhs.m_bsdf_reflectance_multiplier &&
986                 lhs.m_bsdf_transmittance == rhs.m_bsdf_transmittance &&
987                 lhs.m_bsdf_transmittance_multiplier == rhs.m_bsdf_transmittance_multiplier &&
988                 lhs.m_bsdf_fresnel_multiplier == rhs.m_bsdf_fresnel_multiplier &&
989                 feq(lhs.m_bsdf_from_ior, rhs.m_bsdf_to_ior) &&
990                 feq(lhs.m_bsdf_to_ior, rhs.m_bsdf_from_ior) &&
991                 lhs.m_bssrdf == rhs.m_bssrdf &&
992                 lhs.m_edf == rhs.m_edf &&
993                 lhs.m_alpha_map == rhs.m_alpha_map &&
994                 lhs.m_displacement_map == rhs.m_displacement_map &&
995                 lhs.m_displacement_method == rhs.m_displacement_method &&
996                 lhs.m_bump_amplitude == rhs.m_bump_amplitude &&
997                 lhs.m_normal_map_up == rhs.m_normal_map_up;
998         }
999 
cleanup_entity_name(Entity & entity)1000         static void cleanup_entity_name(Entity& entity)
1001         {
1002             string name = entity.get_name();
1003             name = replace(name, "_front_", "");
1004             name = replace(name, "_front", "");
1005             name = replace(name, "front_", "");
1006             name = replace(name, "_back_", "");
1007             name = replace(name, "_back", "");
1008             name = replace(name, "back_", "");
1009             entity.set_name(name.c_str());
1010         }
1011 
update_material_mappings(ObjectInstanceContainer & object_instances,const char * old_material_name,const char * new_material_name)1012         static void update_material_mappings(
1013             ObjectInstanceContainer&    object_instances,
1014             const char*                 old_material_name,
1015             const char*                 new_material_name)
1016         {
1017             for (each<ObjectInstanceContainer> i = object_instances; i; ++i)
1018             {
1019                 update_material_mappings(i->get_front_material_mappings(), old_material_name, new_material_name);
1020                 update_material_mappings(i->get_back_material_mappings(), old_material_name, new_material_name);
1021             }
1022         }
1023 
update_material_mappings(StringDictionary & mappings,const char * old_material_name,const char * new_material_name)1024         static void update_material_mappings(
1025             StringDictionary&           mappings,
1026             const char*                 old_material_name,
1027             const char*                 new_material_name)
1028         {
1029             for (const_each<StringDictionary> i = mappings; i; ++i)
1030             {
1031                 if (strcmp(i->value(), old_material_name) == 0)
1032                     mappings.set(i->key(), new_material_name);
1033             }
1034         }
1035 
update_bsdf(const MaterialInfo & info)1036         static void update_bsdf(const MaterialInfo& info)
1037         {
1038             ParamArray& bsdf_params = info.m_bsdf->get_parameters();
1039 
1040             bsdf_params.strings().remove("from_ior");
1041             bsdf_params.strings().remove("to_ior");
1042 
1043             const float ior =
1044                 feq(info.m_bsdf_from_ior, 1.0f) ? info.m_bsdf_to_ior : info.m_bsdf_from_ior;
1045             bsdf_params.strings().insert("ior", ior);
1046 
1047             cleanup_entity_name(*info.m_bsdf);
1048             info.m_material->get_parameters().insert("bsdf", info.m_bsdf->get_name());
1049         }
1050     };
1051 
1052 
1053     //
1054     // Update from revision 10 to revision 11.
1055     //
1056 
1057     class UpdateFromRevision_10
1058       : public Updater
1059     {
1060       public:
UpdateFromRevision_10(Project & project)1061         explicit UpdateFromRevision_10(Project& project)
1062           : Updater(project, 10)
1063         {
1064         }
1065 
update()1066         void update() override
1067         {
1068             if (Scene* scene = m_project.get_scene())
1069                 update_bssrdf_ior_inputs(scene->assemblies());
1070         }
1071 
1072       private:
update_bssrdf_ior_inputs(AssemblyContainer & assemblies)1073         static void update_bssrdf_ior_inputs(AssemblyContainer& assemblies)
1074         {
1075             for (each<AssemblyContainer> i = assemblies; i; ++i)
1076             {
1077                 update_bssrdf_ior_inputs(*i);
1078                 update_bssrdf_ior_inputs(i->assemblies());
1079             }
1080         }
1081 
update_bssrdf_ior_inputs(Assembly & assembly)1082         static void update_bssrdf_ior_inputs(Assembly& assembly)
1083         {
1084             for (each<BSSRDFContainer> i = assembly.bssrdfs(); i; ++i)
1085                 update_bssrdf_ior_inputs(*i);
1086         }
1087 
update_bssrdf_ior_inputs(BSSRDF & bssrdf)1088         static void update_bssrdf_ior_inputs(BSSRDF& bssrdf)
1089         {
1090             move_if_exist(bssrdf, "ior", "inside_ior");
1091             bssrdf.get_parameters().remove_path("outside_ior");
1092         }
1093     };
1094 
1095 
1096     //
1097     // Update from revision 11 to revision 12.
1098     //
1099 
1100     class UpdateFromRevision_11
1101       : public Updater
1102     {
1103       public:
UpdateFromRevision_11(Project & project)1104         explicit UpdateFromRevision_11(Project& project)
1105           : Updater(project, 11)
1106         {
1107         }
1108 
update()1109         void update() override
1110         {
1111             if (Scene* scene = m_project.get_scene())
1112                 update_bssrdf_mfp_inputs(scene->assemblies());
1113         }
1114 
1115       private:
update_bssrdf_mfp_inputs(AssemblyContainer & assemblies)1116         static void update_bssrdf_mfp_inputs(AssemblyContainer& assemblies)
1117         {
1118             for (each<AssemblyContainer> i = assemblies; i; ++i)
1119             {
1120                 update_bssrdf_mfp_inputs(*i);
1121                 update_bssrdf_mfp_inputs(i->assemblies());
1122             }
1123         }
1124 
update_bssrdf_mfp_inputs(Assembly & assembly)1125         static void update_bssrdf_mfp_inputs(Assembly& assembly)
1126         {
1127             for (each<BSSRDFContainer> i = assembly.bssrdfs(); i; ++i)
1128                 update_bssrdf_mfp_inputs(*i);
1129         }
1130 
update_bssrdf_mfp_inputs(BSSRDF & bssrdf)1131         static void update_bssrdf_mfp_inputs(BSSRDF& bssrdf)
1132         {
1133             move_if_exist(bssrdf, "mfp", "dmfp");
1134             move_if_exist(bssrdf, "mfp_multiplier", "dmfp_multiplier");
1135         }
1136     };
1137 
1138 
1139     //
1140     // Update from revision 12 to revision 13.
1141     //
1142 
1143     class UpdateFromRevision_12
1144       : public Updater
1145     {
1146       public:
UpdateFromRevision_12(Project & project)1147         explicit UpdateFromRevision_12(Project& project)
1148           : Updater(project, 12)
1149         {
1150         }
1151 
update()1152         void update() override
1153         {
1154             Frame* frame = m_project.get_frame();
1155             const Scene* scene = m_project.get_scene();
1156 
1157             if (frame == nullptr || scene == nullptr || scene->cameras().empty())
1158                 return;
1159 
1160             ParamArray& frame_params = frame->get_parameters();
1161 
1162             if (!frame_params.strings().exist("camera"))
1163             {
1164                 // The frame does not reference any camera: use the first camera.
1165                 frame_params.insert(
1166                     "camera",
1167                     scene->cameras().get_by_index(0)->get_name());
1168             }
1169             else
1170             {
1171                 const char* camera_name = frame_params.strings().get("camera");
1172                 if (scene->cameras().get_by_name(camera_name) == nullptr)
1173                 {
1174                     // The frame references a non-existing camera: use the first camera.
1175                     frame_params.insert(
1176                         "camera",
1177                         scene->cameras().get_by_index(0)->get_name());
1178                 }
1179             }
1180         }
1181     };
1182 
1183 
1184     //
1185     // Update from revision 13 to revision 14.
1186     //
1187 
1188     class UpdateFromRevision_13
1189       : public Updater
1190     {
1191       public:
UpdateFromRevision_13(Project & project)1192         explicit UpdateFromRevision_13(Project& project)
1193           : Updater(project, 13)
1194         {
1195         }
1196 
update()1197         void update() override
1198         {
1199             if (Scene* scene = m_project.get_scene())
1200             {
1201                 update_bsdfs_inputs(scene->assemblies());
1202                 update_gaussian_bssrdfs(scene->assemblies());
1203             }
1204         }
1205 
1206       private:
update_bsdfs_inputs(AssemblyContainer & assemblies)1207         static void update_bsdfs_inputs(AssemblyContainer& assemblies)
1208         {
1209             for (each<AssemblyContainer> i = assemblies; i; ++i)
1210             {
1211                 update_bsdfs_inputs(*i);
1212                 update_bsdfs_inputs(i->assemblies());
1213             }
1214         }
1215 
update_bsdfs_inputs(Assembly & assembly)1216         static void update_bsdfs_inputs(Assembly& assembly)
1217         {
1218             for (each<BSDFContainer> i = assembly.bsdfs(); i; ++i)
1219                 update_bsdf_inputs(*i);
1220         }
1221 
update_bsdf_inputs(BSDF & bsdf)1222         static void update_bsdf_inputs(BSDF& bsdf)
1223         {
1224             if (strcmp(bsdf.get_model(), GlassBSDFFactory().get_model()) == 0)
1225             {
1226                 bsdf.get_parameters().insert("volume_parameterization", "transmittance");
1227                 move_if_exist(bsdf, "anisotropy", "anisotropic");
1228             }
1229             else if (strcmp(bsdf.get_model(), GlossyBRDFFactory().get_model()) == 0 ||
1230                      strcmp(bsdf.get_model(), MetalBRDFFactory().get_model()) == 0)
1231             {
1232                 move_if_exist(bsdf, "anisotropy", "anisotropic");
1233             }
1234             else if (strcmp(bsdf.get_model(), SpecularBTDFFactory().get_model()) == 0)
1235             {
1236                 move_if_exist(bsdf, "volume_density", "density");
1237                 move_if_exist(bsdf, "volume_scale", "scale");
1238             }
1239         }
1240 
update_gaussian_bssrdfs(AssemblyContainer & assemblies)1241         static void update_gaussian_bssrdfs(AssemblyContainer& assemblies)
1242         {
1243             for (each<AssemblyContainer> i = assemblies; i; ++i)
1244             {
1245                 update_gaussian_bssrdfs(*i);
1246                 update_gaussian_bssrdfs(i->assemblies());
1247             }
1248         }
1249 
update_gaussian_bssrdfs(Assembly & assembly)1250         static void update_gaussian_bssrdfs(Assembly& assembly)
1251         {
1252             for (each<BSSRDFContainer> i = assembly.bssrdfs(); i; ++i)
1253             {
1254                 if (strcmp(i->get_model(), GaussianBSSRDFFactory().get_model()) == 0)
1255                     update_gaussian_bssrdf(*i);
1256             }
1257         }
1258 
update_gaussian_bssrdf(BSSRDF & bssrdf)1259         static void update_gaussian_bssrdf(BSSRDF& bssrdf)
1260         {
1261             ParamArray& params = bssrdf.get_parameters();
1262 
1263             try
1264             {
1265                 const float v = params.get<float>("v");
1266                 const float mfp = sqrt(v * 16.0f) / 7.0f;
1267                 params.insert("mfp", mfp);
1268                 params.remove_path("v");
1269             }
1270             catch (const Exception&)
1271             {
1272                 RENDERER_LOG_ERROR(
1273                     "while updating gaussian bssrdf \"%s\": failed to convert v parameter.",
1274                     bssrdf.get_name());
1275             }
1276         }
1277     };
1278 
1279 
1280     //
1281     // Update from revision 14 to revision 15.
1282     //
1283 
1284     class UpdateFromRevision_14
1285       : public Updater
1286     {
1287       public:
UpdateFromRevision_14(Project & project)1288         explicit UpdateFromRevision_14(Project& project)
1289           : Updater(project, 14)
1290         {
1291         }
1292 
update()1293         void update() override
1294         {
1295             if (Scene* scene = m_project.get_scene())
1296                 update_entities(scene->assemblies());
1297         }
1298 
1299       private:
update_entities(AssemblyContainer & assemblies)1300         static void update_entities(AssemblyContainer& assemblies)
1301         {
1302             for (each<AssemblyContainer> i = assemblies; i; ++i)
1303             {
1304                 update_entities(*i);
1305                 update_entities(i->assemblies());
1306             }
1307         }
1308 
update_entities(Assembly & assembly)1309         static void update_entities(Assembly& assembly)
1310         {
1311             for (each<BSDFContainer> i = assembly.bsdfs(); i; ++i)
1312                 update_bsdf_inputs(*i);
1313 
1314             for (each<MaterialContainer> i = assembly.materials(); i; ++i)
1315                 update_material_inputs(*i);
1316         }
1317 
update_bsdf_inputs(BSDF & bsdf)1318         static void update_bsdf_inputs(BSDF& bsdf)
1319         {
1320             if (strcmp(bsdf.get_model(), DisneyBRDFFactory().get_model()) == 0)
1321             {
1322                 ParamArray& params = bsdf.get_parameters();
1323                 if (!params.strings().exist("specular"))
1324                     params.insert("specular", 0.5f);
1325                 if (!params.strings().exist("roughness"))
1326                     params.insert("roughness", 0.5f);
1327                 if (!params.strings().exist("sheen_tint"))
1328                     params.insert("sheen_tint", 0.5f);
1329             }
1330         }
1331 
update_material_inputs(Material & material)1332         static void update_material_inputs(Material& material)
1333         {
1334             // Don't rely on DisneyMaterialFactory().get_model() because appleseed needs
1335             // to be able to update projects even when built without Disney material support
1336             // (i.e. the APPLESEED_WITH_DISNEY_MATERIAL preprocessor symbol is undefined).
1337             if (strcmp(material.get_model(), "disney_material") == 0)
1338             {
1339                 ParamArray& params = material.get_parameters();
1340                 for (each<DictionaryDictionary> i = params.dictionaries(); i; ++i)
1341                 {
1342                     Dictionary& layer_params = i->value();
1343                     if (!layer_params.strings().exist("roughness"))
1344                         layer_params.insert("roughness", 0.5f);
1345                 }
1346             }
1347         }
1348     };
1349 
1350 
1351     //
1352     // Update from revision 15 to revision 16.
1353     //
1354 
1355     class UpdateFromRevision_15
1356       : public Updater
1357     {
1358       public:
UpdateFromRevision_15(Project & project)1359         explicit UpdateFromRevision_15(Project& project)
1360           : Updater(project, 15)
1361         {
1362         }
1363 
update()1364         void update() override
1365         {
1366             if (Scene* scene = m_project.get_scene())
1367                 update_physical_surface_shader_inputs(scene->assemblies());
1368         }
1369 
1370       private:
update_physical_surface_shader_inputs(AssemblyContainer & assemblies)1371         static void update_physical_surface_shader_inputs(AssemblyContainer& assemblies)
1372         {
1373             for (each<AssemblyContainer> i = assemblies; i; ++i)
1374             {
1375                 update_physical_surface_shader_inputs(*i);
1376                 update_physical_surface_shader_inputs(i->assemblies());
1377             }
1378         }
1379 
update_physical_surface_shader_inputs(Assembly & assembly)1380         static void update_physical_surface_shader_inputs(Assembly& assembly)
1381         {
1382             for (each<SurfaceShaderContainer> i = assembly.surface_shaders(); i; ++i)
1383                 update_physical_surface_shader_inputs(*i);
1384         }
1385 
update_physical_surface_shader_inputs(SurfaceShader & surface_shader)1386         static void update_physical_surface_shader_inputs(SurfaceShader& surface_shader)
1387         {
1388             if (strcmp(surface_shader.get_model(), PhysicalSurfaceShaderFactory().get_model()) == 0)
1389             {
1390                 move_if_exist(surface_shader, "lighting_samples", "front_lighting_samples");
1391 
1392                 ParamArray& params = surface_shader.get_parameters();
1393                 params.strings().remove("translucency");
1394                 params.strings().remove("back_lighting_samples");
1395                 params.strings().remove("aerial_persp_sky_color");
1396                 params.strings().remove("aerial_persp_mode");
1397                 params.strings().remove("aerial_persp_distance");
1398                 params.strings().remove("aerial_persp_intensity");
1399             }
1400         }
1401     };
1402 
1403 
1404     //
1405     // Update from revision 16 to revision 17.
1406     //
1407 
1408     class UpdateFromRevision_16
1409       : public Updater
1410     {
1411       public:
UpdateFromRevision_16(Project & project)1412         explicit UpdateFromRevision_16(Project& project)
1413           : Updater(project, 16)
1414         {
1415         }
1416 
update()1417         void update() override
1418         {
1419             for (each<ConfigurationContainer> i = m_project.configurations(); i; ++i)
1420                 update(*i);
1421         }
1422 
1423       private:
update(Configuration & configuration)1424         static void update(Configuration& configuration)
1425         {
1426             ParamArray& params = configuration.get_parameters();
1427 
1428             if (params.get_optional<string>("lighting_engine") == "drt")
1429             {
1430                 // The project was using DRT: switch to PT.
1431                 params.insert_path("lighting_engine", "pt");
1432 
1433                 // If they exist, replace DRT parameters by PT ones.
1434                 if (params.dictionaries().exist("drt"))
1435                     params.insert("pt", params.child("drt"));
1436 
1437                 // Set PT parameters to emulate DRT.
1438                 params.insert_path("pt.max_diffuse_bounces", 0);
1439             }
1440 
1441             // Remove DRT parameters.
1442             if (params.dictionaries().exist("drt"))
1443                 params.dictionaries().remove("drt");
1444         }
1445     };
1446 
1447 
1448     //
1449     // Update from revision 17 to revision 18.
1450     //
1451 
1452     class UpdateFromRevision_17
1453       : public Updater
1454     {
1455       public:
UpdateFromRevision_17(Project & project)1456         explicit UpdateFromRevision_17(Project& project)
1457           : Updater(project, 17)
1458         {
1459         }
1460 
update()1461         void update() override
1462         {
1463             for (each<ConfigurationContainer> i = m_project.configurations(); i; ++i)
1464                 update(*i);
1465         }
1466 
1467       private:
update(Configuration & configuration)1468         static void update(Configuration& configuration)
1469         {
1470             ParamArray& params = configuration.get_parameters();
1471 
1472             if (params.dictionaries().exist("pt"))
1473             {
1474                 Dictionary& pt_params = params.dictionary("pt");
1475                 convert_max_path_length_to_max_bounces(pt_params, "max_path_length", "max_bounces");
1476             }
1477 
1478             if (params.dictionaries().exist("sppm"))
1479             {
1480                 Dictionary& sppm_params = params.dictionary("sppm");
1481                 convert_max_path_length_to_max_bounces(sppm_params, "photon_tracing_max_path_length", "photon_tracing_max_bounces");
1482                 convert_max_path_length_to_max_bounces(sppm_params, "path_tracing_max_path_length", "path_tracing_max_bounces");
1483             }
1484 
1485             if (params.dictionaries().exist("lighttracing"))
1486             {
1487                 Dictionary& lt_params = params.dictionary("lighttracing");
1488                 convert_max_path_length_to_max_bounces(lt_params, "max_path_length", "max_bounces");
1489             }
1490         }
1491 
convert_max_path_length_to_max_bounces(Dictionary & params,const char * max_path_length_param_name,const char * max_bounces_param_name)1492         static void convert_max_path_length_to_max_bounces(
1493             Dictionary& params,
1494             const char* max_path_length_param_name,
1495             const char* max_bounces_param_name)
1496         {
1497             if (params.strings().exist(max_path_length_param_name))
1498             {
1499                 const size_t max_path_length = params.get<size_t>(max_path_length_param_name);
1500                 const int max_bounces =
1501                     max_path_length == 0
1502                         ? -1
1503                         : static_cast<int>(max_path_length - 1);
1504 
1505                 params.strings().remove(max_path_length_param_name);
1506                 params.insert(max_bounces_param_name, max_bounces);
1507             }
1508         }
1509     };
1510 
1511 
1512     //
1513     // Update from revision 18 to revision 19.
1514     //
1515 
1516     class UpdateFromRevision_18
1517       : public Updater
1518     {
1519       public:
UpdateFromRevision_18(Project & project)1520         explicit UpdateFromRevision_18(Project& project)
1521           : Updater(project, 18)
1522         {
1523         }
1524 
update()1525         void update() override
1526         {
1527             if (m_project.get_frame())
1528                 update_frame(*m_project.get_frame());
1529         }
1530 
1531       private:
update_frame(Frame & frame)1532         static void update_frame(Frame& frame)
1533         {
1534             ParamArray& params = frame.get_parameters();
1535             params.remove_path("pixel_format");
1536             params.remove_path("color_space");
1537             params.remove_path("gamma_correction");
1538             params.remove_path("clamping");
1539             params.remove_path("premultiplied_alpha");
1540         }
1541     };
1542 
1543 
1544     //
1545     // Update from revision 19 to revision 20.
1546     //
1547 
1548     class UpdateFromRevision_19
1549       : public Updater
1550     {
1551       public:
UpdateFromRevision_19(Project & project)1552         explicit UpdateFromRevision_19(Project& project)
1553           : Updater(project, 19)
1554         {
1555         }
1556 
update()1557         void update() override
1558         {
1559             if (Scene* scene = m_project.get_scene())
1560                 update_material_and_object_inputs(scene->assemblies());
1561         }
1562 
1563       private:
update_material_and_object_inputs(AssemblyContainer & assemblies)1564         static void update_material_and_object_inputs(AssemblyContainer& assemblies)
1565         {
1566             for (each<AssemblyContainer> i = assemblies; i; ++i)
1567             {
1568                 update_material_and_object_inputs(*i);
1569                 update_material_and_object_inputs(i->assemblies());
1570             }
1571         }
1572 
update_material_and_object_inputs(Assembly & assembly)1573         static void update_material_and_object_inputs(Assembly& assembly)
1574         {
1575             for (each<MaterialContainer> i = assembly.materials(); i; ++i)
1576                 remove_shade_alpha_cutouts(*i);
1577 
1578             for (each<ObjectContainer> i = assembly.objects(); i; ++i)
1579                 remove_shade_alpha_cutouts(*i);
1580         }
1581 
1582         template <typename EntityType>
remove_shade_alpha_cutouts(EntityType & entity)1583         static void remove_shade_alpha_cutouts(EntityType& entity)
1584         {
1585             ParamArray& params = entity.get_parameters();
1586             params.remove_path("shade_alpha_cutouts");
1587         }
1588     };
1589 
1590 
1591     //
1592     // Update from revision 20 to revision 21.
1593     //
1594 
1595     class UpdateFromRevision_20
1596       : public Updater
1597     {
1598       public:
UpdateFromRevision_20(Project & project)1599         explicit UpdateFromRevision_20(Project& project)
1600           : Updater(project, 20)
1601         {
1602         }
1603 
update()1604         void update() override
1605         {
1606             if (Scene* scene = m_project.get_scene())
1607                 update_physical_surface_shader_inputs(scene->assemblies());
1608         }
1609 
1610       private:
update_physical_surface_shader_inputs(AssemblyContainer & assemblies)1611         static void update_physical_surface_shader_inputs(AssemblyContainer& assemblies)
1612         {
1613             for (each<AssemblyContainer> i = assemblies; i; ++i)
1614             {
1615                 update_physical_surface_shader_inputs(*i);
1616                 update_physical_surface_shader_inputs(i->assemblies());
1617             }
1618         }
1619 
update_physical_surface_shader_inputs(Assembly & assembly)1620         static void update_physical_surface_shader_inputs(Assembly& assembly)
1621         {
1622             for (each<SurfaceShaderContainer> i = assembly.surface_shaders(); i; ++i)
1623                 update_physical_surface_shader_inputs(*i);
1624         }
1625 
update_physical_surface_shader_inputs(SurfaceShader & surface_shader)1626         static void update_physical_surface_shader_inputs(SurfaceShader& surface_shader)
1627         {
1628             if (strcmp(surface_shader.get_model(), PhysicalSurfaceShaderFactory().get_model()) == 0)
1629             {
1630                 ParamArray& params = surface_shader.get_parameters();
1631                 params.strings().remove("color_multiplier");
1632                 params.strings().remove("alpha_multiplier");
1633             }
1634         }
1635     };
1636 
1637 
1638     //
1639     // Update from revision 21 to revision 22.
1640     //
1641 
1642     class UpdateFromRevision_21
1643       : public Updater
1644     {
1645       public:
UpdateFromRevision_21(Project & project)1646         explicit UpdateFromRevision_21(Project& project)
1647           : Updater(project, 21)
1648         {
1649         }
1650 
update()1651         void update() override
1652         {
1653             if (Scene* scene = m_project.get_scene())
1654             {
1655                 for (each<CameraContainer> i = scene->cameras(); i; ++i)
1656                 {
1657                     Dictionary& camera_params = i->get_parameters();
1658 
1659                     copy_if_exist_no_overwrite(
1660                         camera_params,
1661                         "shutter_open_end_time",
1662                         "shutter_open_time");
1663 
1664                     copy_if_exist_no_overwrite(
1665                         camera_params,
1666                         "shutter_close_start_time",
1667                         "shutter_close_time");
1668                 }
1669             }
1670         }
1671     };
1672 
1673 
1674     //
1675     // Update from revision 22 to revision 23.
1676     //
1677 
1678     class UpdateFromRevision_22
1679       : public Updater
1680     {
1681       public:
UpdateFromRevision_22(Project & project)1682         explicit UpdateFromRevision_22(Project& project)
1683           : Updater(project, 22)
1684         {
1685         }
1686 
update()1687         void update() override
1688         {
1689             if (Scene* scene = m_project.get_scene())
1690             {
1691                 for (each<CameraContainer> i = scene->cameras(); i; ++i)
1692                 {
1693                     Dictionary& camera_params = i->get_parameters();
1694 
1695                     if (camera_params.strings().exist("autofocus_target"))
1696                         camera_params.strings().insert("autofocus_enabled", true);
1697                     else
1698                     {
1699                         // camera_params include "focal_distance".
1700                         camera_params.strings().insert("autofocus_enabled", false);
1701                     }
1702                 }
1703             }
1704         }
1705     };
1706 
1707 
1708     //
1709     // Update from revision 23 to revision 24.
1710     //
1711 
1712     class UpdateFromRevision_23
1713       : public Updater
1714     {
1715       public:
UpdateFromRevision_23(Project & project)1716         explicit UpdateFromRevision_23(Project& project)
1717           : Updater(project, 23)
1718         {
1719         }
1720 
update()1721         void update() override
1722         {
1723             for (each<ConfigurationContainer> i = m_project.configurations(); i; ++i)
1724                 update(*i);
1725         }
1726 
1727       private:
update(Configuration & configuration)1728         static void update(Configuration& configuration)
1729         {
1730             ParamArray& params = configuration.get_parameters();
1731 
1732             if (params.dictionaries().exist("pt"))
1733             {
1734                 Dictionary& pt_params = params.dictionary("pt");
1735 
1736                 if (!pt_params.strings().exist("max_bounces"))
1737                     pt_params.insert("max_bounces", -1);
1738 
1739                 if (!pt_params.strings().exist("max_diffuse_bounces"))
1740                     pt_params.insert("max_diffuse_bounces", -1);
1741 
1742                 if (!pt_params.strings().exist("max_glossy_bounces"))
1743                     pt_params.insert("max_glossy_bounces", -1);
1744 
1745                 if (!pt_params.strings().exist("max_specular_bounces"))
1746                     pt_params.insert("max_specular_bounces", -1);
1747             }
1748             else
1749             {
1750                 params.insert("pt", ParamArray()
1751                     .insert("max_bounces", -1)
1752                     .insert("max_diffuse_bounces", -1)
1753                     .insert("max_glossy_bounces", -1)
1754                     .insert("max_specular_bounces", -1));
1755             }
1756         }
1757     };
1758 
1759 
1760     //
1761     // Update from revision 24 to revision 25.
1762     //
1763 
1764     class UpdateFromRevision_24
1765       : public Updater
1766     {
1767       public:
UpdateFromRevision_24(Project & project)1768         explicit UpdateFromRevision_24(Project& project)
1769           : Updater(project, 24)
1770         {
1771         }
1772 
update()1773         void update() override
1774         {
1775             if (Scene* scene = m_project.get_scene())
1776             {
1777                 for (each<CameraContainer> i = scene->cameras(); i; ++i)
1778                 {
1779                     ParamArray& camera_params = i->get_parameters();
1780                     move_if_exist(camera_params, "shutter_open_begin_time", "shutter_open_time");
1781                     move_if_exist(camera_params, "shutter_close_begin_time", "shutter_close_start_time");
1782                     move_if_exist(camera_params, "shutter_close_end_time", "shutter_close_time");
1783                 }
1784             }
1785         }
1786     };
1787 
1788 
1789     //
1790     // Update from revision 25 to revision 26.
1791     //
1792 
1793     class UpdateFromRevision_25
1794       : public Updater
1795     {
1796       public:
UpdateFromRevision_25(Project & project)1797         explicit UpdateFromRevision_25(Project& project)
1798           : Updater(project, 25)
1799         {
1800         }
1801 
update()1802         void update() override
1803         {
1804             if (m_project.get_frame())
1805                 update_frame(*m_project.get_frame());
1806         }
1807 
1808       private:
update_frame(Frame & frame)1809         static void update_frame(Frame& frame)
1810         {
1811             ParamArray& params = frame.get_parameters();
1812 
1813             if (params.get_optional<bool>("enable_render_stamp"))
1814             {
1815                 // There cannot be any other post-processing stage at this point.
1816                 assert(frame.post_processing_stages().empty());
1817 
1818                 const string format_string = params.get_optional<string>("render_stamp_format");
1819                 frame.post_processing_stages().insert(
1820                     RenderStampPostProcessingStageFactory().create(
1821                         "render_stamp",
1822                         ParamArray()
1823                             .insert("order", 0)
1824                             .insert("format_string", format_string)));
1825             }
1826 
1827             params.remove_path("enable_render_stamp");
1828             params.remove_path("render_stamp_format");
1829         }
1830     };
1831 
1832 
1833     //
1834     // Update from revision 26 to revision 27.
1835     //
1836 
1837     class UpdateFromRevision_26
1838       : public Updater
1839     {
1840       public:
UpdateFromRevision_26(Project & project)1841         explicit UpdateFromRevision_26(Project& project)
1842           : Updater(project, 26)
1843         {
1844         }
1845 
update()1846         void update() override
1847         {
1848             remove_diagnostic_option();
1849             update_passes_path();
1850         }
1851 
1852       private:
1853         // Remove pixel_renderer::enable_diagnostics and frame::save_extra_aovs.
remove_diagnostic_option()1854         void remove_diagnostic_option()
1855         {
1856             for (Configuration& config : m_project.configurations())
1857             {
1858                 Dictionary& root = config.get_parameters();
1859 
1860                 if (root.dictionaries().exist("uniform_pixel_renderer"))
1861                 {
1862                     Dictionary& upr = root.dictionary("uniform_pixel_renderer");
1863                     upr.strings().remove("enable_diagnostics");
1864                 }
1865 
1866                 if (root.dictionaries().exist("adaptive_pixel_renderer"))
1867                 {
1868                     Dictionary& apr = root.dictionary("adaptive_pixel_renderer");
1869                     apr.strings().remove("enable_diagnostics");
1870                 }
1871             }
1872 
1873             Frame* frame = m_project.get_frame();
1874 
1875             if (frame == nullptr)
1876                 return;
1877 
1878             ParamArray& frame_params = frame->get_parameters();
1879             frame_params.strings().remove("save_extra_aovs");
1880         }
1881 
1882         // Move generic_frame_renderer::passes to the root configuration.
update_passes_path()1883         void update_passes_path()
1884         {
1885             for (Configuration& config : m_project.configurations())
1886             {
1887                 Dictionary& root = config.get_parameters();
1888 
1889                 if (root.dictionaries().exist("generic_frame_renderer"))
1890                 {
1891                     Dictionary& gfr = root.dictionary("generic_frame_renderer");
1892 
1893                     move_if_exist(root, "passes", gfr, "passes");
1894 
1895                     // Remove the dictionnary from the root if it's empty.
1896                     if (gfr.empty())
1897                         root.dictionaries().remove("generic_frame_renderer");
1898                 }
1899             }
1900         }
1901     };
1902 
1903 
1904     //
1905     // Update from revision 27 to revision 28.
1906     //
1907 
1908     class UpdateFromRevision_27
1909       : public Updater
1910     {
1911       public:
UpdateFromRevision_27(Project & project)1912         explicit UpdateFromRevision_27(Project& project)
1913           : Updater(project, 27)
1914         {
1915         }
1916 
update()1917         void update() override
1918         {
1919             if (Scene* scene = m_project.get_scene())
1920                 update_assembly_inputs(scene->assemblies());
1921         }
1922 
1923       private:
update_assembly_inputs(AssemblyContainer & assemblies)1924         static void update_assembly_inputs(AssemblyContainer& assemblies)
1925         {
1926             for (each<AssemblyContainer> i = assemblies; i; ++i)
1927             {
1928                 update_assembly_inputs(*i);
1929                 update_assembly_inputs(i->assemblies());
1930             }
1931         }
1932 
update_assembly_inputs(Assembly & assembly)1933         static void update_assembly_inputs(Assembly& assembly)
1934         {
1935             assembly.get_parameters().remove_path("flushable");
1936         }
1937     };
1938 
1939 
1940     //
1941     // Update from revision 28 to revision 29.
1942     //
1943 
1944     class UpdateFromRevision_28
1945       : public Updater
1946     {
1947       public:
UpdateFromRevision_28(Project & project)1948         explicit UpdateFromRevision_28(Project& project)
1949           : Updater(project, 28)
1950         {
1951         }
1952 
update()1953         void update() override
1954         {
1955             if (m_project.get_frame())
1956                 update_frame(*m_project.get_frame());
1957         }
1958 
1959       private:
update_frame(Frame & frame)1960         static void update_frame(Frame& frame)
1961         {
1962             for (PostProcessingStage& stage : frame.post_processing_stages())
1963             {
1964                 if (strcmp(stage.get_model(), RenderStampPostProcessingStageFactory().get_model()) == 0)
1965                 {
1966                     ParamArray& params = stage.get_parameters();
1967                     if (params.strings().exist("format_string"))
1968                     {
1969                         string format_string = params.get<string>("format_string");
1970                         format_string = replace(format_string, "{lib-variant}", "{lib-cpu-features}");
1971                         params.set("format_string", format_string);
1972                     }
1973                 }
1974             }
1975         }
1976     };
1977 
1978 
1979     //
1980     // Update from revision 29 to revision 30.
1981     //
1982 
1983     class UpdateFromRevision_29
1984       : public Updater
1985     {
1986       public:
UpdateFromRevision_29(Project & project)1987         explicit UpdateFromRevision_29(Project& project)
1988           : Updater(project, 29)
1989         {
1990         }
1991 
update()1992         void update() override
1993         {
1994             remove_adaptive_pixel_renderer_settings();
1995             remove_decorrelate_pixels_setting();
1996 
1997             if (m_project.get_frame())
1998                 update_frame_filter(*m_project.get_frame());
1999         }
2000 
2001       private:
update_frame_filter(Frame & frame)2002         static void update_frame_filter(Frame& frame)
2003         {
2004             const char* DefaultFilterName = "blackman-harris";
2005 
2006             ParamArray& params = frame.get_parameters();
2007             const string filter_name = params.get_optional<string>("filter", DefaultFilterName);
2008 
2009             const bool update_filter =
2010                 filter_name == "mitchell" ||
2011                 filter_name == "bspline" ||
2012                 filter_name == "catmull" ||
2013                 filter_name == "lanczos";
2014 
2015             if (update_filter)
2016             {
2017                 RENDERER_LOG_WARNING(
2018                     "with the introduction of filter importance sampling, some reconstruction filters were removed; "
2019                     "migrating this project to use the default reconstruction filter instead (%s).",
2020                     DefaultFilterName);
2021 
2022                 params.insert_path("filter", DefaultFilterName);
2023             }
2024         }
2025 
remove_decorrelate_pixels_setting()2026         void remove_decorrelate_pixels_setting()
2027         {
2028             for (Configuration& config : m_project.configurations())
2029             {
2030                 Dictionary& root = config.get_parameters();
2031 
2032                 if (root.dictionaries().exist("uniform_pixel_renderer"))
2033                 {
2034                     Dictionary& d = root.dictionary("uniform_pixel_renderer");
2035 
2036                     if (d.strings().exist("decorrelate_pixels"))
2037                     {
2038                         if (d.strings().get<bool>("decorrelate_pixels") == false)
2039                         {
2040                             RENDERER_LOG_WARNING(
2041                                 "with the introduction of filter importance sampling, the option to disable pixel decorrelation was removed; "
2042                                 "migrating this project to use pixel decorrelation instead.");
2043                         }
2044 
2045                         d.strings().remove("decorrelate_pixels");
2046                     }
2047                 }
2048             }
2049         }
2050 
remove_adaptive_pixel_renderer_settings()2051         void remove_adaptive_pixel_renderer_settings()
2052         {
2053             for (Configuration& config : m_project.configurations())
2054             {
2055                 Dictionary& root = config.get_parameters();
2056 
2057                 if (root.dictionaries().exist("adaptive_pixel_renderer"))
2058                     root.dictionaries().remove("adaptive_pixel_renderer");
2059 
2060                 if (root.strings().exist("pixel_renderer"))
2061                 {
2062                     const char* pixel_renderer = root.strings().get("pixel_renderer");
2063                     if (strcmp(pixel_renderer, "adaptive") == 0)
2064                     {
2065                         RENDERER_LOG_WARNING(
2066                             "with the introduction of a new adaptive tile renderer, the adaptive pixel renderer was removed; "
2067                             "migrating this project to use the uniform pixel renderer instead.");
2068 
2069                         root.strings().set("pixel_renderer", "uniform");
2070                     }
2071                 }
2072             }
2073         }
2074     };
2075 
2076 
2077     //
2078     // Update from revision 30 to revision 31.
2079     //
2080 
2081     class UpdateFromRevision_30
2082       : public Updater
2083     {
2084       public:
UpdateFromRevision_30(Project & project)2085         explicit UpdateFromRevision_30(Project& project)
2086           : Updater(project, 30)
2087         {
2088         }
2089 
update()2090         void update() override
2091         {
2092             replace_max_samples_interactive_renderer_setting();
2093 
2094             if (Scene* scene = m_project.get_scene())
2095                 update_assemblies(scene->assemblies());
2096         }
2097 
2098       private:
replace_max_samples_interactive_renderer_setting()2099         void replace_max_samples_interactive_renderer_setting()
2100         {
2101             for (Configuration& config : m_project.configurations())
2102             {
2103                 Dictionary& root = config.get_parameters();
2104 
2105                 if (root.dictionaries().exist("progressive_frame_renderer"))
2106                 {
2107                     Dictionary& pfr = root.dictionaries().get("progressive_frame_renderer");
2108 
2109                     if (pfr.strings().exist("max_samples"))
2110                     {
2111                         const uint64 max_samples = pfr.strings().get<uint64>("max_samples");
2112                         pfr.strings().remove("max_samples");
2113 
2114                         if (max_samples < numeric_limits<uint64>::max())
2115                         {
2116                             Frame* frame = m_project.get_frame();
2117                             if (frame)
2118                             {
2119                                 // If max samples was previously set then preserve the nearest max average spp count.
2120                                 const uint64 pixel_count = frame->get_crop_window().volume();
2121                                 const uint64 max_average_spp =
2122                                     static_cast<uint64>(
2123                                         ceil(
2124                                             static_cast<double>(max_samples) / pixel_count));
2125                                 pfr.strings().insert("max_average_spp", max_average_spp);
2126                             }
2127                         }
2128                     }
2129                 }
2130             }
2131         }
2132 
update_assemblies(const AssemblyContainer & assemblies)2133         static void update_assemblies(const AssemblyContainer& assemblies)
2134         {
2135             for (const auto& assembly : assemblies)
2136             {
2137                 update_bsdfs(assembly.bsdfs());
2138                 update_assemblies(assembly.assemblies());
2139             }
2140         }
2141 
update_bsdfs(BSDFContainer & bsdfs)2142         static void update_bsdfs(BSDFContainer& bsdfs)
2143         {
2144             for (auto& bsdf : bsdfs)
2145             {
2146                 if (strcmp(bsdf.get_model(), "glass_bsdf") == 0)
2147                     update_microfacet_params(bsdf);
2148                 else if (strcmp(bsdf.get_model(), "metal_brdf") == 0)
2149                     update_microfacet_params(bsdf);
2150                 else if (strcmp(bsdf.get_model(), "glossy_brdf") == 0)
2151                     update_microfacet_params(bsdf);
2152                 else if (strcmp(bsdf.get_model(), "plastic_brdf") == 0)
2153                     update_microfacet_params(bsdf);
2154             }
2155         }
2156 
update_microfacet_params(BSDF & bsdf)2157         static void update_microfacet_params(BSDF& bsdf)
2158         {
2159             ParamArray& params = bsdf.get_parameters();
2160 
2161             if (params.strings().exist("mdf"))
2162             {
2163                 const string mdf = params.strings().get<string>("mdf");
2164                 params.strings().remove("mdf");
2165                 if (mdf != "ggx")
2166                 {
2167                     RENDERER_LOG_WARNING(
2168                         "the %s microfacet distribution used by BSDF \"%s\" was removed; "
2169                         "the GGX distribution will be used instead.",
2170                         mdf.c_str(),
2171                         bsdf.get_name());
2172                 }
2173             }
2174 
2175             params.strings().remove("highlight_falloff");
2176         }
2177     };
2178 }
2179 
update(Project & project,const size_t to_revision)2180 bool ProjectFileUpdater::update(
2181     Project&        project,
2182     const size_t    to_revision)
2183 {
2184     EventCounters event_counters;
2185     update(project, event_counters, to_revision);
2186     return event_counters.get_error_count() == 0;
2187 }
2188 
update(Project & project,EventCounters & event_counters,const size_t to_revision)2189 void ProjectFileUpdater::update(
2190     Project&        project,
2191     EventCounters&  event_counters,
2192     const size_t    to_revision)
2193 {
2194     size_t format_revision = project.get_format_revision();
2195 
2196 #define CASE_UPDATE_FROM_REVISION(from)             \
2197     case from:                                      \
2198     {                                               \
2199         if (format_revision >= to_revision)         \
2200             break;                                  \
2201                                                     \
2202         UpdateFromRevision_##from updater(project); \
2203         updater.update();                           \
2204                                                     \
2205         format_revision = from + 1;                 \
2206     }
2207 
2208     switch (format_revision)
2209     {
2210       CASE_UPDATE_FROM_REVISION(0);
2211       CASE_UPDATE_FROM_REVISION(1);
2212       CASE_UPDATE_FROM_REVISION(2);
2213       CASE_UPDATE_FROM_REVISION(3);
2214       CASE_UPDATE_FROM_REVISION(4);
2215       CASE_UPDATE_FROM_REVISION(5);
2216       CASE_UPDATE_FROM_REVISION(6);
2217       CASE_UPDATE_FROM_REVISION(7);
2218       CASE_UPDATE_FROM_REVISION(8);
2219       CASE_UPDATE_FROM_REVISION(9);
2220       CASE_UPDATE_FROM_REVISION(10);
2221       CASE_UPDATE_FROM_REVISION(11);
2222       CASE_UPDATE_FROM_REVISION(12);
2223       CASE_UPDATE_FROM_REVISION(13);
2224       CASE_UPDATE_FROM_REVISION(14);
2225       CASE_UPDATE_FROM_REVISION(15);
2226       CASE_UPDATE_FROM_REVISION(16);
2227       CASE_UPDATE_FROM_REVISION(17);
2228       CASE_UPDATE_FROM_REVISION(18);
2229       CASE_UPDATE_FROM_REVISION(19);
2230       CASE_UPDATE_FROM_REVISION(20);
2231       CASE_UPDATE_FROM_REVISION(21);
2232       CASE_UPDATE_FROM_REVISION(22);
2233       CASE_UPDATE_FROM_REVISION(23);
2234       CASE_UPDATE_FROM_REVISION(24);
2235       CASE_UPDATE_FROM_REVISION(25);
2236       CASE_UPDATE_FROM_REVISION(26);
2237       CASE_UPDATE_FROM_REVISION(27);
2238       CASE_UPDATE_FROM_REVISION(28);
2239       CASE_UPDATE_FROM_REVISION(29);
2240       CASE_UPDATE_FROM_REVISION(30);
2241 
2242       case ProjectFormatRevision:
2243         // Project is up-to-date.
2244         break;
2245 
2246       default:
2247         if (format_revision > ProjectFormatRevision)
2248         {
2249             RENDERER_LOG_ERROR(
2250                 "cannot update project in format revision " FMT_SIZE_T ", latest supported revision is " FMT_SIZE_T ".",
2251                 format_revision,
2252                 ProjectFormatRevision);
2253             event_counters.signal_error();
2254         }
2255         else
2256         {
2257             RENDERER_LOG_ERROR(
2258                 "cannot update project format from revision " FMT_SIZE_T " to revision " FMT_SIZE_T ", one or more update steps are missing.",
2259                 format_revision,
2260                 ProjectFormatRevision);
2261             event_counters.signal_error();
2262         }
2263         break;
2264     }
2265 
2266 #undef CASE_UPDATE_FROM_REVISION
2267 }
2268 
2269 }   // namespace renderer
2270