bcab527606021f1d1142ecff38f1fa05ddfea27e
[blender.git] / intern / cycles / blender / blender_object.cpp
1 /*
2  * Copyright 2011-2013 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 "render/camera.h"
18 #include "render/integrator.h"
19 #include "render/graph.h"
20 #include "render/light.h"
21 #include "render/mesh.h"
22 #include "render/object.h"
23 #include "render/scene.h"
24 #include "render/nodes.h"
25 #include "render/particles.h"
26 #include "render/shader.h"
27
28 #include "blender/blender_object_cull.h"
29 #include "blender/blender_sync.h"
30 #include "blender/blender_util.h"
31
32 #include "util/util_foreach.h"
33 #include "util/util_hash.h"
34 #include "util/util_logging.h"
35
36 CCL_NAMESPACE_BEGIN
37
38 /* Utilities */
39
40 bool BlenderSync::BKE_object_is_modified(BL::Object& b_ob)
41 {
42         /* test if we can instance or if the object is modified */
43         if(b_ob.type() == BL::Object::type_META) {
44                 /* multi-user and dupli metaballs are fused, can't instance */
45                 return true;
46         }
47         else if(ccl::BKE_object_is_modified(b_ob, b_scene, preview)) {
48                 /* modifiers */
49                 return true;
50         }
51         else {
52                 /* object level material links */
53                 BL::Object::material_slots_iterator slot;
54                 for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot)
55                         if(slot->link() == BL::MaterialSlot::link_OBJECT)
56                                 return true;
57         }
58
59         return false;
60 }
61
62 bool BlenderSync::object_is_mesh(BL::Object& b_ob)
63 {
64         BL::ID b_ob_data = b_ob.data();
65
66         if(!b_ob_data) {
67                 return false;
68         }
69
70         if(b_ob.type() == BL::Object::type_CURVE) {
71                 /* Skip exporting curves without faces, overhead can be
72                  * significant if there are many for path animation. */
73                 BL::Curve b_curve(b_ob.data());
74
75                 return (b_curve.bevel_object() ||
76                         b_curve.extrude() != 0.0f ||
77                         b_curve.bevel_depth() != 0.0f ||
78                         b_curve.dimensions() == BL::Curve::dimensions_2D ||
79                         b_ob.modifiers.length());
80         }
81         else {
82                 return (b_ob_data.is_a(&RNA_Mesh) ||
83                         b_ob_data.is_a(&RNA_Curve) ||
84                         b_ob_data.is_a(&RNA_MetaBall));
85         }
86 }
87
88 bool BlenderSync::object_is_light(BL::Object& b_ob)
89 {
90         BL::ID b_ob_data = b_ob.data();
91
92         return (b_ob_data && b_ob_data.is_a(&RNA_Light));
93 }
94
95 static uint object_ray_visibility(BL::Object& b_ob)
96 {
97         PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility");
98         uint flag = 0;
99
100         flag |= get_boolean(cvisibility, "camera")? PATH_RAY_CAMERA: 0;
101         flag |= get_boolean(cvisibility, "diffuse")? PATH_RAY_DIFFUSE: 0;
102         flag |= get_boolean(cvisibility, "glossy")? PATH_RAY_GLOSSY: 0;
103         flag |= get_boolean(cvisibility, "transmission")? PATH_RAY_TRANSMIT: 0;
104         flag |= get_boolean(cvisibility, "shadow")? PATH_RAY_SHADOW: 0;
105         flag |= get_boolean(cvisibility, "scatter")? PATH_RAY_VOLUME_SCATTER: 0;
106
107         return flag;
108 }
109
110 /* Light */
111
112 void BlenderSync::sync_light(BL::Object& b_parent,
113                              int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
114                              BL::Object& b_ob,
115                              BL::Object& b_ob_instance,
116                              int random_id,
117                              Transform& tfm,
118                              bool *use_portal)
119 {
120         /* test if we need to sync */
121         Light *light;
122         ObjectKey key(b_parent, persistent_id, b_ob_instance);
123
124         if(!light_map.sync(&light, b_ob, b_parent, key)) {
125                 if(light->is_portal)
126                         *use_portal = true;
127                 return;
128         }
129
130         BL::Light b_light(b_ob.data());
131
132         /* type */
133         switch(b_light.type()) {
134                 case BL::Light::type_POINT: {
135                         BL::PointLight b_point_light(b_light);
136                         light->size = b_point_light.shadow_soft_size();
137                         light->type = LIGHT_POINT;
138                         break;
139                 }
140                 case BL::Light::type_SPOT: {
141                         BL::SpotLight b_spot_light(b_light);
142                         light->size = b_spot_light.shadow_soft_size();
143                         light->type = LIGHT_SPOT;
144                         light->spot_angle = b_spot_light.spot_size();
145                         light->spot_smooth = b_spot_light.spot_blend();
146                         break;
147                 }
148                 /* Hemi were removed from 2.8 */
149                 // case BL::Light::type_HEMI: {
150                 //      light->type = LIGHT_DISTANT;
151                 //      light->size = 0.0f;
152                 //      break;
153                 // }
154                 case BL::Light::type_SUN: {
155                         BL::SunLight b_sun_light(b_light);
156                         light->size = b_sun_light.shadow_soft_size();
157                         light->type = LIGHT_DISTANT;
158                         break;
159                 }
160                 case BL::Light::type_AREA: {
161                         BL::AreaLight b_area_light(b_light);
162                         light->size = 1.0f;
163                         light->axisu = transform_get_column(&tfm, 0);
164                         light->axisv = transform_get_column(&tfm, 1);
165                         light->sizeu = b_area_light.size();
166                         switch(b_area_light.shape()) {
167                                 case BL::AreaLight::shape_SQUARE:
168                                         light->sizev = light->sizeu;
169                                         light->round = false;
170                                         break;
171                                 case BL::AreaLight::shape_RECTANGLE:
172                                         light->sizev = b_area_light.size_y();
173                                         light->round = false;
174                                         break;
175                                 case BL::AreaLight::shape_DISK:
176                                         light->sizev = light->sizeu;
177                                         light->round = true;
178                                         break;
179                                 case BL::AreaLight::shape_ELLIPSE:
180                                         light->sizev = b_area_light.size_y();
181                                         light->round = true;
182                                         break;
183                         }
184                         light->type = LIGHT_AREA;
185                         break;
186                 }
187         }
188
189         /* location and (inverted!) direction */
190         light->co = transform_get_column(&tfm, 3);
191         light->dir = -transform_get_column(&tfm, 2);
192         light->tfm = tfm;
193
194         /* shader */
195         vector<Shader*> used_shaders;
196         find_shader(b_light, used_shaders, scene->default_light);
197         light->shader = used_shaders[0];
198
199         /* shadow */
200         PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
201         PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles");
202         light->cast_shadow = get_boolean(clight, "cast_shadow");
203         light->use_mis = get_boolean(clight, "use_multiple_importance_sampling");
204
205         int samples = get_int(clight, "samples");
206         if(get_boolean(cscene, "use_square_samples"))
207                 light->samples = samples * samples;
208         else
209                 light->samples = samples;
210
211         light->max_bounces = get_int(clight, "max_bounces");
212
213         if(b_ob != b_ob_instance) {
214                 light->random_id = random_id;
215         }
216         else {
217                 light->random_id = hash_int_2d(hash_string(b_ob.name().c_str()), 0);
218         }
219
220         if(light->type == LIGHT_AREA)
221                 light->is_portal = get_boolean(clight, "is_portal");
222         else
223                 light->is_portal = false;
224
225         if(light->is_portal)
226                 *use_portal = true;
227
228         /* visibility */
229         uint visibility = object_ray_visibility(b_ob);
230         light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
231         light->use_glossy = (visibility & PATH_RAY_GLOSSY) != 0;
232         light->use_transmission = (visibility & PATH_RAY_TRANSMIT) != 0;
233         light->use_scatter = (visibility & PATH_RAY_VOLUME_SCATTER) != 0;
234
235         /* tag */
236         light->tag_update(scene);
237 }
238
239 void BlenderSync::sync_background_light(bool use_portal)
240 {
241         BL::World b_world = b_scene.world();
242
243         if(b_world) {
244                 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
245                 PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
246
247                 enum SamplingMethod {
248                         SAMPLING_NONE = 0,
249                         SAMPLING_AUTOMATIC,
250                         SAMPLING_MANUAL,
251                         SAMPLING_NUM
252                 };
253                 int sampling_method = get_enum(cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
254                 bool sample_as_light = (sampling_method != SAMPLING_NONE);
255
256                 if(sample_as_light || use_portal) {
257                         /* test if we need to sync */
258                         Light *light;
259                         ObjectKey key(b_world, 0, b_world);
260
261                         if(light_map.sync(&light, b_world, b_world, key) ||
262                             world_recalc ||
263                             b_world.ptr.data != world_map)
264                         {
265                                 light->type = LIGHT_BACKGROUND;
266                                 if(sampling_method == SAMPLING_MANUAL) {
267                                         light->map_resolution = get_int(cworld, "sample_map_resolution");
268                                 }
269                                 else {
270                                         light->map_resolution = 0;
271                                 }
272                                 light->shader = scene->default_background;
273                                 light->use_mis = sample_as_light;
274                                 light->max_bounces = get_int(cworld, "max_bounces");
275
276                                 int samples = get_int(cworld, "samples");
277                                 if(get_boolean(cscene, "use_square_samples"))
278                                         light->samples = samples * samples;
279                                 else
280                                         light->samples = samples;
281
282                                 light->tag_update(scene);
283                                 light_map.set_recalc(b_world);
284                         }
285                 }
286         }
287
288         world_map = b_world.ptr.data;
289         world_recalc = false;
290 }
291
292 /* Object */
293
294 Object *BlenderSync::sync_object(BL::Depsgraph& b_depsgraph,
295                                  BL::ViewLayer& b_view_layer,
296                                  BL::DepsgraphObjectInstance& b_instance,
297                                  float motion_time,
298                                  bool hide_tris,
299                                  BlenderObjectCulling& culling,
300                                  bool *use_portal)
301 {
302         const bool is_instance = b_instance.is_instance();
303         BL::Object b_ob = b_instance.object();
304         BL::Object b_parent = is_instance ? b_instance.parent()
305                                           : b_instance.object();
306         BL::Object b_ob_instance = is_instance ? b_instance.instance_object()
307                                                : b_ob;
308         const bool motion = motion_time != 0.0f;
309         /*const*/ Transform tfm = get_transform(b_ob.matrix_world());
310         int *persistent_id = NULL;
311         BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id_array;
312         if(is_instance) {
313                 persistent_id_array = b_instance.persistent_id();
314                 persistent_id = persistent_id_array.data;
315         }
316
317         /* light is handled separately */
318         if(!motion && object_is_light(b_ob)) {
319                 /* TODO: don't use lights for excluded layers used as mask layer,
320                  * when dynamic overrides are back. */
321 #if 0
322                 if(!((layer_flag & view_layer.holdout_layer) &&
323                      (layer_flag & view_layer.exclude_layer)))
324 #endif
325                 {
326                         sync_light(b_parent,
327                                    persistent_id,
328                                    b_ob,
329                                    b_ob_instance,
330                                    is_instance ? b_instance.random_id() : 0,
331                                    tfm,
332                                    use_portal);
333                 }
334
335                 return NULL;
336         }
337
338         /* only interested in object that we can create meshes from */
339         if(!object_is_mesh(b_ob)) {
340                 return NULL;
341         }
342
343         /* Perform object culling. */
344         if(culling.test(scene, b_ob, tfm)) {
345                 return NULL;
346         }
347
348         /* Visibility flags for both parent and child. */
349         PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
350         bool use_holdout = get_boolean(cobject, "is_holdout") ||
351                            b_parent.holdout_get(b_view_layer);
352         uint visibility = object_ray_visibility(b_ob) & PATH_RAY_ALL_VISIBILITY;
353
354         if(b_parent.ptr.data != b_ob.ptr.data) {
355                 visibility &= object_ray_visibility(b_parent);
356         }
357
358         /* TODO: make holdout objects on excluded layer invisible for non-camera rays. */
359 #if 0
360         if(use_holdout && (layer_flag & view_layer.exclude_layer)) {
361                 visibility &= ~(PATH_RAY_ALL_VISIBILITY - PATH_RAY_CAMERA);
362         }
363 #endif
364
365         /* Clear camera visibility for indirect only objects. */
366         bool use_indirect_only = b_parent.indirect_only_get(b_view_layer);
367         if(use_indirect_only) {
368                 visibility &= ~PATH_RAY_CAMERA;
369         }
370
371         /* Don't export completely invisible objects. */
372         if(visibility == 0) {
373                 return NULL;
374         }
375
376         /* key to lookup object */
377         ObjectKey key(b_parent, persistent_id, b_ob_instance);
378         Object *object;
379
380         /* motion vector case */
381         if(motion) {
382                 object = object_map.find(key);
383
384                 if(object && object->use_motion()) {
385                         /* Set transform at matching motion time step. */
386                         int time_index = object->motion_step(motion_time);
387                         if(time_index >= 0) {
388                                 object->motion[time_index] = tfm;
389                         }
390
391                         /* mesh deformation */
392                         if(object->mesh)
393                                 sync_mesh_motion(b_depsgraph, b_ob, object, motion_time);
394                 }
395
396                 return object;
397         }
398
399         /* test if we need to sync */
400         bool object_updated = false;
401
402         if(object_map.sync(&object, b_ob, b_parent, key))
403                 object_updated = true;
404
405         /* mesh sync */
406         object->mesh = sync_mesh(b_depsgraph, b_ob, b_ob_instance, object_updated, hide_tris);
407
408         /* special case not tracked by object update flags */
409
410         /* holdout */
411         if(use_holdout != object->use_holdout) {
412                 object->use_holdout = use_holdout;
413                 scene->object_manager->tag_update(scene);
414                 object_updated = true;
415         }
416
417         if(visibility != object->visibility) {
418                 object->visibility = visibility;
419                 object_updated = true;
420         }
421
422         bool is_shadow_catcher = get_boolean(cobject, "is_shadow_catcher");
423         if(is_shadow_catcher != object->is_shadow_catcher) {
424                 object->is_shadow_catcher = is_shadow_catcher;
425                 object_updated = true;
426         }
427
428         /* sync the asset name for Cryptomatte */
429         BL::Object parent = b_ob.parent();
430         ustring parent_name;
431         if(parent) {
432                 while(parent.parent()) {
433                         parent = parent.parent();
434                 }
435                 parent_name = parent.name();
436         }
437         else {
438                 parent_name = b_ob.name();
439         }
440         if(object->asset_name != parent_name) {
441                 object->asset_name = parent_name;
442                 object_updated = true;
443         }
444
445         /* object sync
446          * transform comparison should not be needed, but duplis don't work perfect
447          * in the depsgraph and may not signal changes, so this is a workaround */
448         if(object_updated || (object->mesh && object->mesh->need_update) || tfm != object->tfm) {
449                 object->name = b_ob.name().c_str();
450                 object->pass_id = b_ob.pass_index();
451                 object->tfm = tfm;
452                 object->motion.clear();
453
454                 /* motion blur */
455                 Scene::MotionType need_motion = scene->need_motion();
456                 if(need_motion != Scene::MOTION_NONE && object->mesh) {
457                         Mesh *mesh = object->mesh;
458                         mesh->use_motion_blur = false;
459                         mesh->motion_steps = 0;
460
461                         uint motion_steps;
462
463                         if(scene->need_motion() == Scene::MOTION_BLUR) {
464                                 motion_steps = object_motion_steps(b_parent, b_ob);
465                                 mesh->motion_steps = motion_steps;
466                                 if(motion_steps && object_use_deform_motion(b_parent, b_ob)) {
467                                         mesh->use_motion_blur = true;
468                                 }
469                         }
470                         else {
471                                 motion_steps = 3;
472                                 mesh->motion_steps = motion_steps;
473                         }
474
475                         object->motion.clear();
476                         object->motion.resize(motion_steps, transform_empty());
477
478                         if(motion_steps) {
479                                 object->motion[motion_steps/2] = tfm;
480
481                                 for(size_t step = 0; step < motion_steps; step++) {
482                                         motion_times.insert(object->motion_time(step));
483                                 }
484                         }
485                 }
486
487                 /* dupli texture coordinates and random_id */
488                 if(is_instance) {
489                         object->dupli_generated = 0.5f*get_float3(b_instance.orco()) - make_float3(0.5f, 0.5f, 0.5f);
490                         object->dupli_uv = get_float2(b_instance.uv());
491                         object->random_id = b_instance.random_id();
492
493                         /* Sync possible particle data. */
494                         sync_dupli_particle(b_ob, b_instance, object);
495                 }
496                 else {
497                         object->dupli_generated = make_float3(0.0f, 0.0f, 0.0f);
498                         object->dupli_uv = make_float2(0.0f, 0.0f);
499                         object->random_id =  hash_int_2d(hash_string(object->name.c_str()), 0);
500                 }
501
502                 object->tag_update(scene);
503         }
504
505         return object;
506 }
507
508 static bool object_render_hide_original(BL::Object::type_enum ob_type,
509                                         BL::Object::instance_type_enum dupli_type)
510 {
511         /* metaball exception, they duplicate self */
512         if(ob_type == BL::Object::type_META)
513                 return false;
514
515         return (dupli_type == BL::Object::instance_type_VERTS ||
516                 dupli_type == BL::Object::instance_type_FACES ||
517                 dupli_type == BL::Object::instance_type_FRAMES);
518 }
519
520 static bool object_render_hide(BL::Object& b_ob,
521                                bool top_level,
522                                bool parent_hide,
523                                bool& hide_triangles,
524                                BL::Depsgraph::mode_enum depsgraph_mode)
525 {
526         /* check if we should render or hide particle emitter */
527         BL::Object::particle_systems_iterator b_psys;
528
529         bool hair_present = false;
530         bool has_particles = false;
531         bool show_emitter = false;
532         bool hide_emitter = false;
533         bool hide_as_dupli_parent = false;
534         bool hide_as_dupli_child_original = false;
535
536         for(b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys) {
537                 if((b_psys->settings().render_type() == BL::ParticleSettings::render_type_PATH) &&
538                    (b_psys->settings().type()==BL::ParticleSettings::type_HAIR))
539                         hair_present = true;
540                 has_particles = true;
541         }
542
543         /* Both mode_PREVIEW and mode_VIEWPORT are treated the same here.*/
544         const bool show_instancer = depsgraph_mode == BL::Depsgraph::mode_RENDER
545                                      ? b_ob.show_instancer_for_render()
546                                      : b_ob.show_instancer_for_viewport();
547
548         if(has_particles) {
549                 show_emitter = show_instancer;
550                 hide_emitter = !show_emitter;
551         } else if(b_ob.is_instancer()) {
552                 if(top_level || show_instancer) {
553                         hide_as_dupli_parent = true;
554                 }
555         }
556
557         /* hide original object for duplis */
558         BL::Object parent = b_ob.parent();
559         while(parent) {
560                 if(object_render_hide_original(b_ob.type(),
561                                                parent.instance_type()))
562                 {
563                         if(parent_hide) {
564                                 hide_as_dupli_child_original = true;
565                                 break;
566                         }
567                 }
568                 parent = parent.parent();
569         }
570
571         hide_triangles = hide_emitter;
572
573         if(show_emitter) {
574                 return false;
575         }
576         else if(hair_present) {
577                 return hide_as_dupli_child_original;
578         }
579         else {
580                 return (hide_as_dupli_parent || hide_as_dupli_child_original);
581         }
582 }
583
584 /* Object Loop */
585
586 void BlenderSync::sync_objects(BL::Depsgraph& b_depsgraph, float motion_time)
587 {
588         /* layer data */
589         bool motion = motion_time != 0.0f;
590
591         if(!motion) {
592                 /* prepare for sync */
593                 light_map.pre_sync();
594                 mesh_map.pre_sync();
595                 object_map.pre_sync();
596                 particle_system_map.pre_sync();
597                 motion_times.clear();
598         }
599         else {
600                 mesh_motion_synced.clear();
601         }
602
603         /* initialize culling */
604         BlenderObjectCulling culling(scene, b_scene);
605
606         /* object loop */
607         bool cancel = false;
608         bool use_portal = false;
609
610         BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
611         BL::Depsgraph::mode_enum depsgraph_mode = b_depsgraph.mode();
612
613         BL::Depsgraph::object_instances_iterator b_instance_iter;
614         for(b_depsgraph.object_instances.begin(b_instance_iter);
615             b_instance_iter != b_depsgraph.object_instances.end() && !cancel;
616             ++b_instance_iter)
617         {
618                 BL::DepsgraphObjectInstance b_instance = *b_instance_iter;
619                 BL::Object b_ob = b_instance.object();
620
621                 progress.set_sync_status("Synchronizing object", b_ob.name());
622
623                 /* load per-object culling data */
624                 culling.init_object(scene, b_ob);
625
626                 /* test if object needs to be hidden */
627                 bool hide_tris;
628
629                  if(!object_render_hide(b_ob, true, true, hide_tris, depsgraph_mode)) {
630                         /* object itself */
631                         sync_object(b_depsgraph,
632                                     b_view_layer,
633                                     b_instance,
634                                     motion_time,
635                                     hide_tris,
636                                     culling,
637                                     &use_portal);
638                  }
639
640                 cancel = progress.get_cancel();
641         }
642
643         progress.set_sync_status("");
644
645         if(!cancel && !motion) {
646                 sync_background_light(use_portal);
647
648                 /* handle removed data and modified pointers */
649                 if(light_map.post_sync())
650                         scene->light_manager->tag_update(scene);
651                 if(mesh_map.post_sync())
652                         scene->mesh_manager->tag_update(scene);
653                 if(object_map.post_sync())
654                         scene->object_manager->tag_update(scene);
655                 if(particle_system_map.post_sync())
656                         scene->particle_system_manager->tag_update(scene);
657         }
658
659         if(motion)
660                 mesh_motion_synced.clear();
661 }
662
663 void BlenderSync::sync_motion(BL::RenderSettings& b_render,
664                               BL::Depsgraph& b_depsgraph,
665                               BL::Object& b_override,
666                               int width, int height,
667                               void **python_thread_state)
668 {
669         if(scene->need_motion() == Scene::MOTION_NONE)
670                 return;
671
672         /* get camera object here to deal with camera switch */
673         BL::Object b_cam = b_scene.camera();
674         if(b_override)
675                 b_cam = b_override;
676
677         Camera prevcam = *(scene->camera);
678
679         int frame_center = b_scene.frame_current();
680         float subframe_center = b_scene.frame_subframe();
681         float frame_center_delta = 0.0f;
682
683         if(scene->need_motion() != Scene::MOTION_PASS &&
684            scene->camera->motion_position != Camera::MOTION_POSITION_CENTER)
685         {
686                 float shuttertime = scene->camera->shuttertime;
687                 if(scene->camera->motion_position == Camera::MOTION_POSITION_END) {
688                         frame_center_delta = -shuttertime * 0.5f;
689                 }
690                 else {
691                         assert(scene->camera->motion_position == Camera::MOTION_POSITION_START);
692                         frame_center_delta = shuttertime * 0.5f;
693                 }
694
695                 float time = frame_center + subframe_center + frame_center_delta;
696                 int frame = (int)floorf(time);
697                 float subframe = time - frame;
698                 python_thread_state_restore(python_thread_state);
699                 b_engine.frame_set(frame, subframe);
700                 python_thread_state_save(python_thread_state);
701                 sync_camera_motion(b_render, b_cam, width, height, 0.0f);
702                 sync_objects(b_depsgraph, 0.0f);
703         }
704
705         /* always sample these times for camera motion */
706         motion_times.insert(-1.0f);
707         motion_times.insert(1.0f);
708
709         /* note iteration over motion_times set happens in sorted order */
710         foreach(float relative_time, motion_times) {
711                 /* center time is already handled. */
712                 if(relative_time == 0.0f) {
713                         continue;
714                 }
715
716                 VLOG(1) << "Synchronizing motion for the relative time "
717                         << relative_time << ".";
718
719                 /* fixed shutter time to get previous and next frame for motion pass */
720                 float shuttertime = scene->motion_shutter_time();
721
722                 /* compute frame and subframe time */
723                 float time = frame_center + subframe_center + frame_center_delta + relative_time * shuttertime * 0.5f;
724                 int frame = (int)floorf(time);
725                 float subframe = time - frame;
726
727                 /* change frame */
728                 python_thread_state_restore(python_thread_state);
729                 b_engine.frame_set(frame, subframe);
730                 python_thread_state_save(python_thread_state);
731
732                 /* sync camera, only supports two times at the moment */
733                 if(relative_time == -1.0f || relative_time == 1.0f) {
734                         sync_camera_motion(b_render,
735                                            b_cam,
736                                            width, height,
737                                            relative_time);
738                 }
739
740                 /* sync object */
741                 sync_objects(b_depsgraph, relative_time);
742         }
743
744         /* we need to set the python thread state again because this
745          * function assumes it is being executed from python and will
746          * try to save the thread state */
747         python_thread_state_restore(python_thread_state);
748         b_engine.frame_set(frame_center, subframe_center);
749         python_thread_state_save(python_thread_state);
750
751         /* tag camera for motion update */
752         if(scene->camera->motion_modified(prevcam))
753                 scene->camera->tag_update();
754 }
755
756 CCL_NAMESPACE_END