Fix T55626, fix T55106: Cycles motion blur + persistent images bug.
[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_Lamp));
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::DupliObject& b_dupli_ob,
116                              Transform& tfm,
117                              bool *use_portal)
118 {
119         /* test if we need to sync */
120         Light *light;
121         ObjectKey key(b_parent, persistent_id, b_ob);
122
123         if(!light_map.sync(&light, b_ob, b_parent, key)) {
124                 if(light->is_portal)
125                         *use_portal = true;
126                 return;
127         }
128         
129         BL::Lamp b_lamp(b_ob.data());
130
131         /* type */
132         switch(b_lamp.type()) {
133                 case BL::Lamp::type_POINT: {
134                         BL::PointLamp b_point_lamp(b_lamp);
135                         light->size = b_point_lamp.shadow_soft_size();
136                         light->type = LIGHT_POINT;
137                         break;
138                 }
139                 case BL::Lamp::type_SPOT: {
140                         BL::SpotLamp b_spot_lamp(b_lamp);
141                         light->size = b_spot_lamp.shadow_soft_size();
142                         light->type = LIGHT_SPOT;
143                         light->spot_angle = b_spot_lamp.spot_size();
144                         light->spot_smooth = b_spot_lamp.spot_blend();
145                         break;
146                 }
147                 case BL::Lamp::type_HEMI: {
148                         light->type = LIGHT_DISTANT;
149                         light->size = 0.0f;
150                         break;
151                 }
152                 case BL::Lamp::type_SUN: {
153                         BL::SunLamp b_sun_lamp(b_lamp);
154                         light->size = b_sun_lamp.shadow_soft_size();
155                         light->type = LIGHT_DISTANT;
156                         break;
157                 }
158                 case BL::Lamp::type_AREA: {
159                         BL::AreaLamp b_area_lamp(b_lamp);
160                         light->size = 1.0f;
161                         light->axisu = transform_get_column(&tfm, 0);
162                         light->axisv = transform_get_column(&tfm, 1);
163                         light->sizeu = b_area_lamp.size();
164                         if(b_area_lamp.shape() == BL::AreaLamp::shape_RECTANGLE)
165                                 light->sizev = b_area_lamp.size_y();
166                         else
167                                 light->sizev = light->sizeu;
168                         light->type = LIGHT_AREA;
169                         break;
170                 }
171         }
172
173         /* location and (inverted!) direction */
174         light->co = transform_get_column(&tfm, 3);
175         light->dir = -transform_get_column(&tfm, 2);
176         light->tfm = tfm;
177
178         /* shader */
179         vector<Shader*> used_shaders;
180         find_shader(b_lamp, used_shaders, scene->default_light);
181         light->shader = used_shaders[0];
182
183         /* shadow */
184         PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
185         PointerRNA clamp = RNA_pointer_get(&b_lamp.ptr, "cycles");
186         light->cast_shadow = get_boolean(clamp, "cast_shadow");
187         light->use_mis = get_boolean(clamp, "use_multiple_importance_sampling");
188         
189         int samples = get_int(clamp, "samples");
190         if(get_boolean(cscene, "use_square_samples"))
191                 light->samples = samples * samples;
192         else
193                 light->samples = samples;
194
195         light->max_bounces = get_int(clamp, "max_bounces");
196
197         if(b_dupli_ob) {
198                 light->random_id = b_dupli_ob.random_id();
199         }
200         else {
201                 light->random_id = hash_int_2d(hash_string(b_ob.name().c_str()), 0);
202         }
203
204         if(light->type == LIGHT_AREA)
205                 light->is_portal = get_boolean(clamp, "is_portal");
206         else
207                 light->is_portal = false;
208
209         if(light->is_portal)
210                 *use_portal = true;
211
212         /* visibility */
213         uint visibility = object_ray_visibility(b_ob);
214         light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
215         light->use_glossy = (visibility & PATH_RAY_GLOSSY) != 0;
216         light->use_transmission = (visibility & PATH_RAY_TRANSMIT) != 0;
217         light->use_scatter = (visibility & PATH_RAY_VOLUME_SCATTER) != 0;
218
219         /* tag */
220         light->tag_update(scene);
221 }
222
223 void BlenderSync::sync_background_light(bool use_portal)
224 {
225         BL::World b_world = b_scene.world();
226
227         if(b_world) {
228                 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
229                 PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
230
231                 enum SamplingMethod {
232                         SAMPLING_NONE = 0,
233                         SAMPLING_AUTOMATIC,
234                         SAMPLING_MANUAL,
235                         SAMPLING_NUM
236                 };
237                 int sampling_method = get_enum(cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
238                 bool sample_as_light = (sampling_method != SAMPLING_NONE);
239
240                 if(sample_as_light || use_portal) {
241                         /* test if we need to sync */
242                         Light *light;
243                         ObjectKey key(b_world, 0, b_world);
244
245                         if(light_map.sync(&light, b_world, b_world, key) ||
246                             world_recalc ||
247                             b_world.ptr.data != world_map)
248                         {
249                                 light->type = LIGHT_BACKGROUND;
250                                 if(sampling_method == SAMPLING_MANUAL) {
251                                         light->map_resolution = get_int(cworld, "sample_map_resolution");
252                                 }
253                                 else {
254                                         light->map_resolution = 0;
255                                 }
256                                 light->shader = scene->default_background;
257                                 light->use_mis = sample_as_light;
258                                 light->max_bounces = get_int(cworld, "max_bounces");
259
260                                 int samples = get_int(cworld, "samples");
261                                 if(get_boolean(cscene, "use_square_samples"))
262                                         light->samples = samples * samples;
263                                 else
264                                         light->samples = samples;
265
266                                 light->tag_update(scene);
267                                 light_map.set_recalc(b_world);
268                         }
269                 }
270         }
271
272         world_map = b_world.ptr.data;
273         world_recalc = false;
274 }
275
276 /* Object */
277
278 Object *BlenderSync::sync_object(BL::Object& b_parent,
279                                  int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
280                                  BL::DupliObject& b_dupli_ob,
281                                  Transform& tfm,
282                                  uint layer_flag,
283                                  float motion_time,
284                                  bool hide_tris,
285                                  BlenderObjectCulling& culling,
286                                  bool *use_portal)
287 {
288         BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
289         bool motion = motion_time != 0.0f;
290         
291         /* light is handled separately */
292         if(object_is_light(b_ob)) {
293                 /* don't use lamps for excluded layers used as mask layer */
294                 if(!motion && !((layer_flag & render_layer.holdout_layer) && (layer_flag & render_layer.exclude_layer)))
295                         sync_light(b_parent, persistent_id, b_ob, b_dupli_ob, tfm, use_portal);
296
297                 return NULL;
298         }
299
300         /* only interested in object that we can create meshes from */
301         if(!object_is_mesh(b_ob)) {
302                 return NULL;
303         }
304
305         /* Perform object culling. */
306         if(culling.test(scene, b_ob, tfm)) {
307                 return NULL;
308         }
309
310         /* Visibility flags for both parent and child. */
311         PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
312         bool use_holdout = (layer_flag & render_layer.holdout_layer) != 0 ||
313                            get_boolean(cobject, "is_holdout");
314         uint visibility = object_ray_visibility(b_ob) & PATH_RAY_ALL_VISIBILITY;
315
316         if(b_parent.ptr.data != b_ob.ptr.data) {
317                 visibility &= object_ray_visibility(b_parent);
318         }
319
320         /* Make holdout objects on excluded layer invisible for non-camera rays. */
321         if(use_holdout && (layer_flag & render_layer.exclude_layer)) {
322                 visibility &= ~(PATH_RAY_ALL_VISIBILITY - PATH_RAY_CAMERA);
323         }
324
325         /* Hide objects not on render layer from camera rays. */
326         if(!(layer_flag & render_layer.layer)) {
327                 visibility &= ~PATH_RAY_CAMERA;
328         }
329
330         /* Don't export completely invisible objects. */
331         if(visibility == 0) {
332                 return NULL;
333         }
334
335         /* key to lookup object */
336         ObjectKey key(b_parent, persistent_id, b_ob);
337         Object *object;
338
339         /* motion vector case */
340         if(motion) {
341                 object = object_map.find(key);
342
343                 if(object && object->use_motion()) {
344                         /* Set transform at matching motion time step. */
345                         int time_index = object->motion_step(motion_time);
346                         if(time_index >= 0) {
347                                 object->motion[time_index] = tfm;
348                         }
349
350                         /* mesh deformation */
351                         if(object->mesh)
352                                 sync_mesh_motion(b_ob, object, motion_time);
353                 }
354
355                 return object;
356         }
357
358         /* test if we need to sync */
359         bool object_updated = false;
360
361         if(object_map.sync(&object, b_ob, b_parent, key))
362                 object_updated = true;
363         
364         /* mesh sync */
365         object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
366
367         /* special case not tracked by object update flags */
368
369         /* holdout */
370         if(use_holdout != object->use_holdout) {
371                 object->use_holdout = use_holdout;
372                 scene->object_manager->tag_update(scene);
373                 object_updated = true;
374         }
375
376         if(visibility != object->visibility) {
377                 object->visibility = visibility;
378                 object_updated = true;
379         }
380
381         bool is_shadow_catcher = get_boolean(cobject, "is_shadow_catcher");
382         if(is_shadow_catcher != object->is_shadow_catcher) {
383                 object->is_shadow_catcher = is_shadow_catcher;
384                 object_updated = true;
385         }
386
387         /* object sync
388          * transform comparison should not be needed, but duplis don't work perfect
389          * in the depsgraph and may not signal changes, so this is a workaround */
390         if(object_updated || (object->mesh && object->mesh->need_update) || tfm != object->tfm) {
391                 object->name = b_ob.name().c_str();
392                 object->pass_id = b_ob.pass_index();
393                 object->tfm = tfm;
394                 object->motion.clear();
395
396                 /* motion blur */
397                 Scene::MotionType need_motion = scene->need_motion();
398                 if(need_motion != Scene::MOTION_NONE && object->mesh) {
399                         Mesh *mesh = object->mesh;
400                         mesh->use_motion_blur = false;
401                         mesh->motion_steps = 0;
402
403                         uint motion_steps;
404
405                         if(scene->need_motion() == Scene::MOTION_BLUR) {
406                                 motion_steps = object_motion_steps(b_parent, b_ob);
407                                 if(motion_steps && object_use_deform_motion(b_parent, b_ob)) {
408                                         mesh->motion_steps = motion_steps;
409                                         mesh->use_motion_blur = true;
410                                 }
411                         }
412                         else {
413                                 motion_steps = 3;
414                                 mesh->motion_steps = motion_steps;
415                         }
416
417                         object->motion.clear();
418                         object->motion.resize(motion_steps, transform_empty());
419
420                         if(motion_steps) {
421                                 object->motion[motion_steps/2] = tfm;
422
423                                 for(size_t step = 0; step < motion_steps; step++) {
424                                         motion_times.insert(object->motion_time(step));
425                                 }
426                         }
427                 }
428
429                 /* dupli texture coordinates and random_id */
430                 if(b_dupli_ob) {
431                         object->dupli_generated = 0.5f*get_float3(b_dupli_ob.orco()) - make_float3(0.5f, 0.5f, 0.5f);
432                         object->dupli_uv = get_float2(b_dupli_ob.uv());
433                         object->random_id = b_dupli_ob.random_id();
434                 }
435                 else {
436                         object->dupli_generated = make_float3(0.0f, 0.0f, 0.0f);
437                         object->dupli_uv = make_float2(0.0f, 0.0f);
438                         object->random_id =  hash_int_2d(hash_string(object->name.c_str()), 0);
439                 }
440
441                 object->tag_update(scene);
442         }
443
444         return object;
445 }
446
447 static bool object_render_hide_original(BL::Object::type_enum ob_type,
448                                         BL::Object::dupli_type_enum dupli_type)
449 {
450         /* metaball exception, they duplicate self */
451         if(ob_type == BL::Object::type_META)
452                 return false;
453
454         return (dupli_type == BL::Object::dupli_type_VERTS ||
455                 dupli_type == BL::Object::dupli_type_FACES ||
456                 dupli_type == BL::Object::dupli_type_FRAMES);
457 }
458
459 static bool object_render_hide(BL::Object& b_ob,
460                                bool top_level,
461                                bool parent_hide,
462                                bool& hide_triangles)
463 {
464         /* check if we should render or hide particle emitter */
465         BL::Object::particle_systems_iterator b_psys;
466
467         bool hair_present = false;
468         bool show_emitter = false;
469         bool hide_emitter = false;
470         bool hide_as_dupli_parent = false;
471         bool hide_as_dupli_child_original = false;
472
473         for(b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys) {
474                 if((b_psys->settings().render_type() == BL::ParticleSettings::render_type_PATH) &&
475                    (b_psys->settings().type()==BL::ParticleSettings::type_HAIR))
476                         hair_present = true;
477
478                 if(b_psys->settings().use_render_emitter())
479                         show_emitter = true;
480                 else
481                         hide_emitter = true;
482         }
483
484         if(show_emitter)
485                 hide_emitter = false;
486
487         /* duplicators hidden by default, except dupliframes which duplicate self */
488         if(b_ob.is_duplicator())
489                 if(top_level || b_ob.dupli_type() != BL::Object::dupli_type_FRAMES)
490                         hide_as_dupli_parent = true;
491
492         /* hide original object for duplis */
493         BL::Object parent = b_ob.parent();
494         while(parent) {
495                 if(object_render_hide_original(b_ob.type(),
496                                                parent.dupli_type()))
497                 {
498                         if(parent_hide) {
499                                 hide_as_dupli_child_original = true;
500                                 break;
501                         }
502                 }
503                 parent = parent.parent();
504         }
505         
506         hide_triangles = hide_emitter;
507
508         if(show_emitter) {
509                 return false;
510         }
511         else if(hair_present) {
512                 return hide_as_dupli_child_original;
513         }
514         else {
515                 return (hide_as_dupli_parent || hide_as_dupli_child_original);
516         }
517 }
518
519 static bool object_render_hide_duplis(BL::Object& b_ob)
520 {
521         BL::Object parent = b_ob.parent();
522
523         return (parent && object_render_hide_original(b_ob.type(), parent.dupli_type()));
524 }
525
526 /* Object Loop */
527
528 void BlenderSync::sync_objects(float motion_time)
529 {
530         /* layer data */
531         uint scene_layer = render_layer.scene_layer;
532         bool motion = motion_time != 0.0f;
533         
534         if(!motion) {
535                 /* prepare for sync */
536                 light_map.pre_sync();
537                 mesh_map.pre_sync();
538                 object_map.pre_sync();
539                 particle_system_map.pre_sync();
540                 motion_times.clear();
541         }
542         else {
543                 mesh_motion_synced.clear();
544         }
545
546         /* initialize culling */
547         BlenderObjectCulling culling(scene, b_scene);
548
549         /* object loop */
550         BL::Scene::object_bases_iterator b_base;
551         BL::Scene b_sce = b_scene;
552         /* modifier result type (not exposed as enum in C++ API)
553          * 1 : DAG_EVAL_PREVIEW
554          * 2 : DAG_EVAL_RENDER
555          */
556         int dupli_settings = (render_layer.use_viewport_visibility) ? 1 : 2;
557
558         bool cancel = false;
559         bool use_portal = false;
560
561         uint layer_override = get_layer(b_engine.layer_override());
562         for(; b_sce && !cancel; b_sce = b_sce.background_set()) {
563                 /* Render layer's scene_layer is affected by local view already,
564                  * which is not a desired behavior here.
565                  */
566                 uint scene_layers = layer_override ? layer_override : get_layer(b_scene.layers());
567                 for(b_sce.object_bases.begin(b_base); b_base != b_sce.object_bases.end() && !cancel; ++b_base) {
568                         BL::Object b_ob = b_base->object();
569                         bool hide = (render_layer.use_viewport_visibility)? b_ob.hide(): b_ob.hide_render();
570                         uint ob_layer = get_layer(b_base->layers(),
571                                                   b_base->layers_local_view(),
572                                                   object_is_light(b_ob),
573                                                   scene_layers);
574                         hide = hide || !(ob_layer & scene_layer);
575
576                         if(!hide) {
577                                 progress.set_sync_status("Synchronizing object", b_ob.name());
578
579                                 /* load per-object culling data */
580                                 culling.init_object(scene, b_ob);
581
582                                 if(b_ob.is_duplicator() && !object_render_hide_duplis(b_ob)) {
583                                         /* dupli objects */
584                                         b_ob.dupli_list_create(b_data, b_scene, dupli_settings);
585
586                                         BL::Object::dupli_list_iterator b_dup;
587
588                                         for(b_ob.dupli_list.begin(b_dup); b_dup != b_ob.dupli_list.end(); ++b_dup) {
589                                                 Transform tfm = get_transform(b_dup->matrix());
590                                                 BL::Object b_dup_ob = b_dup->object();
591                                                 bool dup_hide = (render_layer.use_viewport_visibility)? b_dup_ob.hide(): b_dup_ob.hide_render();
592                                                 bool in_dupli_group = (b_dup->type() == BL::DupliObject::type_GROUP);
593                                                 bool hide_tris;
594
595                                                 if(!(b_dup->hide() || dup_hide || object_render_hide(b_dup_ob, false, in_dupli_group, hide_tris))) {
596                                                         /* the persistent_id allows us to match dupli objects
597                                                          * between frames and updates */
598                                                         BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id();
599
600                                                         /* sync object and mesh or light data */
601                                                         Object *object = sync_object(b_ob,
602                                                                                      persistent_id.data,
603                                                                                      *b_dup,
604                                                                                      tfm,
605                                                                                      ob_layer,
606                                                                                      motion_time,
607                                                                                      hide_tris,
608                                                                                      culling,
609                                                                                      &use_portal);
610
611                                                         /* sync possible particle data, note particle_id
612                                                          * starts counting at 1, first is dummy particle */
613                                                         if(!motion && object) {
614                                                                 sync_dupli_particle(b_ob, *b_dup, object);
615                                                         }
616
617                                                 }
618                                         }
619
620                                         b_ob.dupli_list_clear();
621                                 }
622
623                                 /* test if object needs to be hidden */
624                                 bool hide_tris;
625
626                                 if(!object_render_hide(b_ob, true, true, hide_tris)) {
627                                         /* object itself */
628                                         Transform tfm = get_transform(b_ob.matrix_world());
629                                         BL::DupliObject b_empty_dupli_ob(PointerRNA_NULL);
630                                         sync_object(b_ob,
631                                                     NULL,
632                                                     b_empty_dupli_ob,
633                                                     tfm,
634                                                     ob_layer,
635                                                     motion_time,
636                                                     hide_tris,
637                                                     culling,
638                                                     &use_portal);
639                                 }
640                         }
641
642                         cancel = progress.get_cancel();
643                 }
644         }
645
646         progress.set_sync_status("");
647
648         if(!cancel && !motion) {
649                 sync_background_light(use_portal);
650
651                 /* handle removed data and modified pointers */
652                 if(light_map.post_sync())
653                         scene->light_manager->tag_update(scene);
654                 if(mesh_map.post_sync())
655                         scene->mesh_manager->tag_update(scene);
656                 if(object_map.post_sync())
657                         scene->object_manager->tag_update(scene);
658                 if(particle_system_map.post_sync())
659                         scene->particle_system_manager->tag_update(scene);
660         }
661
662         if(motion)
663                 mesh_motion_synced.clear();
664 }
665
666 void BlenderSync::sync_motion(BL::RenderSettings& b_render,
667                               BL::Object& b_override,
668                               int width, int height,
669                               void **python_thread_state)
670 {
671         if(scene->need_motion() == Scene::MOTION_NONE)
672                 return;
673
674         /* get camera object here to deal with camera switch */
675         BL::Object b_cam = b_scene.camera();
676         if(b_override)
677                 b_cam = b_override;
678
679         Camera prevcam = *(scene->camera);
680
681         int frame_center = b_scene.frame_current();
682         float subframe_center = b_scene.frame_subframe();
683         float frame_center_delta = 0.0f;
684
685         if(scene->need_motion() != Scene::MOTION_PASS &&
686            scene->camera->motion_position != Camera::MOTION_POSITION_CENTER)
687         {
688                 float shuttertime = scene->camera->shuttertime;
689                 if(scene->camera->motion_position == Camera::MOTION_POSITION_END) {
690                         frame_center_delta = -shuttertime * 0.5f;
691                 }
692                 else {
693                         assert(scene->camera->motion_position == Camera::MOTION_POSITION_START);
694                         frame_center_delta = shuttertime * 0.5f;
695                 }
696                 float time = frame_center + subframe_center + frame_center_delta;
697                 int frame = (int)floorf(time);
698                 float subframe = time - frame;
699                 python_thread_state_restore(python_thread_state);
700                 b_engine.frame_set(frame, subframe);
701                 python_thread_state_save(python_thread_state);
702                 sync_camera_motion(b_render, b_cam, width, height, 0.0f);
703                 sync_objects(0.0f);
704         }
705
706         /* always sample these times for camera motion */
707         motion_times.insert(-1.0f);
708         motion_times.insert(1.0f);
709
710         /* note iteration over motion_times set happens in sorted order */
711         foreach(float relative_time, motion_times) {
712                 /* center time is already handled. */
713                 if(relative_time == 0.0f) {
714                         continue;
715                 }
716
717                 VLOG(1) << "Synchronizing motion for the relative time "
718                         << relative_time << ".";
719
720                 /* fixed shutter time to get previous and next frame for motion pass */
721                 float shuttertime = scene->motion_shutter_time();
722
723                 /* compute frame and subframe time */
724                 float time = frame_center + subframe_center + frame_center_delta + relative_time * shuttertime * 0.5f;
725                 int frame = (int)floorf(time);
726                 float subframe = time - frame;
727
728                 /* change frame */
729                 python_thread_state_restore(python_thread_state);
730                 b_engine.frame_set(frame, subframe);
731                 python_thread_state_save(python_thread_state);
732
733                 /* sync camera, only supports two times at the moment */
734                 if(relative_time == -1.0f || relative_time == 1.0f) {
735                         sync_camera_motion(b_render,
736                                            b_cam,
737                                            width, height,
738                                            relative_time);
739                 }
740
741                 /* sync object */
742                 sync_objects(relative_time);
743         }
744
745         /* we need to set the python thread state again because this
746          * function assumes it is being executed from python and will
747          * try to save the thread state */
748         python_thread_state_restore(python_thread_state);
749         b_engine.frame_set(frame_center, subframe_center);
750         python_thread_state_save(python_thread_state);
751
752         /* tag camera for motion update */
753         if(scene->camera->motion_modified(prevcam))
754                 scene->camera->tag_update();
755 }
756
757 CCL_NAMESPACE_END
758