Cycles: Camera frustum space object culling scene simplification
[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 "camera.h"
18 #include "integrator.h"
19 #include "graph.h"
20 #include "light.h"
21 #include "mesh.h"
22 #include "object.h"
23 #include "scene.h"
24 #include "nodes.h"
25 #include "particles.h"
26 #include "shader.h"
27
28 #include "blender_sync.h"
29 #include "blender_util.h"
30
31 #include "util_foreach.h"
32 #include "util_hash.h"
33 #include "util_logging.h"
34
35 CCL_NAMESPACE_BEGIN
36
37 /* Utilities */
38
39 bool BlenderSync::BKE_object_is_modified(BL::Object b_ob)
40 {
41         /* test if we can instance or if the object is modified */
42         if(b_ob.type() == BL::Object::type_META) {
43                 /* multi-user and dupli metaballs are fused, can't instance */
44                 return true;
45         }
46         else if(ccl::BKE_object_is_modified(b_ob, b_scene, preview)) {
47                 /* modifiers */
48                 return true;
49         }
50         else {
51                 /* object level material links */
52                 BL::Object::material_slots_iterator slot;
53                 for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot)
54                         if(slot->link() == BL::MaterialSlot::link_OBJECT)
55                                 return true;
56         }
57
58         return false;
59 }
60
61 bool BlenderSync::object_is_mesh(BL::Object b_ob)
62 {
63         BL::ID b_ob_data = b_ob.data();
64
65         return (b_ob_data && (b_ob_data.is_a(&RNA_Mesh) ||
66                 b_ob_data.is_a(&RNA_Curve) || b_ob_data.is_a(&RNA_MetaBall)));
67 }
68
69 bool BlenderSync::object_is_light(BL::Object b_ob)
70 {
71         BL::ID b_ob_data = b_ob.data();
72
73         return (b_ob_data && b_ob_data.is_a(&RNA_Lamp));
74 }
75
76 static uint object_ray_visibility(BL::Object b_ob)
77 {
78         PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility");
79         uint flag = 0;
80
81         flag |= get_boolean(cvisibility, "camera")? PATH_RAY_CAMERA: 0;
82         flag |= get_boolean(cvisibility, "diffuse")? PATH_RAY_DIFFUSE: 0;
83         flag |= get_boolean(cvisibility, "glossy")? PATH_RAY_GLOSSY: 0;
84         flag |= get_boolean(cvisibility, "transmission")? PATH_RAY_TRANSMIT: 0;
85         flag |= get_boolean(cvisibility, "shadow")? PATH_RAY_SHADOW: 0;
86         flag |= get_boolean(cvisibility, "scatter")? PATH_RAY_VOLUME_SCATTER: 0;
87
88         return flag;
89 }
90
91 /* Light */
92
93 void BlenderSync::sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm, bool *use_portal)
94 {
95         /* test if we need to sync */
96         Light *light;
97         ObjectKey key(b_parent, persistent_id, b_ob);
98
99         if(!light_map.sync(&light, b_ob, b_parent, key)) {
100                 if(light->is_portal)
101                         *use_portal = true;
102                 return;
103         }
104         
105         BL::Lamp b_lamp(b_ob.data());
106
107         /* type */
108         switch(b_lamp.type()) {
109                 case BL::Lamp::type_POINT: {
110                         BL::PointLamp b_point_lamp(b_lamp);
111                         light->size = b_point_lamp.shadow_soft_size();
112                         light->type = LIGHT_POINT;
113                         break;
114                 }
115                 case BL::Lamp::type_SPOT: {
116                         BL::SpotLamp b_spot_lamp(b_lamp);
117                         light->size = b_spot_lamp.shadow_soft_size();
118                         light->type = LIGHT_SPOT;
119                         light->spot_angle = b_spot_lamp.spot_size();
120                         light->spot_smooth = b_spot_lamp.spot_blend();
121                         break;
122                 }
123                 case BL::Lamp::type_HEMI: {
124                         light->type = LIGHT_DISTANT;
125                         light->size = 0.0f;
126                         break;
127                 }
128                 case BL::Lamp::type_SUN: {
129                         BL::SunLamp b_sun_lamp(b_lamp);
130                         light->size = b_sun_lamp.shadow_soft_size();
131                         light->type = LIGHT_DISTANT;
132                         break;
133                 }
134                 case BL::Lamp::type_AREA: {
135                         BL::AreaLamp b_area_lamp(b_lamp);
136                         light->size = 1.0f;
137                         light->axisu = transform_get_column(&tfm, 0);
138                         light->axisv = transform_get_column(&tfm, 1);
139                         light->sizeu = b_area_lamp.size();
140                         if(b_area_lamp.shape() == BL::AreaLamp::shape_RECTANGLE)
141                                 light->sizev = b_area_lamp.size_y();
142                         else
143                                 light->sizev = light->sizeu;
144                         light->type = LIGHT_AREA;
145                         break;
146                 }
147         }
148
149         /* location and (inverted!) direction */
150         light->co = transform_get_column(&tfm, 3);
151         light->dir = -transform_get_column(&tfm, 2);
152
153         /* shader */
154         vector<uint> used_shaders;
155
156         find_shader(b_lamp, used_shaders, scene->default_light);
157
158         if(used_shaders.size() == 0)
159                 used_shaders.push_back(scene->default_light);
160
161         light->shader = used_shaders[0];
162
163         /* shadow */
164         PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
165         PointerRNA clamp = RNA_pointer_get(&b_lamp.ptr, "cycles");
166         light->cast_shadow = get_boolean(clamp, "cast_shadow");
167         light->use_mis = get_boolean(clamp, "use_multiple_importance_sampling");
168         
169         int samples = get_int(clamp, "samples");
170         if(get_boolean(cscene, "use_square_samples"))
171                 light->samples = samples * samples;
172         else
173                 light->samples = samples;
174
175         light->max_bounces = get_int(clamp, "max_bounces");
176
177         if(light->type == LIGHT_AREA)
178                 light->is_portal = get_boolean(clamp, "is_portal");
179         else
180                 light->is_portal = false;
181
182         if(light->is_portal)
183                 *use_portal = true;
184
185         /* visibility */
186         uint visibility = object_ray_visibility(b_ob);
187         light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
188         light->use_glossy = (visibility & PATH_RAY_GLOSSY) != 0;
189         light->use_transmission = (visibility & PATH_RAY_TRANSMIT) != 0;
190         light->use_scatter = (visibility & PATH_RAY_VOLUME_SCATTER) != 0;
191
192         /* tag */
193         light->tag_update(scene);
194 }
195
196 void BlenderSync::sync_background_light(bool use_portal)
197 {
198         BL::World b_world = b_scene.world();
199
200         if(b_world) {
201                 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
202                 PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
203                 bool sample_as_light = get_boolean(cworld, "sample_as_light");
204
205                 if(sample_as_light || use_portal) {
206                         /* test if we need to sync */
207                         Light *light;
208                         ObjectKey key(b_world, 0, b_world);
209
210                         if(light_map.sync(&light, b_world, b_world, key) ||
211                                 world_recalc ||
212                                 b_world.ptr.data != world_map)
213                         {
214                                 light->type = LIGHT_BACKGROUND;
215                                 light->map_resolution  = get_int(cworld, "sample_map_resolution");
216                                 light->shader = scene->default_background;
217                                 light->use_mis = sample_as_light;
218                                 light->max_bounces = get_int(cworld, "max_bounces");
219
220                                 int samples = get_int(cworld, "samples");
221                                 if(get_boolean(cscene, "use_square_samples"))
222                                         light->samples = samples * samples;
223                                 else
224                                         light->samples = samples;
225
226                                 light->tag_update(scene);
227                                 light_map.set_recalc(b_world);
228                         }
229                 }
230         }
231
232         world_map = b_world.ptr.data;
233         world_recalc = false;
234 }
235
236 /* Object */
237
238 /* TODO(sergey): Not really optimal, consider approaches based on k-DOP in order
239  * to reduce number of objects which are wrongly considered visible.
240  */
241 static bool object_boundbox_clip(Scene *scene,
242                                  BL::Object b_ob,
243                                  Transform& tfm,
244                                  float margin)
245 {
246         Camera *cam = scene->camera;
247         Transform& worldtondc = cam->worldtondc;
248         BL::Array<float, 24> boundbox = b_ob.bound_box();
249         float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
250                bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
251         bool all_behind = true;
252         for(int i = 0; i < 8; ++i) {
253                 float3 p = make_float3(boundbox[3 * i + 0],
254                                        boundbox[3 * i + 1],
255                                        boundbox[3 * i + 2]);
256                 p = transform_point(&tfm, p);
257                 p = transform_point(&worldtondc, p);
258                 if(p.z >= -margin) {
259                         all_behind = false;
260                 }
261                 p /= p.z;
262                 bb_min = min(bb_min, p);
263                 bb_max = max(bb_max, p);
264         }
265         if(!all_behind) {
266                 if(bb_min.x >= 1.0f + margin ||
267                    bb_min.y >= 1.0f + margin ||
268                    bb_max.x <= -margin ||
269                    bb_max.y <= -margin)
270                 {
271                         return true;
272                 }
273                 return false;
274         }
275         return true;
276 }
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                                  bool use_camera_cull,
286                                  float camera_cull_margin,
287                                  bool *use_portal)
288 {
289         BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
290         bool motion = motion_time != 0.0f;
291         
292         /* light is handled separately */
293         if(object_is_light(b_ob)) {
294                 /* don't use lamps for excluded layers used as mask layer */
295                 if(!motion && !((layer_flag & render_layer.holdout_layer) && (layer_flag & render_layer.exclude_layer)))
296                         sync_light(b_parent, persistent_id, b_ob, tfm, use_portal);
297
298                 return NULL;
299         }
300
301         /* only interested in object that we can create meshes from */
302         if(!object_is_mesh(b_ob))
303                 return NULL;
304
305         /* Perform camera space culling. */
306         if(use_camera_cull && object_boundbox_clip(scene, b_ob, tfm, camera_cull_margin)) {
307                 return NULL;
308         }
309
310         /* key to lookup object */
311         ObjectKey key(b_parent, persistent_id, b_ob);
312         Object *object;
313
314         /* motion vector case */
315         if(motion) {
316                 object = object_map.find(key);
317
318                 if(object && (scene->need_motion() == Scene::MOTION_PASS ||
319                               object_use_motion(b_parent, b_ob)))
320                 {
321                         /* object transformation */
322                         if(tfm != object->tfm) {
323                                 VLOG(1) << "Object " << b_ob.name() << " motion detected.";
324                                 if(motion_time == -1.0f) {
325                                         object->motion.pre = tfm;
326                                         object->use_motion = true;
327                                 }
328                                 else if(motion_time == 1.0f) {
329                                         object->motion.post = tfm;
330                                         object->use_motion = true;
331                                 }
332                         }
333
334                         /* mesh deformation */
335                         if(object->mesh)
336                                 sync_mesh_motion(b_ob, object, motion_time);
337                 }
338
339                 return object;
340         }
341
342         /* test if we need to sync */
343         bool object_updated = false;
344
345         if(object_map.sync(&object, b_ob, b_parent, key))
346                 object_updated = true;
347         
348         bool use_holdout = (layer_flag & render_layer.holdout_layer) != 0;
349         
350         /* mesh sync */
351         object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
352
353         /* special case not tracked by object update flags */
354
355         /* holdout */
356         if(use_holdout != object->use_holdout) {
357                 object->use_holdout = use_holdout;
358                 scene->object_manager->tag_update(scene);
359                 object_updated = true;
360         }
361
362         /* visibility flags for both parent and child */
363         uint visibility = object_ray_visibility(b_ob) & PATH_RAY_ALL_VISIBILITY;
364         if(b_parent.ptr.data != b_ob.ptr.data) {
365                 visibility &= object_ray_visibility(b_parent);
366         }
367
368         /* make holdout objects on excluded layer invisible for non-camera rays */
369         if(use_holdout && (layer_flag & render_layer.exclude_layer))
370                 visibility &= ~(PATH_RAY_ALL_VISIBILITY - PATH_RAY_CAMERA);
371
372         /* camera flag is not actually used, instead is tested against render layer
373          * flags */
374         if(visibility & PATH_RAY_CAMERA) {
375                 visibility |= layer_flag << PATH_RAY_LAYER_SHIFT;
376                 visibility &= ~PATH_RAY_CAMERA;
377         }
378
379         if(visibility != object->visibility) {
380                 object->visibility = visibility;
381                 object_updated = true;
382         }
383
384         /* object sync
385          * transform comparison should not be needed, but duplis don't work perfect
386          * in the depsgraph and may not signal changes, so this is a workaround */
387         if(object_updated || (object->mesh && object->mesh->need_update) || tfm != object->tfm) {
388                 object->name = b_ob.name().c_str();
389                 object->pass_id = b_ob.pass_index();
390                 object->tfm = tfm;
391                 object->motion.pre = tfm;
392                 object->motion.post = tfm;
393                 object->use_motion = false;
394
395                 /* motion blur */
396                 if(scene->need_motion() == Scene::MOTION_BLUR && object->mesh) {
397                         Mesh *mesh = object->mesh;
398
399                         mesh->use_motion_blur = false;
400
401                         if(object_use_motion(b_parent, b_ob)) {
402                                 if(object_use_deform_motion(b_parent, b_ob)) {
403                                         mesh->motion_steps = object_motion_steps(b_ob);
404                                         mesh->use_motion_blur = true;
405                                 }
406
407                                 vector<float> times = object->motion_times();
408                                 foreach(float time, times)
409                                         motion_times.insert(time);
410                         }
411                 }
412
413                 /* random number */
414                 object->random_id = hash_string(object->name.c_str());
415
416                 if(persistent_id) {
417                         for(int i = 0; i < OBJECT_PERSISTENT_ID_SIZE; i++)
418                                 object->random_id = hash_int_2d(object->random_id, persistent_id[i]);
419                 }
420                 else
421                         object->random_id = hash_int_2d(object->random_id, 0);
422
423                 if(b_parent.ptr.data != b_ob.ptr.data)
424                         object->random_id ^= hash_int(hash_string(b_parent.name().c_str()));
425
426                 /* dupli texture coordinates */
427                 if(b_dupli_ob) {
428                         object->dupli_generated = 0.5f*get_float3(b_dupli_ob.orco()) - make_float3(0.5f, 0.5f, 0.5f);
429                         object->dupli_uv = get_float2(b_dupli_ob.uv());
430                 }
431                 else {
432                         object->dupli_generated = make_float3(0.0f, 0.0f, 0.0f);
433                         object->dupli_uv = make_float2(0.0f, 0.0f);
434                 }
435
436                 object->tag_update(scene);
437         }
438
439         return object;
440 }
441
442 static bool object_render_hide_original(BL::Object::type_enum ob_type, BL::Object::dupli_type_enum dupli_type)
443 {
444         /* metaball exception, they duplicate self */
445         if(ob_type == BL::Object::type_META)
446                 return false;
447
448         return (dupli_type == BL::Object::dupli_type_VERTS ||
449                 dupli_type == BL::Object::dupli_type_FACES ||
450                 dupli_type == BL::Object::dupli_type_FRAMES);
451 }
452
453 static bool object_render_hide(BL::Object b_ob, bool top_level, bool parent_hide, bool& hide_triangles)
454 {
455         /* check if we should render or hide particle emitter */
456         BL::Object::particle_systems_iterator b_psys;
457
458         bool hair_present = false;
459         bool show_emitter = false;
460         bool hide_emitter = false;
461         bool hide_as_dupli_parent = false;
462         bool hide_as_dupli_child_original = false;
463
464         for(b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys) {
465                 if((b_psys->settings().render_type() == BL::ParticleSettings::render_type_PATH) &&
466                    (b_psys->settings().type()==BL::ParticleSettings::type_HAIR))
467                         hair_present = true;
468
469                 if(b_psys->settings().use_render_emitter())
470                         show_emitter = true;
471                 else
472                         hide_emitter = true;
473         }
474
475         if(show_emitter)
476                 hide_emitter = false;
477
478         /* duplicators hidden by default, except dupliframes which duplicate self */
479         if(b_ob.is_duplicator())
480                 if(top_level || b_ob.dupli_type() != BL::Object::dupli_type_FRAMES)
481                         hide_as_dupli_parent = true;
482
483         /* hide original object for duplis */
484         BL::Object parent = b_ob.parent();
485         while(parent) {
486                 if(object_render_hide_original(b_ob.type(),
487                                                parent.dupli_type()))
488                 {
489                         if(parent_hide) {
490                                 hide_as_dupli_child_original = true;
491                                 break;
492                         }
493                 }
494                 parent = parent.parent();
495         }
496         
497         hide_triangles = hide_emitter;
498
499         if(show_emitter) {
500                 return false;
501         }
502         else if(hair_present) {
503                 return hide_as_dupli_child_original;
504         }
505         else {
506                 return (hide_as_dupli_parent || hide_as_dupli_child_original);
507         }
508 }
509
510 static bool object_render_hide_duplis(BL::Object b_ob)
511 {
512         BL::Object parent = b_ob.parent();
513
514         return (parent && object_render_hide_original(b_ob.type(), parent.dupli_type()));
515 }
516
517 /* Object Loop */
518
519 void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
520 {
521         /* layer data */
522         uint scene_layer = render_layer.scene_layer;
523         bool motion = motion_time != 0.0f;
524         
525         if(!motion) {
526                 /* prepare for sync */
527                 light_map.pre_sync();
528                 mesh_map.pre_sync();
529                 object_map.pre_sync();
530                 particle_system_map.pre_sync();
531                 motion_times.clear();
532         }
533         else {
534                 mesh_motion_synced.clear();
535         }
536
537         bool allow_camera_cull = false;
538         float camera_cull_margin = 0.0f;
539         if(b_scene.render().use_simplify()) {
540                 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
541                 allow_camera_cull = scene->camera->type != CAMERA_PANORAMA &&
542                                     !b_scene.render().use_multiview() &&
543                                     get_boolean(cscene, "use_camera_cull");
544                 if(allow_camera_cull) {
545                         camera_cull_margin = get_float(cscene, "camera_cull_margin");
546                 }
547         }
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 = preview ? 1 : 2;
557
558         bool cancel = false;
559         bool use_portal = false;
560
561         for(; b_sce && !cancel; b_sce = b_sce.background_set()) {
562                 for(b_sce.object_bases.begin(b_base); b_base != b_sce.object_bases.end() && !cancel; ++b_base) {
563                         BL::Object b_ob = b_base->object();
564                         bool hide = (render_layer.use_viewport_visibility)? b_ob.hide(): b_ob.hide_render();
565                         uint ob_layer = get_layer(b_base->layers(), b_base->layers_local_view(), render_layer.use_localview, object_is_light(b_ob));
566                         hide = hide || !(ob_layer & scene_layer);
567
568                         if(!hide) {
569                                 progress.set_sync_status("Synchronizing object", b_ob.name());
570
571                                 PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
572                                 bool use_camera_cull = allow_camera_cull && get_boolean(cobject, "use_camera_cull");
573                                 if(use_camera_cull) {
574                                         /* Need to have proper projection matrix. */
575                                         scene->camera->update();
576                                 }
577                                 if(b_ob.is_duplicator() && !object_render_hide_duplis(b_ob)) {
578                                         /* dupli objects */
579                                         b_ob.dupli_list_create(b_scene, dupli_settings);
580
581                                         BL::Object::dupli_list_iterator b_dup;
582
583                                         for(b_ob.dupli_list.begin(b_dup); b_dup != b_ob.dupli_list.end(); ++b_dup) {
584                                                 Transform tfm = get_transform(b_dup->matrix());
585                                                 BL::Object b_dup_ob = b_dup->object();
586                                                 bool dup_hide = (b_v3d)? b_dup_ob.hide(): b_dup_ob.hide_render();
587                                                 bool in_dupli_group = (b_dup->type() == BL::DupliObject::type_GROUP);
588                                                 bool hide_tris;
589
590                                                 if(!(b_dup->hide() || dup_hide || object_render_hide(b_dup_ob, false, in_dupli_group, hide_tris))) {
591                                                         /* the persistent_id allows us to match dupli objects
592                                                          * between frames and updates */
593                                                         BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id();
594
595                                                         /* sync object and mesh or light data */
596                                                         Object *object = sync_object(b_ob,
597                                                                                      persistent_id.data,
598                                                                                      *b_dup,
599                                                                                      tfm,
600                                                                                      ob_layer,
601                                                                                      motion_time,
602                                                                                      hide_tris,
603                                                                                      use_camera_cull,
604                                                                                      camera_cull_margin,
605                                                                                      &use_portal);
606
607                                                         /* sync possible particle data, note particle_id
608                                                          * starts counting at 1, first is dummy particle */
609                                                         if(!motion && object) {
610                                                                 sync_dupli_particle(b_ob, *b_dup, object);
611                                                         }
612
613                                                 }
614                                         }
615
616                                         b_ob.dupli_list_clear();
617                                 }
618
619                                 /* test if object needs to be hidden */
620                                 bool hide_tris;
621
622                                 if(!object_render_hide(b_ob, true, true, hide_tris)) {
623                                         /* object itself */
624                                         Transform tfm = get_transform(b_ob.matrix_world());
625                                         sync_object(b_ob,
626                                                     NULL,
627                                                     PointerRNA_NULL,
628                                                     tfm,
629                                                     ob_layer,
630                                                     motion_time,
631                                                     hide_tris,
632                                                     use_camera_cull,
633                                                     camera_cull_margin,
634                                                     &use_portal);
635                                 }
636                         }
637
638                         cancel = progress.get_cancel();
639                 }
640         }
641
642         progress.set_sync_status("");
643
644         if(!cancel && !motion) {
645                 sync_background_light(use_portal);
646
647                 /* handle removed data and modified pointers */
648                 if(light_map.post_sync())
649                         scene->light_manager->tag_update(scene);
650                 if(mesh_map.post_sync())
651                         scene->mesh_manager->tag_update(scene);
652                 if(object_map.post_sync())
653                         scene->object_manager->tag_update(scene);
654                 if(particle_system_map.post_sync())
655                         scene->particle_system_manager->tag_update(scene);
656         }
657
658         if(motion)
659                 mesh_motion_synced.clear();
660 }
661
662 void BlenderSync::sync_motion(BL::SpaceView3D b_v3d, BL::Object b_override, void **python_thread_state)
663 {
664         if(scene->need_motion() == Scene::MOTION_NONE)
665                 return;
666
667         /* get camera object here to deal with camera switch */
668         BL::Object b_cam = b_scene.camera();
669         if(b_override)
670                 b_cam = b_override;
671
672         Camera prevcam = *(scene->camera);
673
674         int frame_center = b_scene.frame_current();
675
676         /* always sample these times for camera motion */
677         motion_times.insert(-1.0f);
678         motion_times.insert(1.0f);
679
680         /* note iteration over motion_times set happens in sorted order */
681         foreach(float relative_time, motion_times) {
682                 /* fixed shutter time to get previous and next frame for motion pass */
683                 float shuttertime;
684
685                 if(scene->need_motion() == Scene::MOTION_PASS)
686                         shuttertime = 2.0f;
687                 else
688                         shuttertime = scene->camera->shuttertime;
689
690                 /* compute frame and subframe time */
691                 float time = frame_center + relative_time * shuttertime * 0.5f;
692                 int frame = (int)floorf(time);
693                 float subframe = time - frame;
694
695                 /* change frame */
696                 python_thread_state_restore(python_thread_state);
697                 b_engine.frame_set(frame, subframe);
698                 python_thread_state_save(python_thread_state);
699
700                 /* sync camera, only supports two times at the moment */
701                 if(relative_time == -1.0f || relative_time == 1.0f)
702                         sync_camera_motion(b_cam, relative_time);
703
704                 /* sync object */
705                 sync_objects(b_v3d, relative_time);
706         }
707
708         /* we need to set the python thread state again because this
709          * function assumes it is being executed from python and will
710          * try to save the thread state */
711         python_thread_state_restore(python_thread_state);
712         b_engine.frame_set(frame_center, 0.0f);
713         python_thread_state_save(python_thread_state);
714
715         /* tag camera for motion update */
716         if(scene->camera->motion_modified(prevcam))
717                 scene->camera->tag_update();
718 }
719
720 CCL_NAMESPACE_END
721