add BLI_strcpy_rlen, replace strcat, which was used in misleading way.
[blender.git] / intern / cycles / blender / blender_camera.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include "camera.h"
20 #include "scene.h"
21
22 #include "blender_sync.h"
23 #include "blender_util.h"
24
25 CCL_NAMESPACE_BEGIN
26
27 /* Blender Camera Intermediate: we first convert both the offline and 3d view
28  * render camera to this, and from there convert to our native camera format. */
29
30 struct BlenderCamera {
31         float nearclip;
32         float farclip;
33
34         CameraType type;
35         float ortho_scale;
36
37         float lens;
38         float shuttertime;
39
40         float aperturesize;
41         uint apertureblades;
42         float aperturerotation;
43         float focaldistance;
44
45         float2 shift;
46         float2 offset;
47         float zoom;
48
49         float2 pixelaspect;
50
51         PanoramaType panorama_type;
52         float fisheye_fov;
53         float fisheye_lens;
54
55         enum { AUTO, HORIZONTAL, VERTICAL } sensor_fit;
56         float sensor_width;
57         float sensor_height;
58
59         int full_width;
60         int full_height;
61
62         BoundBox2D border;
63         BoundBox2D pano_viewplane;
64
65         Transform matrix;
66 };
67
68 static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings b_render, BL::Scene b_scene)
69 {
70         memset(bcam, 0, sizeof(BlenderCamera));
71
72         bcam->type = CAMERA_PERSPECTIVE;
73         bcam->zoom = 1.0f;
74         bcam->pixelaspect = make_float2(1.0f, 1.0f);
75         bcam->sensor_width = 32.0f;
76         bcam->sensor_height = 18.0f;
77         bcam->sensor_fit = BlenderCamera::AUTO;
78         bcam->shuttertime = 1.0f;
79         bcam->border.right = 1.0f;
80         bcam->border.top = 1.0f;
81         bcam->pano_viewplane.right = 1.0f;
82         bcam->pano_viewplane.top = 1.0f;
83
84         /* render resolution */
85         bcam->full_width = render_resolution_x(b_render);
86         bcam->full_height = render_resolution_y(b_render);
87 }
88
89 static float blender_camera_focal_distance(BL::Object b_ob, BL::Camera b_camera)
90 {
91         BL::Object b_dof_object = b_camera.dof_object();
92
93         if(!b_dof_object)
94                 return b_camera.dof_distance();
95         
96         /* for dof object, return distance along camera Z direction */
97         Transform obmat = transform_clear_scale(get_transform(b_ob.matrix_world()));
98         Transform dofmat = get_transform(b_dof_object.matrix_world());
99         Transform mat = transform_inverse(obmat) * dofmat;
100
101         return fabsf(transform_get_column(&mat, 3).z);
102 }
103
104 static void blender_camera_from_object(BlenderCamera *bcam, BL::Object b_ob, bool skip_panorama = false)
105 {
106         BL::ID b_ob_data = b_ob.data();
107
108         if(b_ob_data.is_a(&RNA_Camera)) {
109                 BL::Camera b_camera(b_ob_data);
110                 PointerRNA ccamera = RNA_pointer_get(&b_camera.ptr, "cycles");
111
112                 bcam->nearclip = b_camera.clip_start();
113                 bcam->farclip = b_camera.clip_end();
114
115                 switch(b_camera.type())
116                 {
117                         case BL::Camera::type_ORTHO:
118                                 bcam->type = CAMERA_ORTHOGRAPHIC;
119                                 break;
120                         case BL::Camera::type_PANO:
121                                 if(!skip_panorama)
122                                         bcam->type = CAMERA_PANORAMA;
123                                 else
124                                         bcam->type = CAMERA_PERSPECTIVE;
125                                 break;
126                         case BL::Camera::type_PERSP:
127                         default:
128                                 bcam->type = CAMERA_PERSPECTIVE;
129                                 break;
130                 }       
131
132                 switch(RNA_enum_get(&ccamera, "panorama_type"))
133                 {
134                         case 1:
135                                 bcam->panorama_type = PANORAMA_FISHEYE_EQUIDISTANT;
136                                 break;
137                         case 2:
138                                 bcam->panorama_type = PANORAMA_FISHEYE_EQUISOLID;
139                                 break;
140                         case 0:
141                         default:
142                                 bcam->panorama_type = PANORAMA_EQUIRECTANGULAR;
143                                 break;
144                 }       
145
146                 bcam->fisheye_fov = RNA_float_get(&ccamera, "fisheye_fov");
147                 bcam->fisheye_lens = RNA_float_get(&ccamera, "fisheye_lens");
148
149                 bcam->ortho_scale = b_camera.ortho_scale();
150
151                 bcam->lens = b_camera.lens();
152
153                 /* allow f/stop number to change aperture_size but still
154                  * give manual control over aperture radius */
155                 int aperture_type = RNA_enum_get(&ccamera, "aperture_type");
156
157                 if(aperture_type == 1) {
158                         float fstop = RNA_float_get(&ccamera, "aperture_fstop");
159                         fstop = max(fstop, 1e-5f);
160
161                         if(bcam->type == CAMERA_ORTHOGRAPHIC)
162                                 bcam->aperturesize = 1.0f/(2.0f*fstop);
163                         else
164                                 bcam->aperturesize = (bcam->lens*1e-3f)/(2.0f*fstop);
165                 }
166                 else
167                         bcam->aperturesize = RNA_float_get(&ccamera, "aperture_size");
168
169                 bcam->apertureblades = RNA_int_get(&ccamera, "aperture_blades");
170                 bcam->aperturerotation = RNA_float_get(&ccamera, "aperture_rotation");
171                 bcam->focaldistance = blender_camera_focal_distance(b_ob, b_camera);
172
173                 bcam->shift.x = b_camera.shift_x();
174                 bcam->shift.y = b_camera.shift_y();
175
176                 bcam->sensor_width = b_camera.sensor_width();
177                 bcam->sensor_height = b_camera.sensor_height();
178
179                 if(b_camera.sensor_fit() == BL::Camera::sensor_fit_AUTO)
180                         bcam->sensor_fit = BlenderCamera::AUTO;
181                 else if(b_camera.sensor_fit() == BL::Camera::sensor_fit_HORIZONTAL)
182                         bcam->sensor_fit = BlenderCamera::HORIZONTAL;
183                 else
184                         bcam->sensor_fit = BlenderCamera::VERTICAL;
185         }
186         else {
187                 /* from lamp not implemented yet */
188         }
189 }
190
191 static Transform blender_camera_matrix(const Transform& tfm, CameraType type)
192 {
193         Transform result;
194
195         if(type == CAMERA_PANORAMA) {
196                 /* make it so environment camera needs to be pointed in the direction
197                  * of the positive x-axis to match an environment texture, this way
198                  * it is looking at the center of the texture */
199                 result = tfm *
200                         make_transform( 0.0f, -1.0f, 0.0f, 0.0f,
201                                         0.0f,  0.0f, 1.0f, 0.0f,
202                                        -1.0f,  0.0f, 0.0f, 0.0f,
203                                         0.0f,  0.0f, 0.0f, 1.0f);
204         }
205         else {
206                 /* note the blender camera points along the negative z-axis */
207                 result = tfm * transform_scale(1.0f, 1.0f, -1.0f);
208         }
209
210         return transform_clear_scale(result);
211 }
212
213 static void blender_camera_viewplane(BlenderCamera *bcam, int width, int height,
214         BoundBox2D *viewplane, float *aspectratio, float *sensor_size)
215 {
216         /* dimensions */
217         float xratio = width*bcam->pixelaspect.x;
218         float yratio = height*bcam->pixelaspect.y;
219
220         /* compute x/y aspect and ratio */
221         float xaspect, yaspect;
222         bool horizontal_fit;
223
224         /* sensor fitting */
225         if(bcam->sensor_fit == BlenderCamera::AUTO) {
226                 horizontal_fit = (xratio > yratio);
227                 *sensor_size = bcam->sensor_width;
228         }
229         else if(bcam->sensor_fit == BlenderCamera::HORIZONTAL) {
230                 horizontal_fit = true;
231                 *sensor_size = bcam->sensor_width;
232         }
233         else {
234                 horizontal_fit = false;
235                 *sensor_size = bcam->sensor_height;
236         }
237
238         if(horizontal_fit) {
239                 *aspectratio = xratio/yratio;
240                 xaspect = *aspectratio;
241                 yaspect = 1.0f;
242         }
243         else {
244                 *aspectratio = yratio/xratio;
245                 xaspect = 1.0f;
246                 yaspect = *aspectratio;
247         }
248
249         /* modify aspect for orthographic scale */
250         if(bcam->type == CAMERA_ORTHOGRAPHIC) {
251                 xaspect = xaspect*bcam->ortho_scale/(*aspectratio*2.0f);
252                 yaspect = yaspect*bcam->ortho_scale/(*aspectratio*2.0f);
253                 *aspectratio = bcam->ortho_scale/2.0f;
254         }
255
256         if(bcam->type == CAMERA_PANORAMA) {
257                 /* set viewplane */
258                 *viewplane = bcam->pano_viewplane;
259         }
260         else {
261                 /* set viewplane */
262                 viewplane->left = -xaspect;
263                 viewplane->right = xaspect;
264                 viewplane->bottom = -yaspect;
265                 viewplane->top = yaspect;
266
267                 /* zoom for 3d camera view */
268                 *viewplane = (*viewplane) * bcam->zoom;
269
270                 /* modify viewplane with camera shift and 3d camera view offset */
271                 float dx = 2.0f*(*aspectratio*bcam->shift.x + bcam->offset.x*xaspect*2.0f);
272                 float dy = 2.0f*(*aspectratio*bcam->shift.y + bcam->offset.y*yaspect*2.0f);
273
274                 viewplane->left += dx;
275                 viewplane->right += dx;
276                 viewplane->bottom += dy;
277                 viewplane->top += dy;
278         }
279 }
280
281 static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int height)
282 {
283         /* copy camera to compare later */
284         Camera prevcam = *cam;
285         float aspectratio, sensor_size;
286
287         /* viewplane */
288         blender_camera_viewplane(bcam, width, height,
289                 &cam->viewplane, &aspectratio, &sensor_size);
290
291         /* panorama sensor */
292         if (bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) {
293                 float fit_xratio = bcam->full_width*bcam->pixelaspect.x;
294                 float fit_yratio = bcam->full_height*bcam->pixelaspect.y;
295                 bool horizontal_fit;
296                 float sensor_size;
297
298                 if(bcam->sensor_fit == BlenderCamera::AUTO) {
299                         horizontal_fit = (fit_xratio > fit_yratio);
300                         sensor_size = bcam->sensor_width;
301                 }
302                 else if(bcam->sensor_fit == BlenderCamera::HORIZONTAL) {
303                         horizontal_fit = true;
304                         sensor_size = bcam->sensor_width;
305                 }
306                 else { /* vertical */
307                         horizontal_fit = false;
308                         sensor_size = bcam->sensor_height;
309                 }
310
311                 if(horizontal_fit) {
312                         cam->sensorwidth = sensor_size;
313                         cam->sensorheight = sensor_size * fit_yratio / fit_xratio;
314                 }
315                 else {
316                         cam->sensorwidth = sensor_size * fit_xratio / fit_yratio;
317                         cam->sensorheight = sensor_size;
318                 }
319         }
320
321         /* clipping distances */
322         cam->nearclip = bcam->nearclip;
323         cam->farclip = bcam->farclip;
324
325         /* type */
326         cam->type = bcam->type;
327
328         /* panorama */
329         cam->panorama_type = bcam->panorama_type;
330         cam->fisheye_fov = bcam->fisheye_fov;
331         cam->fisheye_lens = bcam->fisheye_lens;
332
333         /* perspective */
334         cam->fov = 2.0f * atanf((0.5f * sensor_size) / bcam->lens / aspectratio);
335         cam->focaldistance = bcam->focaldistance;
336         cam->aperturesize = bcam->aperturesize;
337         cam->blades = bcam->apertureblades;
338         cam->bladesrotation = bcam->aperturerotation;
339
340         /* transform */
341         cam->matrix = blender_camera_matrix(bcam->matrix, bcam->type);
342         cam->motion.pre = cam->matrix;
343         cam->motion.post = cam->matrix;
344         cam->use_motion = false;
345         cam->shuttertime = bcam->shuttertime;
346
347         /* border */
348         cam->border = bcam->border;
349
350         /* set update flag */
351         if(cam->modified(prevcam))
352                 cam->tag_update();
353 }
354
355 /* Sync Render Camera */
356
357 void BlenderSync::sync_camera(BL::RenderSettings b_render, BL::Object b_override, int width, int height)
358 {
359         BlenderCamera bcam;
360         blender_camera_init(&bcam, b_render, b_scene);
361
362         /* pixel aspect */
363         bcam.pixelaspect.x = b_render.pixel_aspect_x();
364         bcam.pixelaspect.y = b_render.pixel_aspect_y();
365         bcam.shuttertime = b_render.motion_blur_shutter();
366
367         /* border */
368         if(b_render.use_border()) {
369                 bcam.border.left = b_render.border_min_x();
370                 bcam.border.right = b_render.border_max_x();
371                 bcam.border.bottom = b_render.border_min_y();
372                 bcam.border.top = b_render.border_max_y();
373         }
374
375         /* camera object */
376         BL::Object b_ob = b_scene.camera();
377
378         if(b_override)
379                 b_ob = b_override;
380
381         if(b_ob) {
382                 blender_camera_from_object(&bcam, b_ob);
383                 bcam.matrix = get_transform(b_ob.matrix_world());
384         }
385
386         /* sync */
387         Camera *cam = scene->camera;
388         blender_camera_sync(cam, &bcam, width, height);
389 }
390
391 void BlenderSync::sync_camera_motion(BL::Object b_ob, int motion)
392 {
393         Camera *cam = scene->camera;
394
395         Transform tfm = get_transform(b_ob.matrix_world());
396         tfm = blender_camera_matrix(tfm, cam->type);
397
398         if(tfm != cam->matrix) {
399                 if(motion == -1)
400                         cam->motion.pre = tfm;
401                 else
402                         cam->motion.post = tfm;
403
404                 cam->use_motion = true;
405         }
406 }
407
408 /* Sync 3D View Camera */
409
410 static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_scene, BL::Object b_ob, BL::SpaceView3D b_v3d,
411         BL::RegionView3D b_rv3d, int width, int height, BoundBox2D *view_box, BoundBox2D *cam_box);
412
413 static void blender_camera_from_view(BlenderCamera *bcam, BL::Scene b_scene, BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height, bool skip_panorama = false)
414 {
415         /* 3d view parameters */
416         bcam->nearclip = b_v3d.clip_start();
417         bcam->farclip = b_v3d.clip_end();
418         bcam->lens = b_v3d.lens();
419         bcam->shuttertime = b_scene.render().motion_blur_shutter();
420
421         if(b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_CAMERA) {
422                 /* camera view */
423                 BL::Object b_ob = (b_v3d.lock_camera_and_layers())? b_scene.camera(): b_v3d.camera();
424
425                 if(b_ob) {
426                         blender_camera_from_object(bcam, b_ob, skip_panorama);
427
428                         if(!skip_panorama && bcam->type == CAMERA_PANORAMA) {
429                                 /* in panorama camera view, we map viewplane to camera border */
430                                 BoundBox2D view_box, cam_box;
431
432                                 blender_camera_view_subset(b_scene.render(), b_scene, b_ob, b_v3d, b_rv3d, width, height,
433                                         &view_box, &cam_box);
434
435                                 bcam->pano_viewplane = view_box.make_relative_to(cam_box);
436                         }
437                         else {
438                                 /* magic zoom formula */
439                                 bcam->zoom = (float)b_rv3d.view_camera_zoom();
440                                 bcam->zoom = (1.41421f + bcam->zoom/50.0f);
441                                 bcam->zoom *= bcam->zoom;
442                                 bcam->zoom = 2.0f/bcam->zoom;
443
444                                 /* offset */
445                                 bcam->offset = get_float2(b_rv3d.view_camera_offset());
446                         }
447                 }
448         }
449         else if(b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_ORTHO) {
450                 /* orthographic view */
451                 bcam->farclip *= 0.5f;
452                 bcam->nearclip = -bcam->farclip;
453
454                 float sensor_size;
455                 if(bcam->sensor_fit == BlenderCamera::VERTICAL)
456                         sensor_size = bcam->sensor_height;
457                 else
458                         sensor_size = bcam->sensor_width;
459
460                 bcam->type = CAMERA_ORTHOGRAPHIC;
461                 bcam->ortho_scale = b_rv3d.view_distance() * sensor_size / b_v3d.lens();
462         }
463
464         bcam->zoom *= 2.0f;
465
466         /* 3d view transform */
467         bcam->matrix = transform_inverse(get_transform(b_rv3d.view_matrix()));
468 }
469
470 static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_scene, BL::Object b_ob, BL::SpaceView3D b_v3d,
471         BL::RegionView3D b_rv3d, int width, int height, BoundBox2D *view_box, BoundBox2D *cam_box)
472 {
473         BoundBox2D cam, view;
474         float view_aspect, cam_aspect, sensor_size;
475
476         /* get viewport viewplane */
477         BlenderCamera view_bcam;
478         blender_camera_init(&view_bcam, b_render, b_scene);
479         blender_camera_from_view(&view_bcam, b_scene, b_v3d, b_rv3d, width, height, true);
480
481         blender_camera_viewplane(&view_bcam, width, height,
482                 &view, &view_aspect, &sensor_size);
483
484         /* get camera viewplane */
485         BlenderCamera cam_bcam;
486         blender_camera_init(&cam_bcam, b_render, b_scene);
487         blender_camera_from_object(&cam_bcam, b_ob, true);
488
489         blender_camera_viewplane(&cam_bcam, cam_bcam.full_width, cam_bcam.full_height,
490                 &cam, &cam_aspect, &sensor_size);
491         
492         /* return */
493         *view_box = view * (1.0f/view_aspect);
494         *cam_box = cam * (1.0f/cam_aspect);
495 }
496
497 static void blender_camera_border(BlenderCamera *bcam, BL::RenderSettings b_render, BL::Scene b_scene, BL::SpaceView3D b_v3d,
498         BL::RegionView3D b_rv3d, int width, int height)
499 {
500         bool is_camera_view;
501
502         /* camera view? */
503         is_camera_view = b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_CAMERA;
504
505         if(!is_camera_view) {
506                 /* for non-camera view check whether render border is enabled for viewport
507                  * and if so use border from 3d viewport
508                  * assume viewport has got correctly clamped border already
509                  */
510                 if(b_v3d.use_render_border()) {
511                         bcam->border.left = b_v3d.render_border_min_x();
512                         bcam->border.right = b_v3d.render_border_max_x();
513                         bcam->border.bottom = b_v3d.render_border_min_y();
514                         bcam->border.top = b_v3d.render_border_max_y();
515
516                         return;
517                 }
518         }
519         else if(!b_render.use_border())
520                 return;
521
522         BL::Object b_ob = (b_v3d.lock_camera_and_layers())? b_scene.camera(): b_v3d.camera();
523
524         if(!b_ob)
525                 return;
526
527         bcam->border.left = b_render.border_min_x();
528         bcam->border.right = b_render.border_max_x();
529         bcam->border.bottom = b_render.border_min_y();
530         bcam->border.top = b_render.border_max_y();
531
532         /* determine camera viewport subset */
533         BoundBox2D view_box, cam_box;
534
535         blender_camera_view_subset(b_render, b_scene, b_ob, b_v3d, b_rv3d, width, height,
536                 &view_box, &cam_box);
537
538         /* determine viewport subset matching camera border */
539         cam_box = cam_box.make_relative_to(view_box);
540         bcam->border = cam_box.subset(bcam->border).clamp();
541 }
542
543 void BlenderSync::sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height)
544 {
545         BlenderCamera bcam;
546         blender_camera_init(&bcam, b_scene.render(), b_scene);
547         blender_camera_from_view(&bcam, b_scene, b_v3d, b_rv3d, width, height);
548         blender_camera_border(&bcam, b_scene.render(), b_scene, b_v3d, b_rv3d, width, height);
549
550         blender_camera_sync(scene->camera, &bcam, width, height);
551 }
552
553 BufferParams BlenderSync::get_buffer_params(BL::RenderSettings b_render, BL::Scene b_scene, BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, Camera *cam, int width, int height)
554 {
555         BufferParams params;
556         bool use_border = false;
557
558         params.full_width = width;
559         params.full_height = height;
560
561         if(b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA)
562                 use_border = b_v3d.use_render_border();
563         else
564                 use_border = b_render.use_border();
565
566         if(use_border) {
567                 /* border render */
568                 params.full_x = cam->border.left*width;
569                 params.full_y = cam->border.bottom*height;
570                 params.width = (int)(cam->border.right*width) - params.full_x;
571                 params.height = (int)(cam->border.top*height) - params.full_y;
572
573                 /* survive in case border goes out of view or becomes too small */
574                 params.width = max(params.width, 1);
575                 params.height = max(params.height, 1);
576         }
577         else {
578                 params.width = width;
579                 params.height = height;
580         }
581
582         return params;
583 }
584
585 CCL_NAMESPACE_END
586