Cleanup: rename ED_view3d_smooth_view_finish (to force_finish)
[blender.git] / source / blender / editors / space_view3d / view3d_edit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
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  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_view3d/view3d_edit.c
28  *  \ingroup spview3d
29  */
30
31 #include <string.h>
32 #include <stdio.h>
33 #include <math.h>
34 #include <float.h>
35
36 #include "DNA_armature_types.h"
37 #include "DNA_curve_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_kdopbvh.h"
45 #include "BLI_math.h"
46 #include "BLI_utildefines.h"
47
48 #include "BKE_armature.h"
49 #include "BKE_camera.h"
50 #include "BKE_context.h"
51 #include "BKE_font.h"
52 #include "BKE_library.h"
53 #include "BKE_object.h"
54 #include "BKE_paint.h"
55 #include "BKE_report.h"
56 #include "BKE_scene.h"
57 #include "BKE_screen.h"
58 #include "BKE_action.h"
59 #include "BKE_depsgraph.h" /* for ED_view3d_camera_lock_sync */
60
61
62 #include "BIF_gl.h"
63 #include "BIF_glutil.h"
64
65 #include "WM_api.h"
66 #include "WM_types.h"
67
68 #include "RNA_access.h"
69 #include "RNA_define.h"
70
71 #include "ED_armature.h"
72 #include "ED_particle.h"
73 #include "ED_keyframing.h"
74 #include "ED_screen.h"
75 #include "ED_transform.h"
76 #include "ED_mesh.h"
77 #include "ED_view3d.h"
78
79 #include "UI_resources.h"
80
81 #include "PIL_time.h" /* smoothview */
82
83 #include "view3d_intern.h"  /* own include */
84
85 bool ED_view3d_offset_lock_check(const  View3D *v3d, const  RegionView3D *rv3d)
86 {
87         return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre);
88 }
89
90 static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op)
91 {
92         View3D *v3d = CTX_wm_view3d(C);
93         RegionView3D *rv3d = CTX_wm_region_view3d(C);
94         if (ED_view3d_offset_lock_check(v3d, rv3d)) {
95                 BKE_report(op->reports, RPT_WARNING, "View offset is locked");
96                 return true;
97         }
98         else {
99                 return false;
100         }
101 }
102
103 /* ********************** view3d_edit: view manipulations ********************* */
104
105 /**
106  * \return true when the view-port is locked to its camera.
107  */
108 bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
109 {
110         return ((v3d->camera) &&
111                 (v3d->camera->id.lib == NULL) &&
112                 (v3d->flag2 & V3D_LOCK_CAMERA) &&
113                 (rv3d->persp == RV3D_CAMOB));
114 }
115
116 /**
117  * Apply the camera object transformation to the view-port.
118  * (needed so we can use regular view-port manipulation operators, that sync back to the camera).
119  */
120 void ED_view3d_camera_lock_init_ex(View3D *v3d, RegionView3D *rv3d, const bool calc_dist)
121 {
122         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
123                 if (calc_dist) {
124                         /* using a fallback dist is OK here since ED_view3d_from_object() compensates for it */
125                         rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
126                 }
127                 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
128         }
129 }
130
131 void ED_view3d_camera_lock_init(View3D *v3d, RegionView3D *rv3d)
132 {
133         ED_view3d_camera_lock_init_ex(v3d, rv3d, true);
134 }
135
136 /**
137  * Apply the view-port transformation back to the camera object.
138  *
139  * \return true if the camera is moved.
140  */
141 bool ED_view3d_camera_lock_sync(View3D *v3d, RegionView3D *rv3d)
142 {
143         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
144                 ObjectTfmProtectedChannels obtfm;
145                 Object *root_parent;
146
147                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
148                         Object *ob_update;
149                         float tmat[4][4];
150                         float imat[4][4];
151                         float view_mat[4][4];
152                         float diff_mat[4][4];
153                         float parent_mat[4][4];
154
155                         while (root_parent->parent) {
156                                 root_parent = root_parent->parent;
157                         }
158
159                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
160
161                         normalize_m4_m4(tmat, v3d->camera->obmat);
162
163                         invert_m4_m4(imat, tmat);
164                         mul_m4_m4m4(diff_mat, view_mat, imat);
165
166                         mul_m4_m4m4(parent_mat, diff_mat, root_parent->obmat);
167
168                         BKE_object_tfm_protected_backup(root_parent, &obtfm);
169                         BKE_object_apply_mat4(root_parent, parent_mat, true, false);
170                         BKE_object_tfm_protected_restore(root_parent, &obtfm, root_parent->protectflag);
171
172                         ob_update = v3d->camera;
173                         while (ob_update) {
174                                 DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
175                                 WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, ob_update);
176                                 ob_update = ob_update->parent;
177                         }
178                 }
179                 else {
180                         /* always maintain the same scale */
181                         const short protect_scale_all = (OB_LOCK_SCALEX | OB_LOCK_SCALEY | OB_LOCK_SCALEZ);
182                         BKE_object_tfm_protected_backup(v3d->camera, &obtfm);
183                         ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
184                         BKE_object_tfm_protected_restore(v3d->camera, &obtfm, v3d->camera->protectflag | protect_scale_all);
185
186                         DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
187                         WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, v3d->camera);
188                 }
189
190                 return true;
191         }
192         else {
193                 return false;
194         }
195 }
196
197 bool ED_view3d_camera_autokey(
198         Scene *scene, ID *id_key,
199         struct bContext *C, const bool do_rotate, const bool do_translate)
200 {
201         if (autokeyframe_cfra_can_key(scene, id_key)) {
202                 const float cfra = (float)CFRA;
203                 ListBase dsources = {NULL, NULL};
204
205                 /* add data-source override for the camera object */
206                 ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL);
207
208                 /* insert keyframes
209                  * 1) on the first frame
210                  * 2) on each subsequent frame
211                  *    TODO: need to check in future that frame changed before doing this
212                  */
213                 if (do_rotate) {
214                         struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_ROTATION_ID);
215                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
216                 }
217                 if (do_translate) {
218                         struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
219                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
220                 }
221
222                 /* free temp data */
223                 BLI_freelistN(&dsources);
224
225                 return true;
226         }
227         else {
228                 return false;
229         }
230 }
231
232 /**
233  * Call after modifying a locked view.
234  *
235  * \note Not every view edit currently auto-keys (numpad for eg),
236  * this is complicated because of smoothview.
237  */
238 bool ED_view3d_camera_lock_autokey(
239         View3D *v3d, RegionView3D *rv3d,
240         struct bContext *C, const bool do_rotate, const bool do_translate)
241 {
242         /* similar to ED_view3d_cameracontrol_update */
243         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
244                 Scene *scene = CTX_data_scene(C);
245                 ID *id_key;
246                 Object *root_parent;
247                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
248                         while (root_parent->parent) {
249                                 root_parent = root_parent->parent;
250                         }
251                         id_key = &root_parent->id;
252                 }
253                 else {
254                         id_key = &v3d->camera->id;
255                 }
256
257                 return ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate);
258         }
259         else {
260                 return false;
261         }
262 }
263
264 /**
265  * For viewport operators that exit camera persp.
266  *
267  * \note This differs from simply setting ``rv3d->persp = persp`` because it
268  * sets the ``ofs`` and ``dist`` values of the viewport so it matches the camera,
269  * otherwise switching out of camera view may jump to a different part of the scene.
270  */
271 static void view3d_persp_switch_from_camera(View3D *v3d, RegionView3D *rv3d, const char persp)
272 {
273         BLI_assert(rv3d->persp == RV3D_CAMOB);
274         BLI_assert(persp != RV3D_CAMOB);
275
276         if (v3d->camera) {
277                 rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
278                 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
279         }
280
281         if (!ED_view3d_camera_lock_check(v3d, rv3d)) {
282                 rv3d->persp = persp;
283         }
284 }
285
286 /* ********************* box view support ***************** */
287
288 static void view3d_boxview_clip(ScrArea *sa)
289 {
290         ARegion *ar;
291         BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb");
292         float clip[6][4];
293         float x1 = 0.0f, y1 = 0.0f, z1 = 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f};
294         int val;
295
296         /* create bounding box */
297         for (ar = sa->regionbase.first; ar; ar = ar->next) {
298                 if (ar->regiontype == RGN_TYPE_WINDOW) {
299                         RegionView3D *rv3d = ar->regiondata;
300
301                         if (rv3d->viewlock & RV3D_BOXCLIP) {
302                                 if (ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
303                                         if (ar->winx > ar->winy) x1 = rv3d->dist;
304                                         else x1 = ar->winx * rv3d->dist / ar->winy;
305
306                                         if (ar->winx > ar->winy) y1 = ar->winy * rv3d->dist / ar->winx;
307                                         else y1 = rv3d->dist;
308                                         copy_v2_v2(ofs, rv3d->ofs);
309                                 }
310                                 else if (ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
311                                         ofs[2] = rv3d->ofs[2];
312
313                                         if (ar->winx > ar->winy) z1 = ar->winy * rv3d->dist / ar->winx;
314                                         else z1 = rv3d->dist;
315                                 }
316                         }
317                 }
318         }
319
320         for (val = 0; val < 8; val++) {
321                 if (ELEM(val, 0, 3, 4, 7))
322                         bb->vec[val][0] = -x1 - ofs[0];
323                 else
324                         bb->vec[val][0] =  x1 - ofs[0];
325
326                 if (ELEM(val, 0, 1, 4, 5))
327                         bb->vec[val][1] = -y1 - ofs[1];
328                 else
329                         bb->vec[val][1] =  y1 - ofs[1];
330
331                 if (val > 3)
332                         bb->vec[val][2] = -z1 - ofs[2];
333                 else
334                         bb->vec[val][2] =  z1 - ofs[2];
335         }
336
337         /* normals for plane equations */
338         normal_tri_v3(clip[0], bb->vec[0], bb->vec[1], bb->vec[4]);
339         normal_tri_v3(clip[1], bb->vec[1], bb->vec[2], bb->vec[5]);
340         normal_tri_v3(clip[2], bb->vec[2], bb->vec[3], bb->vec[6]);
341         normal_tri_v3(clip[3], bb->vec[3], bb->vec[0], bb->vec[7]);
342         normal_tri_v3(clip[4], bb->vec[4], bb->vec[5], bb->vec[6]);
343         normal_tri_v3(clip[5], bb->vec[0], bb->vec[2], bb->vec[1]);
344
345         /* then plane equations */
346         for (val = 0; val < 6; val++) {
347                 clip[val][3] = -dot_v3v3(clip[val], bb->vec[val % 5]);
348         }
349
350         /* create bounding box */
351         for (ar = sa->regionbase.first; ar; ar = ar->next) {
352                 if (ar->regiontype == RGN_TYPE_WINDOW) {
353                         RegionView3D *rv3d = ar->regiondata;
354
355                         if (rv3d->viewlock & RV3D_BOXCLIP) {
356                                 rv3d->rflag |= RV3D_CLIPPING;
357                                 memcpy(rv3d->clip, clip, sizeof(clip));
358                                 if (rv3d->clipbb) MEM_freeN(rv3d->clipbb);
359                                 rv3d->clipbb = MEM_dupallocN(bb);
360                         }
361                 }
362         }
363         MEM_freeN(bb);
364 }
365
366 /**
367  * Find which axis values are shared between both views and copy to \a rv3d_dst
368  * taking axis flipping into account.
369  */
370 static void view3d_boxview_sync_axis(RegionView3D *rv3d_dst, RegionView3D *rv3d_src)
371 {
372         /* absolute axis values above this are considered to be set (will be ~1.0f) */
373         const float axis_eps = 0.5f;
374         float viewinv[4];
375
376         /* use the view rotation to identify which axis to sync on */
377         float view_axis_all[4][3] = {
378             {1.0f, 0.0f, 0.0f},
379             {0.0f, 1.0f, 0.0f},
380             {1.0f, 0.0f, 0.0f},
381             {0.0f, 1.0f, 0.0f}};
382
383         float *view_src_x = &view_axis_all[0][0];
384         float *view_src_y = &view_axis_all[1][0];
385
386         float *view_dst_x = &view_axis_all[2][0];
387         float *view_dst_y = &view_axis_all[3][0];
388         int i;
389
390
391         /* we could use rv3d->viewinv, but better not depend on view matrix being updated */
392         if (UNLIKELY(ED_view3d_quat_from_axis_view(rv3d_src->view, viewinv) == false)) {
393                 return;
394         }
395         invert_qt_normalized(viewinv);
396         mul_qt_v3(viewinv, view_src_x);
397         mul_qt_v3(viewinv, view_src_y);
398
399         if (UNLIKELY(ED_view3d_quat_from_axis_view(rv3d_dst->view, viewinv) == false)) {
400                 return;
401         }
402         invert_qt_normalized(viewinv);
403         mul_qt_v3(viewinv, view_dst_x);
404         mul_qt_v3(viewinv, view_dst_y);
405
406         /* check source and dest have a matching axis */
407         for (i = 0; i < 3; i++) {
408                 if (((fabsf(view_src_x[i]) > axis_eps) || (fabsf(view_src_y[i]) > axis_eps)) &&
409                     ((fabsf(view_dst_x[i]) > axis_eps) || (fabsf(view_dst_y[i]) > axis_eps)))
410                 {
411                         rv3d_dst->ofs[i] = rv3d_src->ofs[i];
412                 }
413         }
414 }
415
416 /* sync center/zoom view of region to others, for view transforms */
417 static void view3d_boxview_sync(ScrArea *sa, ARegion *ar)
418 {
419         ARegion *artest;
420         RegionView3D *rv3d = ar->regiondata;
421         short clip = 0;
422
423         for (artest = sa->regionbase.first; artest; artest = artest->next) {
424                 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
425                         RegionView3D *rv3dtest = artest->regiondata;
426
427                         if (rv3dtest->viewlock & RV3D_LOCKED) {
428                                 rv3dtest->dist = rv3d->dist;
429                                 view3d_boxview_sync_axis(rv3dtest, rv3d);
430                                 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
431
432                                 ED_region_tag_redraw(artest);
433                         }
434                 }
435         }
436
437         if (clip) {
438                 view3d_boxview_clip(sa);
439         }
440 }
441
442 /* for home, center etc */
443 void view3d_boxview_copy(ScrArea *sa, ARegion *ar)
444 {
445         ARegion *artest;
446         RegionView3D *rv3d = ar->regiondata;
447         bool clip = false;
448
449         for (artest = sa->regionbase.first; artest; artest = artest->next) {
450                 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
451                         RegionView3D *rv3dtest = artest->regiondata;
452
453                         if (rv3dtest->viewlock) {
454                                 rv3dtest->dist = rv3d->dist;
455                                 copy_v3_v3(rv3dtest->ofs, rv3d->ofs);
456                                 ED_region_tag_redraw(artest);
457
458                                 clip |= ((rv3dtest->viewlock & RV3D_BOXCLIP) != 0);
459                         }
460                 }
461         }
462
463         if (clip) {
464                 view3d_boxview_clip(sa);
465         }
466 }
467
468 /* 'clip' is used to know if our clip setting has changed */
469 void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, bool do_clip)
470 {
471         ARegion *ar_sync = NULL;
472         RegionView3D *rv3d = ar->regiondata;
473         short viewlock;
474         /* this function copies flags from the first of the 3 other quadview
475          * regions to the 2 other, so it assumes this is the region whose
476          * properties are always being edited, weak */
477         viewlock = rv3d->viewlock;
478
479         if ((viewlock & RV3D_LOCKED) == 0) {
480                 do_clip = (viewlock & RV3D_BOXCLIP) != 0;
481                 viewlock = 0;
482         }
483         else if ((viewlock & RV3D_BOXVIEW) == 0 && (viewlock & RV3D_BOXCLIP) != 0) {
484                 do_clip = true;
485                 viewlock &= ~RV3D_BOXCLIP;
486         }
487
488         for (; ar; ar = ar->prev) {
489                 if (ar->alignment == RGN_ALIGN_QSPLIT) {
490                         rv3d = ar->regiondata;
491                         rv3d->viewlock = viewlock;
492
493                         if (do_clip && (viewlock & RV3D_BOXCLIP) == 0) {
494                                 rv3d->rflag &= ~RV3D_BOXCLIP;
495                         }
496
497                         /* use ar_sync so we sync with one of the aligned views below
498                          * else the view jumps on changing view settings like 'clip'
499                          * since it copies from the perspective view */
500                         ar_sync = ar;
501                 }
502         }
503
504         if (rv3d->viewlock & RV3D_BOXVIEW) {
505                 view3d_boxview_sync(sa, ar_sync ? ar_sync : sa->regionbase.last);
506         }
507
508         /* ensure locked regions have an axis, locked user views don't make much sense */
509         if (viewlock & RV3D_LOCKED) {
510                 int index_qsplit = 0;
511                 for (ar = sa->regionbase.first; ar; ar = ar->next) {
512                         if (ar->alignment == RGN_ALIGN_QSPLIT) {
513                                 rv3d = ar->regiondata;
514                                 if (rv3d->viewlock) {
515                                         if (!RV3D_VIEW_IS_AXIS(rv3d->view)) {
516                                                 rv3d->view = ED_view3d_lock_view_from_index(index_qsplit);
517                                                 rv3d->persp = RV3D_ORTHO;
518                                                 ED_view3d_lock(rv3d);
519                                         }
520                                 }
521                                 index_qsplit++;
522                         }
523                 }
524         }
525
526         ED_area_tag_redraw(sa);
527 }
528
529 /* ************************** init for view ops **********************************/
530
531 typedef struct ViewOpsData {
532         /* context pointers (assigned by viewops_data_alloc) */
533         Scene *scene;
534         ScrArea *sa;
535         ARegion *ar;
536         View3D *v3d;
537         RegionView3D *rv3d;
538
539         /* needed for continuous zoom */
540         wmTimer *timer;
541         double timer_lastdraw;
542
543         float oldquat[4];
544         float viewquat[4]; /* working copy of rv3d->viewquat */
545         float trackvec[3];
546         float mousevec[3]; /* dolly only */
547         float reverse;
548         float dist_prev, camzoom_prev;
549         float grid, far;
550         bool axis_snap;  /* view rotate only */
551         float zfac;
552
553         /* use for orbit selection and auto-dist */
554         float ofs[3], dyn_ofs[3];
555         bool use_dyn_ofs;
556
557         int origx, origy, oldx, oldy;
558         int origkey; /* the key that triggered the operator */
559
560 } ViewOpsData;
561
562 #define TRACKBALLSIZE  (1.1f)
563
564 static void calctrackballvec(const rcti *rect, int mx, int my, float vec[3])
565 {
566         float x, y, radius, d, z, t;
567
568         radius = TRACKBALLSIZE;
569
570         /* normalize x and y */
571         x = BLI_rcti_cent_x(rect) - mx;
572         x /= (float)(BLI_rcti_size_x(rect) / 4);
573         y = BLI_rcti_cent_y(rect) - my;
574         y /= (float)(BLI_rcti_size_y(rect) / 2);
575
576         d = sqrtf(x * x + y * y);
577         if (d < radius * (float)M_SQRT1_2) { /* Inside sphere */
578                 z = sqrtf(radius * radius - d * d);
579         }
580         else { /* On hyperbola */
581                 t = radius / (float)M_SQRT2;
582                 z = t * t / d;
583         }
584
585         vec[0] = x;
586         vec[1] = y;
587         vec[2] = -z;     /* yah yah! */
588 }
589
590
591 /* -------------------------------------------------------------------- */
592 /* ViewOpsData */
593
594 /** \name Generic View Operator Custom-Data.
595  * \{ */
596
597 /**
598  * Allocate and fill in context pointers for #ViewOpsData
599  */
600 static void viewops_data_alloc(bContext *C, wmOperator *op)
601 {
602         ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), "viewops data");
603
604         /* store data */
605         op->customdata = vod;
606         vod->scene = CTX_data_scene(C);
607         vod->sa = CTX_wm_area(C);
608         vod->ar = CTX_wm_region(C);
609         vod->v3d = vod->sa->spacedata.first;
610         vod->rv3d = vod->ar->regiondata;
611 }
612
613 void view3d_orbit_apply_dyn_ofs(
614         float r_ofs[3], const float ofs_old[3], const float viewquat_old[4],
615         const float viewquat_new[4], const float dyn_ofs[3])
616 {
617         float q[4];
618         invert_qt_qt_normalized(q, viewquat_old);
619         mul_qt_qtqt(q, q, viewquat_new);
620
621         invert_qt_normalized(q);
622
623         sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
624         mul_qt_v3(q, r_ofs);
625         add_v3_v3(r_ofs, dyn_ofs);
626 }
627
628 static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
629 {
630         static float lastofs[3] = {0, 0, 0};
631         bool is_set = false;
632
633         Scene *scene = CTX_data_scene(C);
634         Object *ob_act = OBACT;
635
636         if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
637             /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
638             ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
639         {
640                 /* in case of sculpting use last average stroke position as a rotation
641                  * center, in other cases it's not clear what rotation center shall be
642                  * so just rotate around object origin
643                  */
644                 if (ob_act->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
645                         float stroke[3];
646                         BKE_paint_stroke_get_average(scene, ob_act, stroke);
647                         copy_v3_v3(lastofs, stroke);
648                 }
649                 else {
650                         copy_v3_v3(lastofs, ob_act->obmat[3]);
651                 }
652                 is_set = true;
653         }
654         else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
655                 Curve *cu = ob_act->data;
656                 EditFont *ef = cu->editfont;
657                 int i;
658
659                 zero_v3(lastofs);
660                 for (i = 0; i < 4; i++) {
661                         add_v2_v2(lastofs, ef->textcurs[i]);
662                 }
663                 mul_v2_fl(lastofs, 1.0f / 4.0f);
664
665                 mul_m4_v3(ob_act->obmat, lastofs);
666
667                 is_set = true;
668         }
669         else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
670                 /* object mode use boundbox centers */
671                 View3D *v3d = CTX_wm_view3d(C);
672                 Base *base;
673                 unsigned int tot = 0;
674                 float select_center[3];
675
676                 zero_v3(select_center);
677                 for (base = FIRSTBASE; base; base = base->next) {
678                         if (TESTBASE(v3d, base)) {
679                                 /* use the boundbox if we can */
680                                 Object *ob = base->object;
681
682                                 if (ob->bb && !(ob->bb->flag & BOUNDBOX_DIRTY)) {
683                                         float cent[3];
684
685                                         BKE_boundbox_calc_center_aabb(ob->bb, cent);
686
687                                         mul_m4_v3(ob->obmat, cent);
688                                         add_v3_v3(select_center, cent);
689                                 }
690                                 else {
691                                         add_v3_v3(select_center, ob->obmat[3]);
692                                 }
693                                 tot++;
694                         }
695                 }
696                 if (tot) {
697                         mul_v3_fl(select_center, 1.0f / (float)tot);
698                         copy_v3_v3(lastofs, select_center);
699                         is_set = true;
700                 }
701         }
702         else {
703                 /* If there's no selection, lastofs is unmodified and last value since static */
704                 is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEAN, lastofs, NULL);
705         }
706
707         copy_v3_v3(r_dyn_ofs, lastofs);
708
709         return is_set;
710 }
711
712 /**
713  * Calculate the values for #ViewOpsData
714  */
715 static void viewops_data_create_ex(bContext *C, wmOperator *op, const wmEvent *event,
716                                    const bool use_orbit_select,
717                                    const bool use_orbit_zbuf)
718 {
719         ViewOpsData *vod = op->customdata;
720         RegionView3D *rv3d = vod->rv3d;
721
722         /* set the view from the camera, if view locking is enabled.
723          * we may want to make this optional but for now its needed always */
724         ED_view3d_camera_lock_init(vod->v3d, vod->rv3d);
725
726         vod->dist_prev = rv3d->dist;
727         vod->camzoom_prev = rv3d->camzoom;
728         copy_qt_qt(vod->viewquat, rv3d->viewquat);
729         copy_qt_qt(vod->oldquat, rv3d->viewquat);
730         vod->origx = vod->oldx = event->x;
731         vod->origy = vod->oldy = event->y;
732         vod->origkey = event->type; /* the key that triggered the operator.  */
733         vod->use_dyn_ofs = false;
734         copy_v3_v3(vod->ofs, rv3d->ofs);
735
736         if (use_orbit_select) {
737
738                 vod->use_dyn_ofs = true;
739
740                 view3d_orbit_calc_center(C, vod->dyn_ofs);
741
742                 negate_v3(vod->dyn_ofs);
743         }
744         else if (use_orbit_zbuf) {
745                 Scene *scene = CTX_data_scene(C);
746                 float fallback_depth_pt[3];
747
748                 view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
749
750                 negate_v3_v3(fallback_depth_pt, rv3d->ofs);
751
752                 if ((vod->use_dyn_ofs = ED_view3d_autodist(scene, vod->ar, vod->v3d,
753                                                            event->mval, vod->dyn_ofs, true, fallback_depth_pt)))
754                 {
755                         if (rv3d->is_persp) {
756                                 float my_origin[3]; /* original G.vd->ofs */
757                                 float my_pivot[3]; /* view */
758                                 float dvec[3];
759
760                                 /* locals for dist correction */
761                                 float mat[3][3];
762                                 float upvec[3];
763
764                                 negate_v3_v3(my_origin, rv3d->ofs);             /* ofs is flipped */
765
766                                 /* Set the dist value to be the distance from this 3d point
767                                  * this means youll always be able to zoom into it and panning wont go bad when dist was zero */
768
769                                 /* remove dist value */
770                                 upvec[0] = upvec[1] = 0;
771                                 upvec[2] = rv3d->dist;
772                                 copy_m3_m4(mat, rv3d->viewinv);
773
774                                 mul_m3_v3(mat, upvec);
775                                 sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
776                                 negate_v3(my_pivot);                /* ofs is flipped */
777
778                                 /* find a new ofs value that is along the view axis (rather than the mouse location) */
779                                 closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
780                                 vod->dist_prev = rv3d->dist = len_v3v3(my_pivot, dvec);
781
782                                 negate_v3_v3(rv3d->ofs, dvec);
783                         }
784                         else {
785                                 const float mval_ar_mid[2] = {
786                                     (float)vod->ar->winx / 2.0f,
787                                     (float)vod->ar->winy / 2.0f};
788
789                                 ED_view3d_win_to_3d(vod->ar, vod->dyn_ofs, mval_ar_mid, rv3d->ofs);
790                                 negate_v3(rv3d->ofs);
791                         }
792                         negate_v3(vod->dyn_ofs);
793                         copy_v3_v3(vod->ofs, rv3d->ofs);
794                 }
795         }
796
797         {
798                 /* for dolly */
799                 const float mval_f[2] = {(float)event->mval[0],
800                                          (float)event->mval[1]};
801                 ED_view3d_win_to_vector(vod->ar, mval_f, vod->mousevec);
802         }
803
804         /* lookup, we don't pass on v3d to prevent confusement */
805         vod->grid = vod->v3d->grid;
806         vod->far = vod->v3d->far;
807
808         calctrackballvec(&vod->ar->winrct, event->x, event->y, vod->trackvec);
809
810         {
811                 float tvec[3];
812                 negate_v3_v3(tvec, rv3d->ofs);
813                 vod->zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL);
814         }
815
816         vod->reverse = 1.0f;
817         if (rv3d->persmat[2][1] < 0.0f)
818                 vod->reverse = -1.0f;
819
820         rv3d->rflag |= RV3D_NAVIGATING;
821 }
822
823 static void viewops_data_create(bContext *C, wmOperator *op, const wmEvent *event)
824 {
825         viewops_data_create_ex(
826                 C, op, event,
827                 (U.uiflag & USER_ORBIT_SELECTION) != 0,
828                 (U.uiflag & USER_ZBUF_ORBIT) != 0);
829 }
830
831 static void viewops_data_free(bContext *C, wmOperator *op)
832 {
833         ARegion *ar;
834         Paint *p = BKE_paint_get_active_from_context(C);
835
836         if (op->customdata) {
837                 ViewOpsData *vod = op->customdata;
838                 ar = vod->ar;
839                 vod->rv3d->rflag &= ~RV3D_NAVIGATING;
840
841                 if (vod->timer)
842                         WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
843
844                 MEM_freeN(vod);
845                 op->customdata = NULL;
846         }
847         else {
848                 ar = CTX_wm_region(C);
849         }
850
851         if (p && (p->flags & PAINT_FAST_NAVIGATE))
852                 ED_region_tag_redraw(ar);
853 }
854 /** \} */
855
856
857 /* ************************** viewrotate **********************************/
858
859 enum {
860         VIEW_PASS = 0,
861         VIEW_APPLY,
862         VIEW_CONFIRM
863 };
864
865 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
866 #define VIEW_MODAL_CONFIRM              1 /* used for all view operations */
867 #define VIEWROT_MODAL_AXIS_SNAP_ENABLE  2
868 #define VIEWROT_MODAL_AXIS_SNAP_DISABLE 3
869 #define VIEWROT_MODAL_SWITCH_ZOOM       4
870 #define VIEWROT_MODAL_SWITCH_MOVE       5
871 #define VIEWROT_MODAL_SWITCH_ROTATE     6
872
873 /* called in transform_ops.c, on each regeneration of keymaps  */
874 void viewrotate_modal_keymap(wmKeyConfig *keyconf)
875 {
876         static EnumPropertyItem modal_items[] = {
877                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
878
879                 {VIEWROT_MODAL_AXIS_SNAP_ENABLE,    "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""},
880                 {VIEWROT_MODAL_AXIS_SNAP_DISABLE,   "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""},
881                 
882                 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
883                 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
884
885                 {0, NULL, 0, NULL, NULL}
886         };
887
888         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Rotate Modal");
889
890         /* this function is called for each spacetype, only needs to add map once */
891         if (keymap && keymap->modal_items) return;
892
893         keymap = WM_modalkeymap_add(keyconf, "View3D Rotate Modal", modal_items);
894
895         /* items for modal map */
896         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
897         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
898
899         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_ENABLE);
900         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_DISABLE);
901
902         /* disabled mode switching for now, can re-implement better, later on */
903 #if 0
904         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
905         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
906         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
907 #endif
908         
909         /* assign map to operators */
910         WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
911
912 }
913
914 static void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
915 {
916         if (vod->use_dyn_ofs) {
917                 RegionView3D *rv3d = vod->rv3d;
918                 view3d_orbit_apply_dyn_ofs(rv3d->ofs, vod->ofs, vod->oldquat, viewquat_new, vod->dyn_ofs);
919         }
920 }
921
922 static void viewrotate_apply_snap(ViewOpsData *vod)
923 {
924         const float axis_limit = DEG2RADF(45 / 3);
925
926         RegionView3D *rv3d = vod->rv3d;
927
928         float viewquat_inv[4];
929         float zaxis[3] = {0, 0, 1};
930         float zaxis_best[3];
931         int x, y, z;
932         bool found = false;
933
934         invert_qt_qt_normalized(viewquat_inv, vod->viewquat);
935
936         mul_qt_v3(viewquat_inv, zaxis);
937         normalize_v3(zaxis);
938
939
940         for (x = -1; x < 2; x++) {
941                 for (y = -1; y < 2; y++) {
942                         for (z = -1; z < 2; z++) {
943                                 if (x || y || z) {
944                                         float zaxis_test[3] = {x, y, z};
945
946                                         normalize_v3(zaxis_test);
947
948                                         if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) {
949                                                 copy_v3_v3(zaxis_best, zaxis_test);
950                                                 found = true;
951                                         }
952                                 }
953                         }
954                 }
955         }
956
957         if (found) {
958
959                 /* find the best roll */
960                 float quat_roll[4], quat_final[4], quat_best[4], quat_snap[4];
961                 float viewquat_align[4]; /* viewquat aligned to zaxis_best */
962                 float viewquat_align_inv[4]; /* viewquat aligned to zaxis_best */
963                 float best_angle = axis_limit;
964                 int j;
965
966                 /* viewquat_align is the original viewquat aligned to the snapped axis
967                  * for testing roll */
968                 rotation_between_vecs_to_quat(viewquat_align, zaxis_best, zaxis);
969                 normalize_qt(viewquat_align);
970                 mul_qt_qtqt(viewquat_align, vod->viewquat, viewquat_align);
971                 normalize_qt(viewquat_align);
972                 invert_qt_qt_normalized(viewquat_align_inv, viewquat_align);
973
974                 vec_to_quat(quat_snap, zaxis_best, OB_NEGZ, OB_POSY);
975                 normalize_qt(quat_snap);
976                 invert_qt_normalized(quat_snap);
977
978                 /* check if we can find the roll */
979                 found = false;
980
981                 /* find best roll */
982                 for (j = 0; j < 8; j++) {
983                         float angle;
984                         float xaxis1[3] = {1, 0, 0};
985                         float xaxis2[3] = {1, 0, 0};
986                         float quat_final_inv[4];
987
988                         axis_angle_to_quat(quat_roll, zaxis_best, (float)j * DEG2RADF(45.0f));
989                         normalize_qt(quat_roll);
990
991                         mul_qt_qtqt(quat_final, quat_snap, quat_roll);
992                         normalize_qt(quat_final);
993
994                         /* compare 2 vector angles to find the least roll */
995                         invert_qt_qt_normalized(quat_final_inv, quat_final);
996                         mul_qt_v3(viewquat_align_inv, xaxis1);
997                         mul_qt_v3(quat_final_inv, xaxis2);
998                         angle = angle_v3v3(xaxis1, xaxis2);
999
1000                         if (angle <= best_angle) {
1001                                 found = true;
1002                                 best_angle = angle;
1003                                 copy_qt_qt(quat_best, quat_final);
1004                         }
1005                 }
1006
1007                 if (found) {
1008                         /* lock 'quat_best' to an axis view if we can */
1009                         rv3d->view = ED_view3d_quat_to_axis_view(quat_best, 0.01f);
1010                         if (rv3d->view != RV3D_VIEW_USER) {
1011                                 ED_view3d_quat_from_axis_view(rv3d->view, quat_best);
1012                         }
1013                 }
1014                 else {
1015                         copy_qt_qt(quat_best, viewquat_align);
1016                 }
1017
1018                 copy_qt_qt(rv3d->viewquat, quat_best);
1019
1020                 viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
1021         }
1022 }
1023
1024 static void viewrotate_apply(ViewOpsData *vod, int x, int y)
1025 {
1026         RegionView3D *rv3d = vod->rv3d;
1027
1028         rv3d->view = RV3D_VIEW_USER; /* need to reset every time because of view snapping */
1029
1030         if (U.flag & USER_TRACKBALL) {
1031                 float axis[3], q1[4], dvec[3], newvec[3];
1032                 float angle;
1033
1034                 calctrackballvec(&vod->ar->winrct, x, y, newvec);
1035
1036                 sub_v3_v3v3(dvec, newvec, vod->trackvec);
1037
1038                 angle = (len_v3(dvec) / (2.0f * TRACKBALLSIZE)) * (float)M_PI;
1039
1040                 /* Allow for rotation beyond the interval [-pi, pi] */
1041                 angle = angle_wrap_rad(angle);
1042
1043                 /* This relation is used instead of the actual angle between vectors
1044                  * so that the angle of rotation is linearly proportional to
1045                  * the distance that the mouse is dragged. */
1046
1047                 cross_v3_v3v3(axis, vod->trackvec, newvec);
1048                 axis_angle_to_quat(q1, axis, angle);
1049
1050                 mul_qt_qtqt(vod->viewquat, q1, vod->oldquat);
1051
1052                 viewrotate_apply_dyn_ofs(vod, vod->viewquat);
1053         }
1054         else {
1055                 /* New turntable view code by John Aughey */
1056                 float quat_local_x[4], quat_global_z[4];
1057                 float m[3][3];
1058                 float m_inv[3][3];
1059                 const float zvec_global[3] = {0.0f, 0.0f, 1.0f};
1060                 float xaxis[3];
1061
1062                 /* Sensitivity will control how fast the viewport rotates.  0.007 was
1063                  * obtained experimentally by looking at viewport rotation sensitivities
1064                  * on other modeling programs. */
1065                 /* Perhaps this should be a configurable user parameter. */
1066                 const float sensitivity = 0.007f;
1067
1068                 /* Get the 3x3 matrix and its inverse from the quaternion */
1069                 quat_to_mat3(m, vod->viewquat);
1070                 invert_m3_m3(m_inv, m);
1071
1072                 /* avoid gimble lock */
1073 #if 1
1074                 if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) {
1075                         float fac;
1076                         cross_v3_v3v3(xaxis, zvec_global, m_inv[2]);
1077                         if (dot_v3v3(xaxis, m_inv[0]) < 0) {
1078                                 negate_v3(xaxis);
1079                         }
1080                         fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI;
1081                         fac = fabsf(fac - 0.5f) * 2;
1082                         fac = fac * fac;
1083                         interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac);
1084                 }
1085                 else {
1086                         copy_v3_v3(xaxis, m_inv[0]);
1087                 }
1088 #else
1089                 copy_v3_v3(xaxis, m_inv[0]);
1090 #endif
1091
1092                 /* Determine the direction of the x vector (for rotating up and down) */
1093                 /* This can likely be computed directly from the quaternion. */
1094
1095                 /* Perform the up/down rotation */
1096                 axis_angle_to_quat(quat_local_x, xaxis, sensitivity * -(y - vod->oldy));
1097                 mul_qt_qtqt(quat_local_x, vod->viewquat, quat_local_x);
1098
1099                 /* Perform the orbital rotation */
1100                 axis_angle_to_quat_single(quat_global_z, 'Z', sensitivity * vod->reverse * (x - vod->oldx));
1101                 mul_qt_qtqt(vod->viewquat, quat_local_x, quat_global_z);
1102
1103                 viewrotate_apply_dyn_ofs(vod, vod->viewquat);
1104         }
1105
1106         /* avoid precision loss over time */
1107         normalize_qt(vod->viewquat);
1108
1109         /* use a working copy so view rotation locking doesnt overwrite the locked
1110          * rotation back into the view we calculate with */
1111         copy_qt_qt(rv3d->viewquat, vod->viewquat);
1112
1113         /* check for view snap,
1114          * note: don't apply snap to vod->viewquat so the view wont jam up */
1115         if (vod->axis_snap) {
1116                 viewrotate_apply_snap(vod);
1117         }
1118         vod->oldx = x;
1119         vod->oldy = y;
1120
1121         ED_view3d_camera_lock_sync(vod->v3d, rv3d);
1122
1123         ED_region_tag_redraw(vod->ar);
1124 }
1125
1126 static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
1127 {
1128         ViewOpsData *vod = op->customdata;
1129         short event_code = VIEW_PASS;
1130         bool use_autokey = false;
1131         int ret = OPERATOR_RUNNING_MODAL;
1132
1133         /* execute the events */
1134         if (event->type == MOUSEMOVE) {
1135                 event_code = VIEW_APPLY;
1136         }
1137         else if (event->type == EVT_MODAL_MAP) {
1138                 switch (event->val) {
1139                         case VIEW_MODAL_CONFIRM:
1140                                 event_code = VIEW_CONFIRM;
1141                                 break;
1142                         case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
1143                                 vod->axis_snap = true;
1144                                 event_code = VIEW_APPLY;
1145                                 break;
1146                         case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
1147                                 vod->axis_snap = false;
1148                                 event_code = VIEW_APPLY;
1149                                 break;
1150                         case VIEWROT_MODAL_SWITCH_ZOOM:
1151                                 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
1152                                 event_code = VIEW_CONFIRM;
1153                                 break;
1154                         case VIEWROT_MODAL_SWITCH_MOVE:
1155                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
1156                                 event_code = VIEW_CONFIRM;
1157                                 break;
1158                 }
1159         }
1160         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
1161                 event_code = VIEW_CONFIRM;
1162         }
1163
1164         if (event_code == VIEW_APPLY) {
1165                 viewrotate_apply(vod, event->x, event->y);
1166                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
1167                         use_autokey = true;
1168                 }
1169         }
1170         else if (event_code == VIEW_CONFIRM) {
1171                 ED_view3d_depth_tag_update(vod->rv3d);
1172                 use_autokey = true;
1173                 ret = OPERATOR_FINISHED;
1174         }
1175
1176         if (use_autokey) {
1177                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true);
1178         }
1179
1180         if (ret & OPERATOR_FINISHED) {
1181                 viewops_data_free(C, op);
1182         }
1183
1184         return ret;
1185 }
1186
1187 /**
1188  * Action to take when rotating the view,
1189  * handle auto-persp and logic for switching out of views.
1190  *
1191  * shared with NDOF.
1192  */
1193 static bool view3d_ensure_persp(struct View3D *v3d, ARegion *ar)
1194 {
1195         RegionView3D *rv3d = ar->regiondata;
1196         const bool autopersp = (U.uiflag & USER_AUTOPERSP) != 0;
1197
1198         BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0);
1199
1200         if (ED_view3d_camera_lock_check(v3d, rv3d))
1201                 return false;
1202
1203         if (rv3d->persp != RV3D_PERSP) {
1204                 if (rv3d->persp == RV3D_CAMOB) {
1205                         /* If autopersp and previous view was an axis one, switch back to PERSP mode, else reuse previous mode. */
1206                         char persp = (autopersp && RV3D_VIEW_IS_AXIS(rv3d->lview)) ? RV3D_PERSP : rv3d->lpersp;
1207                         view3d_persp_switch_from_camera(v3d, rv3d, persp);
1208                 }
1209                 else if (autopersp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
1210                         rv3d->persp = RV3D_PERSP;
1211                 }
1212                 return true;
1213         }
1214
1215         return false;
1216 }
1217
1218 static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1219 {
1220         ViewOpsData *vod;
1221
1222         /* makes op->customdata */
1223         viewops_data_alloc(C, op);
1224         vod = op->customdata;
1225
1226         /* poll should check but in some cases fails, see poll func for details */
1227         if (vod->rv3d->viewlock & RV3D_LOCKED) {
1228                 viewops_data_free(C, op);
1229                 return OPERATOR_PASS_THROUGH;
1230         }
1231
1232         /* switch from camera view when: */
1233         if (view3d_ensure_persp(vod->v3d, vod->ar)) {
1234                 /* If we're switching from camera view to the perspective one,
1235                  * need to tag viewport update, so camera vuew and borders
1236                  * are properly updated.
1237                  */
1238                 ED_region_tag_redraw(vod->ar);
1239         }
1240
1241         viewops_data_create(C, op, event);
1242
1243         if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) {
1244                 /* Rotate direction we keep always same */
1245                 int x, y;
1246
1247                 if (event->type == MOUSEPAN) {
1248                         if (U.uiflag2 & USER_TRACKPAD_NATURAL) {
1249                                 x = 2 * event->x - event->prevx;
1250                                 y = 2 * event->y - event->prevy;
1251                         }
1252                         else {
1253                                 x = event->prevx;
1254                                 y = event->prevy;
1255                         }
1256                 }
1257                 else {
1258                         /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
1259                         x = event->prevx;
1260                         y = event->y;
1261                 }
1262
1263                 viewrotate_apply(vod, x, y);
1264                 ED_view3d_depth_tag_update(vod->rv3d);
1265
1266                 viewops_data_free(C, op);
1267
1268                 return OPERATOR_FINISHED;
1269         }
1270         else {
1271                 /* add temp handler */
1272                 WM_event_add_modal_handler(C, op);
1273
1274                 return OPERATOR_RUNNING_MODAL;
1275         }
1276 }
1277
1278 /* test for unlocked camera view in quad view */
1279 static int view3d_camera_user_poll(bContext *C)
1280 {
1281         View3D *v3d;
1282         ARegion *ar;
1283
1284         if (ED_view3d_context_user_region(C, &v3d, &ar)) {
1285                 RegionView3D *rv3d = ar->regiondata;
1286                 if (rv3d->persp == RV3D_CAMOB) {
1287                         return 1;
1288                 }
1289         }
1290
1291         return 0;
1292 }
1293
1294 static int view3d_lock_poll(bContext *C)
1295 {
1296         View3D *v3d = CTX_wm_view3d(C);
1297         if (v3d) {
1298                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1299                 if (rv3d) {
1300                         return ED_view3d_offset_lock_check(v3d, rv3d);
1301                 }
1302         }
1303         return false;
1304 }
1305
1306 static void viewrotate_cancel(bContext *C, wmOperator *op)
1307 {
1308         viewops_data_free(C, op);
1309 }
1310
1311 void VIEW3D_OT_rotate(wmOperatorType *ot)
1312 {
1313         /* identifiers */
1314         ot->name = "Rotate View";
1315         ot->description = "Rotate the view";
1316         ot->idname = "VIEW3D_OT_rotate";
1317
1318         /* api callbacks */
1319         ot->invoke = viewrotate_invoke;
1320         ot->modal = viewrotate_modal;
1321         ot->poll = ED_operator_region_view3d_active;
1322         ot->cancel = viewrotate_cancel;
1323
1324         /* flags */
1325         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
1326 }
1327
1328 /** \name NDOF Utility Functions
1329  * \{ */
1330
1331 #define NDOF_HAS_TRANSLATE ((!ED_view3d_offset_lock_check(v3d, rv3d)) && !is_zero_v3(ndof->tvec))
1332 #define NDOF_HAS_ROTATE    (((rv3d->viewlock & RV3D_LOCKED) == 0) && !is_zero_v3(ndof->rvec))
1333
1334 /**
1335  * \param depth_pt: A point to calculate the depth (in perspective mode)
1336  */
1337 static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth_pt[3])
1338 {
1339         float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND;
1340
1341         if (rv3d->is_persp) {
1342                 speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL);
1343         }
1344
1345         return speed;
1346 }
1347
1348 static float view3d_ndof_pan_speed_calc_from_dist(RegionView3D *rv3d, const float dist)
1349 {
1350         float viewinv[4];
1351         float tvec[3];
1352
1353         BLI_assert(dist >= 0.0f);
1354
1355         copy_v3_fl3(tvec, 0.0f, 0.0f, dist);
1356         /* rv3d->viewinv isn't always valid */
1357 #if 0
1358         mul_mat3_m4_v3(rv3d->viewinv, tvec);
1359 #else
1360         invert_qt_qt_normalized(viewinv, rv3d->viewquat);
1361         mul_qt_v3(viewinv, tvec);
1362 #endif
1363
1364         return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
1365 }
1366
1367 static float view3d_ndof_pan_speed_calc(RegionView3D *rv3d)
1368 {
1369         float tvec[3];
1370         negate_v3_v3(tvec, rv3d->ofs);
1371
1372         return view3d_ndof_pan_speed_calc_ex(rv3d, tvec);
1373 }
1374
1375 /**
1376  * Zoom and pan in the same function since sometimes zoom is interpreted as dolly (pan forward).
1377  *
1378  * \param has_zoom zoom, otherwise dolly, often `!rv3d->is_persp` since it doesnt make sense to dolly in ortho.
1379  */
1380 static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof, ScrArea *sa, ARegion *ar,
1381                                  const bool has_translate, const bool has_zoom)
1382 {
1383         RegionView3D *rv3d = ar->regiondata;
1384         float view_inv[4];
1385         float pan_vec[3];
1386
1387         if (has_translate == false && has_zoom == false) {
1388                 return;
1389         }
1390
1391         WM_event_ndof_pan_get(ndof, pan_vec, false);
1392
1393         if (has_zoom) {
1394                 /* zoom with Z */
1395
1396                 /* Zoom!
1397                  * velocity should be proportional to the linear velocity attained by rotational motion of same strength
1398                  * [got that?]
1399                  * proportional to arclength = radius * angle
1400                  */
1401
1402                 pan_vec[2] = 0.0f;
1403
1404                 /* "zoom in" or "translate"? depends on zoom mode in user settings? */
1405                 if (ndof->tvec[2]) {
1406                         float zoom_distance = rv3d->dist * ndof->dt * ndof->tvec[2];
1407
1408                         if (U.ndof_flag & NDOF_ZOOM_INVERT)
1409                                 zoom_distance = -zoom_distance;
1410
1411                         rv3d->dist += zoom_distance;
1412                 }
1413         }
1414         else {
1415                 /* dolly with Z */
1416
1417                 /* all callers must check */
1418                 if (has_translate) {
1419                         BLI_assert(ED_view3d_offset_lock_check((View3D *)sa->spacedata.first, rv3d) == false);
1420                 }
1421         }
1422
1423         if (has_translate) {
1424                 const float speed = view3d_ndof_pan_speed_calc(rv3d);
1425
1426                 mul_v3_fl(pan_vec, speed * ndof->dt);
1427
1428                 /* transform motion from view to world coordinates */
1429                 invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1430                 mul_qt_v3(view_inv, pan_vec);
1431
1432                 /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1433                 sub_v3_v3(rv3d->ofs, pan_vec);
1434
1435                 if (rv3d->viewlock & RV3D_BOXVIEW) {
1436                         view3d_boxview_sync(sa, ar);
1437                 }
1438         }
1439 }
1440
1441
1442 static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, ScrArea *sa, ARegion *ar,
1443                               /* optional, can be NULL*/
1444                               ViewOpsData *vod)
1445 {
1446         View3D *v3d = sa->spacedata.first;
1447         RegionView3D *rv3d = ar->regiondata;
1448
1449         float view_inv[4];
1450
1451         BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0);
1452
1453         view3d_ensure_persp(v3d, ar);
1454
1455         rv3d->view = RV3D_VIEW_USER;
1456
1457         invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1458
1459         if (U.ndof_flag & NDOF_TURNTABLE) {
1460                 float rot[3];
1461
1462                 /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
1463                 float angle, quat[4];
1464                 float xvec[3] = {1, 0, 0};
1465
1466                 /* only use XY, ignore Z */
1467                 WM_event_ndof_rotate_get(ndof, rot);
1468
1469                 /* Determine the direction of the x vector (for rotating up and down) */
1470                 mul_qt_v3(view_inv, xvec);
1471
1472                 /* Perform the up/down rotation */
1473                 angle = ndof->dt * rot[0];
1474                 axis_angle_to_quat(quat, xvec, angle);
1475                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
1476
1477                 /* Perform the orbital rotation */
1478                 angle = ndof->dt * rot[1];
1479
1480                 /* update the onscreen doo-dad */
1481                 rv3d->rot_angle = angle;
1482                 rv3d->rot_axis[0] = 0;
1483                 rv3d->rot_axis[1] = 0;
1484                 rv3d->rot_axis[2] = 1;
1485
1486                 axis_angle_to_quat_single(quat, 'Z', angle);
1487                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
1488
1489         }
1490         else {
1491                 float quat[4];
1492                 float axis[3];
1493                 float angle = WM_event_ndof_to_axis_angle(ndof, axis);
1494
1495                 /* transform rotation axis from view to world coordinates */
1496                 mul_qt_v3(view_inv, axis);
1497
1498                 /* update the onscreen doo-dad */
1499                 rv3d->rot_angle = angle;
1500                 copy_v3_v3(rv3d->rot_axis, axis);
1501
1502                 axis_angle_to_quat(quat, axis, angle);
1503
1504                 /* apply rotation */
1505                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat);
1506         }
1507
1508         if (vod) {
1509                 viewrotate_apply_dyn_ofs(vod, rv3d->viewquat);
1510         }
1511 }
1512
1513 /**
1514  * Called from both fly mode and walk mode,
1515  */
1516 void view3d_ndof_fly(
1517         const wmNDOFMotionData *ndof,
1518         View3D *v3d, RegionView3D *rv3d,
1519         const bool use_precision, const short protectflag,
1520         bool *r_has_translate, bool *r_has_rotate)
1521 {
1522         bool has_translate = NDOF_HAS_TRANSLATE;
1523         bool has_rotate = NDOF_HAS_ROTATE;
1524
1525         float view_inv[4];
1526         invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1527
1528         rv3d->rot_angle = 0.0f;  /* disable onscreen rotation doo-dad */
1529
1530         if (has_translate) {
1531                 /* ignore real 'dist' since fly has its own speed settings,
1532                  * also its overwritten at this point. */
1533                 float speed = view3d_ndof_pan_speed_calc_from_dist(rv3d, 1.0f);
1534                 float trans[3], trans_orig_y;
1535
1536                 if (use_precision)
1537                         speed *= 0.2f;
1538
1539                 WM_event_ndof_pan_get(ndof, trans, false);
1540                 mul_v3_fl(trans, speed * ndof->dt);
1541                 trans_orig_y = trans[1];
1542
1543                 if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
1544                         trans[1] = 0.0f;
1545                 }
1546
1547                 /* transform motion from view to world coordinates */
1548                 mul_qt_v3(view_inv, trans);
1549
1550                 if (U.ndof_flag & NDOF_FLY_HELICOPTER) {
1551                         /* replace world z component with device y (yes it makes sense) */
1552                         trans[2] = trans_orig_y;
1553                 }
1554
1555                 if (rv3d->persp == RV3D_CAMOB) {
1556                         /* respect camera position locks */
1557                         if (protectflag & OB_LOCK_LOCX) trans[0] = 0.0f;
1558                         if (protectflag & OB_LOCK_LOCY) trans[1] = 0.0f;
1559                         if (protectflag & OB_LOCK_LOCZ) trans[2] = 0.0f;
1560                 }
1561
1562                 if (!is_zero_v3(trans)) {
1563                         /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1564                         sub_v3_v3(rv3d->ofs, trans);
1565                         has_translate = true;
1566                 }
1567                 else {
1568                         has_translate = false;
1569                 }
1570         }
1571
1572         if (has_rotate) {
1573                 const float turn_sensitivity = 1.0f;
1574
1575                 float rotation[4];
1576                 float axis[3];
1577                 float angle = turn_sensitivity * WM_event_ndof_to_axis_angle(ndof, axis);
1578
1579                 if (fabsf(angle) > 0.0001f) {
1580                         has_rotate = true;
1581
1582                         if (use_precision)
1583                                 angle *= 0.2f;
1584
1585                         /* transform rotation axis from view to world coordinates */
1586                         mul_qt_v3(view_inv, axis);
1587
1588                         /* apply rotation to view */
1589                         axis_angle_to_quat(rotation, axis, angle);
1590                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
1591
1592                         if (U.ndof_flag & NDOF_LOCK_HORIZON) {
1593                                 /* force an upright viewpoint
1594                                  * TODO: make this less... sudden */
1595                                 float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */
1596                                 float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */
1597
1598                                 /* find new inverse since viewquat has changed */
1599                                 invert_qt_qt_normalized(view_inv, rv3d->viewquat);
1600                                 /* could apply reverse rotation to existing view_inv to save a few cycles */
1601
1602                                 /* transform view vectors to world coordinates */
1603                                 mul_qt_v3(view_inv, view_horizon);
1604                                 mul_qt_v3(view_inv, view_direction);
1605
1606
1607                                 /* find difference between view & world horizons
1608                                  * true horizon lives in world xy plane, so look only at difference in z */
1609                                 angle = -asinf(view_horizon[2]);
1610
1611                                 /* rotate view so view horizon = world horizon */
1612                                 axis_angle_to_quat(rotation, view_direction, angle);
1613                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
1614                         }
1615
1616                         rv3d->view = RV3D_VIEW_USER;
1617                 }
1618                 else {
1619                         has_rotate = false;
1620                 }
1621         }
1622
1623         *r_has_translate = has_translate;
1624         *r_has_rotate    = has_rotate;
1625 }
1626
1627 /** \} */
1628
1629
1630 /* -- "orbit" navigation (trackball/turntable)
1631  * -- zooming
1632  * -- panning in rotationally-locked views
1633  */
1634 static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1635 {
1636         
1637         if (event->type != NDOF_MOTION) {
1638                 return OPERATOR_CANCELLED;
1639         }
1640         else {
1641                 ViewOpsData *vod;
1642                 View3D *v3d;
1643                 RegionView3D *rv3d;
1644
1645                 const wmNDOFMotionData *ndof = event->customdata;
1646
1647                 viewops_data_alloc(C, op);
1648                 viewops_data_create_ex(C, op, event,
1649                                        (U.uiflag & USER_ORBIT_SELECTION) != 0, false);
1650
1651                 vod = op->customdata;
1652                 v3d = vod->v3d;
1653                 rv3d = vod->rv3d;
1654
1655                 /* off by default, until changed later this function */
1656                 rv3d->rot_angle = 0.0f;
1657
1658                 ED_view3d_camera_lock_init_ex(v3d, rv3d, false);
1659
1660                 if (ndof->progress != P_FINISHING) {
1661                         const bool has_rotation = NDOF_HAS_ROTATE;
1662                         /* if we can't rotate, fallback to translate (locked axis views) */
1663                         const bool has_translate = NDOF_HAS_TRANSLATE && (rv3d->viewlock & RV3D_LOCKED);
1664                         const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
1665
1666                         if (has_translate || has_zoom) {
1667                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, has_translate, has_zoom);
1668                         }
1669
1670                         if (has_rotation) {
1671                                 view3d_ndof_orbit(ndof, vod->sa, vod->ar, vod);
1672                         }
1673                 }
1674
1675                 ED_view3d_camera_lock_sync(v3d, rv3d);
1676
1677                 ED_region_tag_redraw(vod->ar);
1678
1679                 viewops_data_free(C, op);
1680
1681                 return OPERATOR_FINISHED;
1682         }
1683 }
1684
1685 void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
1686 {
1687         /* identifiers */
1688         ot->name = "NDOF Orbit View";
1689         ot->description = "Orbit the view using the 3D mouse";
1690         ot->idname = "VIEW3D_OT_ndof_orbit";
1691
1692         /* api callbacks */
1693         ot->invoke = ndof_orbit_invoke;
1694         ot->poll = ED_operator_view3d_active;
1695
1696         /* flags */
1697         ot->flag = 0;
1698 }
1699
1700 static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1701 {
1702         
1703         if (event->type != NDOF_MOTION) {
1704                 return OPERATOR_CANCELLED;
1705         }
1706         else {
1707                 ViewOpsData *vod;
1708                 View3D *v3d;
1709                 RegionView3D *rv3d;
1710
1711                 const wmNDOFMotionData *ndof = event->customdata;
1712
1713                 viewops_data_alloc(C, op);
1714                 viewops_data_create_ex(C, op, event,
1715                                        (U.uiflag & USER_ORBIT_SELECTION) != 0, false);
1716
1717                 vod = op->customdata;
1718                 v3d = vod->v3d;
1719                 rv3d = vod->rv3d;
1720
1721                 /* off by default, until changed later this function */
1722                 rv3d->rot_angle = 0.0f;
1723
1724                 ED_view3d_camera_lock_init_ex(v3d, rv3d, false);
1725
1726                 if (ndof->progress == P_FINISHING) {
1727                         /* pass */
1728                 }
1729                 else if ((rv3d->persp == RV3D_ORTHO) && RV3D_VIEW_IS_AXIS(rv3d->view)) {
1730                         /* if we can't rotate, fallback to translate (locked axis views) */
1731                         const bool has_translate = NDOF_HAS_TRANSLATE;
1732                         const bool has_zoom = (ndof->tvec[2] != 0.0f) && ED_view3d_offset_lock_check(v3d, rv3d);
1733
1734                         if (has_translate || has_zoom) {
1735                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, has_translate, true);
1736                         }
1737                 }
1738                 else if ((U.ndof_flag & NDOF_MODE_ORBIT) ||
1739                          ED_view3d_offset_lock_check(v3d, rv3d))
1740                 {
1741                         const bool has_rotation = NDOF_HAS_ROTATE;
1742                         const bool has_zoom = (ndof->tvec[2] != 0.0f);
1743
1744                         if (has_zoom) {
1745                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, false, has_zoom);
1746                         }
1747
1748                         if (has_rotation) {
1749                                 view3d_ndof_orbit(ndof, vod->sa, vod->ar, vod);
1750                         }
1751                 }
1752                 else {  /* free/explore (like fly mode) */
1753                         const bool has_rotation = NDOF_HAS_ROTATE;
1754                         const bool has_translate = NDOF_HAS_TRANSLATE;
1755                         const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
1756
1757                         float dist_backup;
1758
1759                         if (has_translate || has_zoom) {
1760                                 view3d_ndof_pan_zoom(ndof, vod->sa, vod->ar, has_translate, has_zoom);
1761                         }
1762
1763                         dist_backup = rv3d->dist;
1764                         ED_view3d_distance_set(rv3d, 0.0f);
1765
1766                         if (has_rotation) {
1767                                 view3d_ndof_orbit(ndof, vod->sa, vod->ar, NULL);
1768                         }
1769
1770                         ED_view3d_distance_set(rv3d, dist_backup);
1771                 }
1772
1773                 ED_view3d_camera_lock_sync(v3d, rv3d);
1774
1775                 ED_region_tag_redraw(vod->ar);
1776
1777                 viewops_data_free(C, op);
1778
1779                 return OPERATOR_FINISHED;
1780         }
1781 }
1782
1783 void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot)
1784 {
1785         /* identifiers */
1786         ot->name = "NDOF Orbit View with Zoom";
1787         ot->description = "Orbit and zoom the view using the 3D mouse";
1788         ot->idname = "VIEW3D_OT_ndof_orbit_zoom";
1789
1790         /* api callbacks */
1791         ot->invoke = ndof_orbit_zoom_invoke;
1792         ot->poll = ED_operator_view3d_active;
1793
1794         /* flags */
1795         ot->flag = 0;
1796 }
1797
1798 /* -- "pan" navigation
1799  * -- zoom or dolly?
1800  */
1801 static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1802 {
1803         if (event->type != NDOF_MOTION) {
1804                 return OPERATOR_CANCELLED;
1805         }
1806         else {
1807                 View3D *v3d = CTX_wm_view3d(C);
1808                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1809                 const wmNDOFMotionData *ndof = event->customdata;
1810
1811                 const bool has_translate = NDOF_HAS_TRANSLATE;
1812                 const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp;
1813
1814                 /* we're panning here! so erase any leftover rotation from other operators */
1815                 rv3d->rot_angle = 0.0f;
1816
1817                 if (!(has_translate || has_zoom))
1818                         return OPERATOR_CANCELLED;
1819
1820                 ED_view3d_camera_lock_init_ex(v3d, rv3d, false);
1821
1822                 if (ndof->progress != P_FINISHING) {
1823                         ScrArea *sa = CTX_wm_area(C);
1824                         ARegion *ar = CTX_wm_region(C);
1825
1826                         if (has_translate || has_zoom) {
1827                                 view3d_ndof_pan_zoom(ndof, sa, ar, has_translate, has_zoom);
1828                         }
1829                 }
1830
1831                 ED_view3d_camera_lock_sync(v3d, rv3d);
1832
1833                 ED_region_tag_redraw(CTX_wm_region(C));
1834
1835                 return OPERATOR_FINISHED;
1836         }
1837 }
1838
1839 void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
1840 {
1841         /* identifiers */
1842         ot->name = "NDOF Pan View";
1843         ot->description = "Pan the view with the 3D mouse";
1844         ot->idname = "VIEW3D_OT_ndof_pan";
1845
1846         /* api callbacks */
1847         ot->invoke = ndof_pan_invoke;
1848         ot->poll = ED_operator_view3d_active;
1849
1850         /* flags */
1851         ot->flag = 0;
1852 }
1853
1854
1855 /**
1856  * wraps #ndof_orbit_zoom but never restrict to orbit.
1857  */
1858 static int ndof_all_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1859 {
1860         /* weak!, but it works */
1861         const int ndof_flag = U.ndof_flag;
1862         int ret;
1863
1864         U.ndof_flag &= ~NDOF_MODE_ORBIT;
1865
1866         ret = ndof_orbit_zoom_invoke(C, op, event);
1867
1868         U.ndof_flag = ndof_flag;
1869
1870         return ret;
1871 }
1872
1873 void VIEW3D_OT_ndof_all(struct wmOperatorType *ot)
1874 {
1875         /* identifiers */
1876         ot->name = "NDOF Move View";
1877         ot->description = "Pan and rotate the view with the 3D mouse";
1878         ot->idname = "VIEW3D_OT_ndof_all";
1879
1880         /* api callbacks */
1881         ot->invoke = ndof_all_invoke;
1882         ot->poll = ED_operator_view3d_active;
1883
1884         /* flags */
1885         ot->flag = 0;
1886 }
1887
1888 /* ************************ viewmove ******************************** */
1889
1890
1891 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
1892
1893 /* called in transform_ops.c, on each regeneration of keymaps  */
1894 void viewmove_modal_keymap(wmKeyConfig *keyconf)
1895 {
1896         static EnumPropertyItem modal_items[] = {
1897                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
1898                 
1899                 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
1900                 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1901
1902                 {0, NULL, 0, NULL, NULL}
1903         };
1904
1905         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Move Modal");
1906
1907         /* this function is called for each spacetype, only needs to add map once */
1908         if (keymap && keymap->modal_items) return;
1909
1910         keymap = WM_modalkeymap_add(keyconf, "View3D Move Modal", modal_items);
1911
1912         /* items for modal map */
1913         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1914         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1915
1916         /* disabled mode switching for now, can re-implement better, later on */
1917 #if 0
1918         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
1919         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
1920         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1921 #endif
1922         
1923         /* assign map to operators */
1924         WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
1925 }
1926
1927
1928 static void viewmove_apply(ViewOpsData *vod, int x, int y)
1929 {
1930         if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
1931                 vod->rv3d->ofs_lock[0] -= ((vod->oldx - x) * 2.0f) / (float)vod->ar->winx;
1932                 vod->rv3d->ofs_lock[1] -= ((vod->oldy - y) * 2.0f) / (float)vod->ar->winy;
1933         }
1934         else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
1935                 const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
1936                 vod->rv3d->camdx += (vod->oldx - x) / (vod->ar->winx * zoomfac);
1937                 vod->rv3d->camdy += (vod->oldy - y) / (vod->ar->winy * zoomfac);
1938                 CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
1939                 CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
1940         }
1941         else {
1942                 float dvec[3];
1943                 float mval_f[2];
1944
1945                 mval_f[0] = x - vod->oldx;
1946                 mval_f[1] = y - vod->oldy;
1947                 ED_view3d_win_to_delta(vod->ar, mval_f, dvec, vod->zfac);
1948
1949                 add_v3_v3(vod->rv3d->ofs, dvec);
1950
1951                 if (vod->rv3d->viewlock & RV3D_BOXVIEW)
1952                         view3d_boxview_sync(vod->sa, vod->ar);
1953         }
1954
1955         vod->oldx = x;
1956         vod->oldy = y;
1957
1958         ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
1959
1960         ED_region_tag_redraw(vod->ar);
1961 }
1962
1963
1964 static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
1965 {
1966
1967         ViewOpsData *vod = op->customdata;
1968         short event_code = VIEW_PASS;
1969         bool use_autokey = false;
1970         int ret = OPERATOR_RUNNING_MODAL;
1971
1972         /* execute the events */
1973         if (event->type == MOUSEMOVE) {
1974                 event_code = VIEW_APPLY;
1975         }
1976         else if (event->type == EVT_MODAL_MAP) {
1977                 switch (event->val) {
1978                         case VIEW_MODAL_CONFIRM:
1979                                 event_code = VIEW_CONFIRM;
1980                                 break;
1981                         case VIEWROT_MODAL_SWITCH_ZOOM:
1982                                 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
1983                                 event_code = VIEW_CONFIRM;
1984                                 break;
1985                         case VIEWROT_MODAL_SWITCH_ROTATE:
1986                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
1987                                 event_code = VIEW_CONFIRM;
1988                                 break;
1989                 }
1990         }
1991         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
1992                 event_code = VIEW_CONFIRM;
1993         }
1994
1995         if (event_code == VIEW_APPLY) {
1996                 viewmove_apply(vod, event->x, event->y);
1997                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
1998                         use_autokey = true;
1999                 }
2000         }
2001         else if (event_code == VIEW_CONFIRM) {
2002                 ED_view3d_depth_tag_update(vod->rv3d);
2003                 use_autokey = true;
2004                 ret = OPERATOR_FINISHED;
2005         }
2006
2007         if (use_autokey) {
2008                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2009         }
2010
2011         if (ret & OPERATOR_FINISHED) {
2012                 viewops_data_free(C, op);
2013         }
2014
2015         return ret;
2016 }
2017
2018 static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2019 {
2020         ViewOpsData *vod;
2021
2022         /* makes op->customdata */
2023         viewops_data_alloc(C, op);
2024         viewops_data_create(C, op, event);
2025         vod = op->customdata;
2026
2027         if (event->type == MOUSEPAN) {
2028                 /* invert it, trackpad scroll follows same principle as 2d windows this way */
2029                 viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy);
2030                 ED_view3d_depth_tag_update(vod->rv3d);
2031                 
2032                 viewops_data_free(C, op);
2033                 
2034                 return OPERATOR_FINISHED;
2035         }
2036         else {
2037                 /* add temp handler */
2038                 WM_event_add_modal_handler(C, op);
2039
2040                 return OPERATOR_RUNNING_MODAL;
2041         }
2042 }
2043
2044 static void viewmove_cancel(bContext *C, wmOperator *op)
2045 {
2046         viewops_data_free(C, op);
2047 }
2048
2049 void VIEW3D_OT_move(wmOperatorType *ot)
2050 {
2051
2052         /* identifiers */
2053         ot->name = "Move View";
2054         ot->description = "Move the view";
2055         ot->idname = "VIEW3D_OT_move";
2056
2057         /* api callbacks */
2058         ot->invoke = viewmove_invoke;
2059         ot->modal = viewmove_modal;
2060         ot->poll = ED_operator_view3d_active;
2061         ot->cancel = viewmove_cancel;
2062
2063         /* flags */
2064         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
2065 }
2066
2067 /* ************************ viewzoom ******************************** */
2068
2069 /* viewdolly_modal_keymap has an exact copy of this, apply fixes to both */
2070 /* called in transform_ops.c, on each regeneration of keymaps  */
2071 void viewzoom_modal_keymap(wmKeyConfig *keyconf)
2072 {
2073         static EnumPropertyItem modal_items[] = {
2074                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
2075                 
2076                 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
2077                 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
2078
2079                 {0, NULL, 0, NULL, NULL}
2080         };
2081
2082         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Zoom Modal");
2083
2084         /* this function is called for each spacetype, only needs to add map once */
2085         if (keymap && keymap->modal_items) return;
2086
2087         keymap = WM_modalkeymap_add(keyconf, "View3D Zoom Modal", modal_items);
2088
2089         /* items for modal map */
2090         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2091         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2092
2093         /* disabled mode switching for now, can re-implement better, later on */
2094 #if 0
2095         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2096         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2097         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
2098 #endif
2099         
2100         /* assign map to operators */
2101         WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
2102 }
2103
2104 static void view_zoom_mouseloc_camera(
2105         Scene *scene, View3D *v3d,
2106         ARegion *ar, float dfac, int mx, int my)
2107 {
2108         RegionView3D *rv3d = ar->regiondata;
2109         const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
2110         const float zoomfac_new = CLAMPIS(zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR);
2111         const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new);
2112
2113
2114         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
2115                 float zoomfac_px;
2116                 rctf camera_frame_old;
2117                 rctf camera_frame_new;
2118
2119                 const float pt_src[2] = {mx, my};
2120                 float pt_dst[2];
2121                 float delta_px[2];
2122
2123                 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_old, false);
2124                 BLI_rctf_translate(&camera_frame_old, ar->winrct.xmin, ar->winrct.ymin);
2125
2126                 rv3d->camzoom = camzoom_new;
2127                 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
2128
2129                 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_new, false);
2130                 BLI_rctf_translate(&camera_frame_new, ar->winrct.xmin, ar->winrct.ymin);
2131
2132                 BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src);
2133                 sub_v2_v2v2(delta_px, pt_dst, pt_src);
2134
2135                 /* translate the camera offset using pixel space delta
2136                  * mapped back to the camera (same logic as panning in camera view) */
2137                 zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f;
2138
2139                 rv3d->camdx += delta_px[0] / (ar->winx * zoomfac_px);
2140                 rv3d->camdy += delta_px[1] / (ar->winy * zoomfac_px);
2141                 CLAMP(rv3d->camdx, -1.0f, 1.0f);
2142                 CLAMP(rv3d->camdy, -1.0f, 1.0f);
2143         }
2144         else {
2145                 rv3d->camzoom = camzoom_new;
2146                 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
2147         }
2148 }
2149
2150 static void view_zoom_mouseloc_3d(ARegion *ar, float dfac, int mx, int my)
2151 {
2152         RegionView3D *rv3d = ar->regiondata;
2153         const float dist_new = rv3d->dist * dfac;
2154
2155         if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
2156                 float dvec[3];
2157                 float tvec[3];
2158                 float tpos[3];
2159                 float mval_f[2];
2160
2161                 float zfac;
2162
2163                 negate_v3_v3(tpos, rv3d->ofs);
2164
2165                 mval_f[0] = (float)(((mx - ar->winrct.xmin) * 2) - ar->winx) / 2.0f;
2166                 mval_f[1] = (float)(((my - ar->winrct.ymin) * 2) - ar->winy) / 2.0f;
2167
2168                 /* Project cursor position into 3D space */
2169                 zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL);
2170                 ED_view3d_win_to_delta(ar, mval_f, dvec, zfac);
2171
2172                 /* Calculate view target position for dolly */
2173                 add_v3_v3v3(tvec, tpos, dvec);
2174                 negate_v3(tvec);
2175
2176                 /* Offset to target position and dolly */
2177                 copy_v3_v3(rv3d->ofs, tvec);
2178                 rv3d->dist = dist_new;
2179
2180                 /* Calculate final offset */
2181                 madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
2182         }
2183         else {
2184                 rv3d->dist = dist_new;
2185         }
2186 }
2187
2188 static float viewzoom_scale_value(
2189         const rcti *winrct,
2190         const short viewzoom,
2191         const bool zoom_invert, const bool zoom_invert_force,
2192         const int xy[2], const int xy_orig[2],
2193         const float val, const float val_orig,
2194         double *r_timer_lastdraw)
2195 {
2196         float zfac;
2197
2198         if (viewzoom == USER_ZOOM_CONT) {
2199                 double time = PIL_check_seconds_timer();
2200                 float time_step = (float)(time - *r_timer_lastdraw);
2201                 float fac;
2202
2203                 if (U.uiflag & USER_ZOOM_HORIZ) {
2204                         fac = (float)(xy_orig[0] - xy[0]);
2205                 }
2206                 else {
2207                         fac = (float)(xy_orig[1] - xy[1]);
2208                 }
2209
2210                 if (zoom_invert != zoom_invert_force) {
2211                         fac = -fac;
2212                 }
2213
2214                 /* oldstyle zoom */
2215                 zfac = 1.0f + ((fac / 20.0f) * time_step);
2216                 *r_timer_lastdraw = time;
2217         }
2218         else if (viewzoom == USER_ZOOM_SCALE) {
2219                 /* method which zooms based on how far you move the mouse */
2220
2221                 const int ctr[2] = {
2222                     BLI_rcti_cent_x(winrct),
2223                     BLI_rcti_cent_y(winrct),
2224                 };
2225                 float len_new = 5 + len_v2v2_int(ctr, xy);
2226                 float len_old = 5 + len_v2v2_int(ctr, xy_orig);
2227
2228                 /* intentionally ignore 'zoom_invert' for scale */
2229                 if (zoom_invert_force) {
2230                         SWAP(float, len_new, len_old);
2231                 }
2232
2233                 zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val;
2234         }
2235         else {  /* USER_ZOOM_DOLLY */
2236                 float len_new = 5;
2237                 float len_old = 5;
2238
2239                 if (U.uiflag & USER_ZOOM_HORIZ) {
2240                         len_new += (winrct->xmax - xy[0]);
2241                         len_old += (winrct->xmax - xy_orig[0]);
2242                 }
2243                 else {
2244                         len_new += (winrct->ymax - xy[1]);
2245                         len_old += (winrct->ymax - xy_orig[1]);
2246                 }
2247
2248                 if (zoom_invert != zoom_invert_force) {
2249                         SWAP(float, len_new, len_old);
2250                 }
2251
2252                 zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val;
2253         }
2254
2255
2256         return zfac;
2257 }
2258
2259 static void viewzoom_apply_camera(
2260         ViewOpsData *vod, const int xy[2],
2261         const short viewzoom, const bool zoom_invert)
2262 {
2263         float zfac;
2264         float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->camzoom_prev) * 2.0f;
2265         float zoomfac =      BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
2266
2267         zfac = viewzoom_scale_value(
2268                &vod->ar->winrct, viewzoom, zoom_invert, true, xy, &vod->origx,
2269                zoomfac, zoomfac_prev,
2270                &vod->timer_lastdraw);
2271
2272         if (zfac != 1.0f && zfac != 0.0f) {
2273                 /* calculate inverted, then invert again (needed because of camera zoom scaling) */
2274                 zfac = 1.0f / zfac;
2275                 view_zoom_mouseloc_camera(
2276                         vod->scene, vod->v3d,
2277                         vod->ar, zfac, vod->oldx, vod->oldy);
2278         }
2279
2280         ED_region_tag_redraw(vod->ar);
2281 }
2282
2283 static void viewzoom_apply_3d(
2284         ViewOpsData *vod, const int xy[2],
2285         const short viewzoom, const bool zoom_invert)
2286 {
2287         float zfac;
2288         float dist_range[2];
2289
2290         ED_view3d_dist_range_get(vod->v3d, dist_range);
2291
2292         zfac = viewzoom_scale_value(
2293                &vod->ar->winrct, viewzoom, zoom_invert, false, xy, &vod->origx,
2294                vod->rv3d->dist, vod->dist_prev,
2295                &vod->timer_lastdraw);
2296
2297         if (zfac != 1.0f) {
2298                 const float zfac_min = dist_range[0] / vod->rv3d->dist;
2299                 const float zfac_max = dist_range[1] / vod->rv3d->dist;
2300                 CLAMP(zfac, zfac_min, zfac_max);
2301
2302                 view_zoom_mouseloc_3d(
2303                         vod->ar, zfac, vod->oldx, vod->oldy);
2304         }
2305
2306         /* these limits were in old code too */
2307         CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]);
2308
2309         if (vod->rv3d->viewlock & RV3D_BOXVIEW)
2310                 view3d_boxview_sync(vod->sa, vod->ar);
2311
2312         ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
2313
2314         ED_region_tag_redraw(vod->ar);
2315 }
2316
2317 static void viewzoom_apply(
2318         ViewOpsData *vod, const int xy[2],
2319         const short viewzoom, const bool zoom_invert)
2320 {
2321         if ((vod->rv3d->persp == RV3D_CAMOB) &&
2322             (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0)
2323         {
2324                 viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert);
2325         }
2326         else {
2327                 viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert);
2328         }
2329 }
2330
2331 static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
2332 {
2333         ViewOpsData *vod = op->customdata;
2334         short event_code = VIEW_PASS;
2335         bool use_autokey = false;
2336         int ret = OPERATOR_RUNNING_MODAL;
2337
2338         /* execute the events */
2339         if (event->type == TIMER && event->customdata == vod->timer) {
2340                 /* continuous zoom */
2341                 event_code = VIEW_APPLY;
2342         }
2343         else if (event->type == MOUSEMOVE) {
2344                 event_code = VIEW_APPLY;
2345         }
2346         else if (event->type == EVT_MODAL_MAP) {
2347                 switch (event->val) {
2348                         case VIEW_MODAL_CONFIRM:
2349                                 event_code = VIEW_CONFIRM;
2350                                 break;
2351                         case VIEWROT_MODAL_SWITCH_MOVE:
2352                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
2353                                 event_code = VIEW_CONFIRM;
2354                                 break;
2355                         case VIEWROT_MODAL_SWITCH_ROTATE:
2356                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
2357                                 event_code = VIEW_CONFIRM;
2358                                 break;
2359                 }
2360         }
2361         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
2362                 event_code = VIEW_CONFIRM;
2363         }
2364
2365         if (event_code == VIEW_APPLY) {
2366                 viewzoom_apply(vod, &event->x, U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0);
2367                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
2368                         use_autokey = true;
2369                 }
2370         }
2371         else if (event_code == VIEW_CONFIRM) {
2372                 ED_view3d_depth_tag_update(vod->rv3d);
2373                 use_autokey = true;
2374                 ret = OPERATOR_FINISHED;
2375         }
2376
2377         if (use_autokey) {
2378                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2379         }
2380
2381         if (ret & OPERATOR_FINISHED) {
2382                 viewops_data_free(C, op);
2383         }
2384
2385         return ret;
2386 }
2387
2388 static int viewzoom_exec(bContext *C, wmOperator *op)
2389 {
2390         Scene *scene = CTX_data_scene(C);
2391         View3D *v3d;
2392         RegionView3D *rv3d;
2393         ScrArea *sa;
2394         ARegion *ar;
2395         bool use_cam_zoom;
2396         float dist_range[2];
2397
2398         const int delta = RNA_int_get(op->ptr, "delta");
2399         int mx, my;
2400
2401         if (op->customdata) {
2402                 ViewOpsData *vod = op->customdata;
2403
2404                 sa = vod->sa;
2405                 ar = vod->ar;
2406         }
2407         else {
2408                 sa = CTX_wm_area(C);
2409                 ar = CTX_wm_region(C);
2410         }
2411
2412         v3d = sa->spacedata.first;
2413         rv3d = ar->regiondata;
2414
2415         mx = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : ar->winx / 2;
2416         my = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : ar->winy / 2;
2417
2418         use_cam_zoom = (rv3d->persp == RV3D_CAMOB) && !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
2419
2420         ED_view3d_dist_range_get(v3d, dist_range);
2421
2422         if (delta < 0) {
2423                 const float step = 1.2f;
2424                 /* this min and max is also in viewmove() */
2425                 if (use_cam_zoom) {
2426                         view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my);
2427                 }
2428                 else {
2429                         if (rv3d->dist < dist_range[1]) {
2430                                 view_zoom_mouseloc_3d(ar, step, mx, my);
2431                         }
2432                 }
2433         }
2434         else {
2435                 const float step = 1.0f / 1.2f;
2436                 if (use_cam_zoom) {
2437                         view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my);
2438                 }
2439                 else {
2440                         if (rv3d->dist > dist_range[0]) {
2441                                 view_zoom_mouseloc_3d(ar, step, mx, my);
2442                         }
2443                 }
2444         }
2445
2446         if (rv3d->viewlock & RV3D_BOXVIEW)
2447                 view3d_boxview_sync(sa, ar);
2448
2449         ED_view3d_depth_tag_update(rv3d);
2450
2451         ED_view3d_camera_lock_sync(v3d, rv3d);
2452         ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true);
2453
2454         ED_region_tag_redraw(ar);
2455
2456         viewops_data_free(C, op);
2457
2458         return OPERATOR_FINISHED;
2459 }
2460
2461 /* this is an exact copy of viewzoom_modal_keymap */
2462 /* called in transform_ops.c, on each regeneration of keymaps  */
2463 void viewdolly_modal_keymap(wmKeyConfig *keyconf)
2464 {
2465         static EnumPropertyItem modal_items[] = {
2466                 {VIEW_MODAL_CONFIRM,    "CONFIRM", 0, "Confirm", ""},
2467
2468                 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
2469                 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
2470
2471                 {0, NULL, 0, NULL, NULL}
2472         };
2473
2474         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Dolly Modal");
2475
2476         /* this function is called for each spacetype, only needs to add map once */
2477         if (keymap && keymap->modal_items) return;
2478
2479         keymap = WM_modalkeymap_add(keyconf, "View3D Dolly Modal", modal_items);
2480
2481         /* items for modal map */
2482         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2483         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2484
2485         /* disabled mode switching for now, can re-implement better, later on */
2486 #if 0
2487         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2488         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2489         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
2490 #endif
2491         
2492         /* assign map to operators */
2493         WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
2494 }
2495
2496 /* viewdolly_invoke() copied this function, changes here may apply there */
2497 static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2498 {
2499         ViewOpsData *vod;
2500
2501         /* makes op->customdata */
2502         viewops_data_alloc(C, op);
2503         viewops_data_create(C, op, event);
2504         vod = op->customdata;
2505
2506         /* if one or the other zoom position aren't set, set from event */
2507         if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
2508                 RNA_int_set(op->ptr, "mx", event->x);
2509                 RNA_int_set(op->ptr, "my", event->y);
2510         }
2511
2512         if (RNA_struct_property_is_set(op->ptr, "delta")) {
2513                 viewzoom_exec(C, op);
2514         }
2515         else {
2516                 if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
2517
2518                         if (U.uiflag & USER_ZOOM_HORIZ) {
2519                                 vod->origx = vod->oldx = event->x;
2520                                 viewzoom_apply(vod, &event->prevx, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
2521                         }
2522                         else {
2523                                 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
2524                                 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
2525                                 viewzoom_apply(vod, &event->prevx, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
2526                         }
2527                         ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2528
2529                         ED_view3d_depth_tag_update(vod->rv3d);
2530
2531                         viewops_data_free(C, op);
2532                         return OPERATOR_FINISHED;
2533                 }
2534                 else {
2535                         if (U.viewzoom == USER_ZOOM_CONT) {
2536                                 /* needs a timer to continue redrawing */
2537                                 vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
2538                                 vod->timer_lastdraw = PIL_check_seconds_timer();
2539                         }
2540
2541                         /* add temp handler */
2542                         WM_event_add_modal_handler(C, op);
2543
2544                         return OPERATOR_RUNNING_MODAL;
2545                 }
2546         }
2547         return OPERATOR_FINISHED;
2548 }
2549
2550 static void viewzoom_cancel(bContext *C, wmOperator *op)
2551 {
2552         viewops_data_free(C, op);
2553 }
2554
2555 void VIEW3D_OT_zoom(wmOperatorType *ot)
2556 {
2557         PropertyRNA *prop;
2558
2559         /* identifiers */
2560         ot->name = "Zoom View";
2561         ot->description = "Zoom in/out in the view";
2562         ot->idname = "VIEW3D_OT_zoom";
2563
2564         /* api callbacks */
2565         ot->invoke = viewzoom_invoke;
2566         ot->exec = viewzoom_exec;
2567         ot->modal = viewzoom_modal;
2568         ot->poll = ED_operator_region_view3d_active;
2569         ot->cancel = viewzoom_cancel;
2570
2571         /* flags */
2572         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
2573
2574         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2575         prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
2576         RNA_def_property_flag(prop, PROP_HIDDEN);
2577         prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
2578         RNA_def_property_flag(prop, PROP_HIDDEN);
2579 }
2580
2581
2582 /* ************************ viewdolly ******************************** */
2583 static void view_dolly_mouseloc(ARegion *ar, float orig_ofs[3], float dvec[3], float dfac)
2584 {
2585         RegionView3D *rv3d = ar->regiondata;
2586         madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
2587 }
2588
2589 static void viewdolly_apply(ViewOpsData *vod, int x, int y, const short zoom_invert)
2590 {
2591         float zfac = 1.0;
2592
2593         {
2594                 float len1, len2;
2595
2596                 if (U.uiflag & USER_ZOOM_HORIZ) {
2597                         len1 = (vod->ar->winrct.xmax - x) + 5;
2598                         len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
2599                 }
2600                 else {
2601                         len1 = (vod->ar->winrct.ymax - y) + 5;
2602                         len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
2603                 }
2604                 if (zoom_invert)
2605                         SWAP(float, len1, len2);
2606
2607                 zfac =  1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist);
2608         }
2609
2610         if (zfac != 1.0f)
2611                 view_dolly_mouseloc(vod->ar, vod->ofs, vod->mousevec, zfac);
2612
2613         if (vod->rv3d->viewlock & RV3D_BOXVIEW)
2614                 view3d_boxview_sync(vod->sa, vod->ar);
2615
2616         ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
2617
2618         ED_region_tag_redraw(vod->ar);
2619 }
2620
2621
2622 static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event)
2623 {
2624         ViewOpsData *vod = op->customdata;
2625         short event_code = VIEW_PASS;
2626         bool use_autokey = false;
2627         int ret = OPERATOR_RUNNING_MODAL;
2628
2629         /* execute the events */
2630         if (event->type == MOUSEMOVE) {
2631                 event_code = VIEW_APPLY;
2632         }
2633         else if (event->type == EVT_MODAL_MAP) {
2634                 switch (event->val) {
2635                         case VIEW_MODAL_CONFIRM:
2636                                 event_code = VIEW_CONFIRM;
2637                                 break;
2638                         case VIEWROT_MODAL_SWITCH_MOVE:
2639                                 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
2640                                 event_code = VIEW_CONFIRM;
2641                                 break;
2642                         case VIEWROT_MODAL_SWITCH_ROTATE:
2643                                 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
2644                                 event_code = VIEW_CONFIRM;
2645                                 break;
2646                 }
2647         }
2648         else if (event->type == vod->origkey && event->val == KM_RELEASE) {
2649                 event_code = VIEW_CONFIRM;
2650         }
2651
2652         if (event_code == VIEW_APPLY) {
2653                 viewdolly_apply(vod, event->x, event->y, (U.uiflag & USER_ZOOM_INVERT) != 0);
2654                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
2655                         use_autokey = true;
2656                 }
2657         }
2658         else if (event_code == VIEW_CONFIRM) {
2659                 ED_view3d_depth_tag_update(vod->rv3d);
2660                 use_autokey = true;
2661                 ret = OPERATOR_FINISHED;
2662         }
2663
2664         if (use_autokey) {
2665                 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
2666         }
2667
2668         if (ret & OPERATOR_FINISHED) {
2669                 viewops_data_free(C, op);
2670         }
2671
2672         return ret;
2673 }
2674
2675 static int viewdolly_exec(bContext *C, wmOperator *op)
2676 {
2677         View3D *v3d;
2678         RegionView3D *rv3d;
2679         ScrArea *sa;
2680         ARegion *ar;
2681         float mousevec[3];
2682
2683         const int delta = RNA_int_get(op->ptr, "delta");
2684
2685         if (op->customdata) {
2686                 ViewOpsData *vod = op->customdata;
2687
2688                 sa = vod->sa;
2689                 ar = vod->ar;
2690                 copy_v3_v3(mousevec, vod->mousevec);
2691         }
2692         else {
2693                 sa = CTX_wm_area(C);
2694                 ar = CTX_wm_region(C);
2695                 negate_v3_v3(mousevec, ((RegionView3D *)ar->regiondata)->viewinv[2]);
2696                 normalize_v3(mousevec);
2697         }
2698
2699         v3d = sa->spacedata.first;
2700         rv3d = ar->regiondata;
2701
2702         /* overwrite the mouse vector with the view direction (zoom into the center) */
2703         if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
2704                 normalize_v3_v3(mousevec, rv3d->viewinv[2]);
2705         }
2706
2707         if (delta < 0) {
2708                 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 0.2f);
2709         }
2710         else {
2711                 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 1.8f);
2712         }
2713
2714         if (rv3d->viewlock & RV3D_BOXVIEW)
2715                 view3d_boxview_sync(sa, ar);
2716
2717         ED_view3d_depth_tag_update(rv3d);
2718
2719         ED_view3d_camera_lock_sync(v3d, rv3d);
2720
2721         ED_region_tag_redraw(ar);
2722
2723         viewops_data_free(C, op);
2724
2725         return OPERATOR_FINISHED;
2726 }
2727
2728 /* copied from viewzoom_invoke(), changes here may apply there */
2729 static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2730 {
2731         ViewOpsData *vod;
2732
2733         if (view3d_operator_offset_lock_check(C, op))
2734                 return OPERATOR_CANCELLED;
2735
2736         /* makes op->customdata */
2737         viewops_data_alloc(C, op);
2738         vod = op->customdata;
2739
2740         /* poll should check but in some cases fails, see poll func for details */
2741         if (vod->rv3d->viewlock & RV3D_LOCKED) {
2742                 viewops_data_free(C, op);
2743                 return OPERATOR_PASS_THROUGH;
2744         }
2745
2746         /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */
2747         /* switch from camera view when: */
2748         if (vod->rv3d->persp != RV3D_PERSP) {
2749                 if (vod->rv3d->persp == RV3D_CAMOB) {
2750                         /* ignore rv3d->lpersp because dolly only makes sense in perspective mode */
2751                         view3d_persp_switch_from_camera(vod->v3d, vod->rv3d, RV3D_PERSP);
2752                 }
2753                 else {
2754                         vod->rv3d->persp = RV3D_PERSP;
2755                 }
2756                 ED_region_tag_redraw(vod->ar);
2757         }
2758
2759         viewops_data_create(C, op, event);
2760
2761
2762         /* if one or the other zoom position aren't set, set from event */
2763         if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
2764                 RNA_int_set(op->ptr, "mx", event->x);
2765                 RNA_int_set(op->ptr, "my", event->y);
2766         }
2767
2768         if (RNA_struct_property_is_set(op->ptr, "delta")) {
2769                 viewdolly_exec(C, op);
2770         }
2771         else {
2772                 /* overwrite the mouse vector with the view direction (zoom into the center) */
2773                 if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
2774                         negate_v3_v3(vod->mousevec, vod->rv3d->viewinv[2]);
2775                         normalize_v3(vod->mousevec);
2776                 }
2777
2778                 if (event->type == MOUSEZOOM) {
2779                         /* Bypass Zoom invert flag for track pads (pass false always) */
2780
2781                         if (U.uiflag & USER_ZOOM_HORIZ) {
2782                                 vod->origx = vod->oldx = event->x;
2783                                 viewdolly_apply(vod, event->prevx, event->prevy, (U.uiflag & USER_ZOOM_INVERT) == 0);
2784                         }
2785                         else {
2786
2787                                 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
2788                                 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
2789                                 viewdolly_apply(vod, event->prevx, event->prevy, (U.uiflag & USER_ZOOM_INVERT) == 0);
2790                         }
2791                         ED_view3d_depth_tag_update(vod->rv3d);
2792
2793                         viewops_data_free(C, op);
2794                         return OPERATOR_FINISHED;
2795                 }
2796                 else {
2797                         /* add temp handler */
2798                         WM_event_add_modal_handler(C, op);
2799
2800                         return OPERATOR_RUNNING_MODAL;
2801                 }
2802         }
2803         return OPERATOR_FINISHED;
2804 }
2805
2806 static void viewdolly_cancel(bContext *C, wmOperator *op)
2807 {
2808         viewops_data_free(C, op);
2809 }
2810
2811 void VIEW3D_OT_dolly(wmOperatorType *ot)
2812 {
2813         /* identifiers */
2814         ot->name = "Dolly View";
2815         ot->description = "Dolly in/out in the view";
2816         ot->idname = "VIEW3D_OT_dolly";
2817
2818         /* api callbacks */
2819         ot->invoke = viewdolly_invoke;
2820         ot->exec = viewdolly_exec;
2821         ot->modal = viewdolly_modal;
2822         ot->poll = ED_operator_region_view3d_active;
2823         ot->cancel = viewdolly_cancel;
2824
2825         /* flags */
2826         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
2827
2828         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2829         RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
2830         RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
2831 }
2832
2833 static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar,
2834                                const float min[3], const float max[3],
2835                                bool ok_dist, const int smooth_viewtx)
2836 {
2837         RegionView3D *rv3d = ar->regiondata;
2838         float afm[3];
2839         float size;
2840
2841         ED_view3d_smooth_view_force_finish(C, v3d, ar);
2842
2843         /* SMOOTHVIEW */
2844         float new_ofs[3];
2845         float new_dist;
2846
2847         sub_v3_v3v3(afm, max, min);
2848         size = max_fff(afm[0], afm[1], afm[2]);
2849
2850         if (ok_dist) {
2851                 char persp;
2852
2853                 if (rv3d->is_persp) {
2854                         if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) {
2855                                 persp = RV3D_CAMOB;
2856                         }
2857                         else {
2858                                 persp = RV3D_PERSP;
2859                         }
2860                 }
2861                 else { /* ortho */
2862                         if (size < 0.0001f) {
2863                                 /* bounding box was a single point so do not zoom */
2864                                 ok_dist = false;
2865                         }
2866                         else {
2867                                 /* adjust zoom so it looks nicer */
2868                                 persp = RV3D_ORTHO;
2869                         }
2870                 }
2871
2872                 if (ok_dist) {
2873                         new_dist = ED_view3d_radius_to_dist(v3d, ar, persp, true, (size / 2) * VIEW3D_MARGIN);
2874                         if (rv3d->is_persp) {
2875                                 /* don't zoom closer than the near clipping plane */
2876                                 new_dist = max_ff(new_dist, v3d->near * 1.5f);
2877                         }
2878                 }
2879         }
2880
2881         mid_v3_v3v3(new_ofs, min, max);
2882         negate_v3(new_ofs);
2883
2884         if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) {
2885                 rv3d->persp = RV3D_PERSP;
2886                 ED_view3d_smooth_view(
2887                         C, v3d, ar, smooth_viewtx,
2888                         &(const V3D_SmoothParams) {
2889                             .camera_old = v3d->camera, .ofs = new_ofs,
2890                             .dist = ok_dist ? &new_dist : NULL});
2891         }
2892         else {
2893                 ED_view3d_smooth_view(
2894                         C, v3d, ar, smooth_viewtx,
2895                         &(const V3D_SmoothParams) {.ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL});
2896         }
2897
2898         /* smooth view does viewlock RV3D_BOXVIEW copy */
2899 }
2900
2901 /* same as view3d_from_minmax but for all regions (except cameras) */
2902 static void view3d_from_minmax_multi(bContext *C, View3D *v3d,
2903                                      const float min[3], const float max[3],
2904                                      const bool ok_dist, const int smooth_viewtx)
2905 {
2906         ScrArea *sa = CTX_wm_area(C);
2907         ARegion *ar;
2908         for (ar = sa->regionbase.first; ar; ar = ar->next) {
2909                 if (ar->regiontype == RGN_TYPE_WINDOW) {
2910                         RegionView3D *rv3d = ar->regiondata;
2911                         /* when using all regions, don't jump out of camera view,
2912                          * but _do_ allow locked cameras to be moved */
2913                         if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
2914                                 view3d_from_minmax(C, v3d, ar, min, max, ok_dist, smooth_viewtx);
2915                         }
2916                 }
2917         }
2918 }
2919
2920 static int view3d_all_exec(bContext *C, wmOperator *op) /* was view3d_home() in 2.4x */
2921 {
2922         ARegion *ar = CTX_wm_region(C);
2923         View3D *v3d = CTX_wm_view3d(C);
2924         Scene *scene = CTX_data_scene(C);
2925         Base *base;
2926         float *curs;
2927         const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
2928         const bool skip_camera = (ED_view3d_camera_lock_check(v3d, ar->regiondata) ||
2929                                   /* any one of the regions may be locked */
2930                                   (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
2931         const bool center = RNA_boolean_get(op->ptr, "center");
2932         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
2933
2934         float min[3], max[3];
2935         bool changed = false;
2936
2937         if (center) {
2938                 /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
2939                 curs = ED_view3d_cursor3d_get(scene, v3d);
2940                 zero_v3(min);
2941                 zero_v3(max);
2942                 zero_v3(curs);
2943         }
2944         else {
2945                 INIT_MINMAX(min, max);
2946         }
2947
2948         for (base = scene->base.first; base; base = base->next) {
2949                 if (BASE_VISIBLE(v3d, base)) {
2950                         changed = true;
2951
2952                         if (skip_camera && base->object == v3d->camera) {
2953                                 continue;
2954                         }
2955
2956                         BKE_object_minmax(base->object, min, max, false);
2957                 }
2958         }
2959         if (!changed) {
2960                 ED_region_tag_redraw(ar);
2961                 /* TODO - should this be cancel?
2962                  * I think no, because we always move the cursor, with or without
2963                  * object, but in this case there is no change in the scene,
2964                  * only the cursor so I choice a ED_region_tag like
2965                  * view3d_smooth_view do for the center_cursor.
2966                  * See bug #22640
2967                  */
2968                 return OPERATOR_FINISHED;
2969         }
2970
2971         if (use_all_regions) {
2972                 view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx);
2973         }
2974         else {
2975                 view3d_from_minmax(C, v3d, ar, min, max, true, smooth_viewtx);
2976         }
2977
2978         return OPERATOR_FINISHED;
2979 }
2980
2981
2982 void VIEW3D_OT_view_all(wmOperatorType *ot)
2983 {
2984         PropertyRNA *prop;
2985
2986         /* identifiers */
2987         ot->name = "View All";
2988         ot->description = "View all objects in scene";
2989         ot->idname = "VIEW3D_OT_view_all";
2990
2991         /* api callbacks */
2992         ot->exec = view3d_all_exec;
2993         ot->poll = ED_operator_region_view3d_active;
2994
2995         /* flags */
2996         ot->flag = 0;
2997
2998         prop = RNA_def_boolean(ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
2999         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3000         RNA_def_boolean(ot->srna, "center", 0, "Center", "");
3001 }
3002
3003 /* like a localview without local!, was centerview() in 2.4x */
3004 static int viewselected_exec(bContext *C, wmOperator *op)
3005 {
3006         ARegion *ar = CTX_wm_region(C);
3007         View3D *v3d = CTX_wm_view3d(C);
3008         Scene *scene = CTX_data_scene(C);
3009         Object *ob = OBACT;
3010         Object *obedit = CTX_data_edit_object(C);
3011         float min[3], max[3];
3012         bool ok = false, ok_dist = true;
3013         const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
3014         const bool skip_camera = (ED_view3d_camera_lock_check(v3d, ar->regiondata) ||
3015                                   /* any one of the regions may be locked */
3016                                   (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
3017         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
3018
3019         INIT_MINMAX(min, max);
3020
3021         if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) {
3022                 /* hard-coded exception, we look for the one selected armature */
3023                 /* this is weak code this way, we should make a generic active/selection callback interface once... */
3024                 Base *base;
3025                 for (base = scene->base.first; base; base = base->next) {
3026                         if (TESTBASELIB(v3d, base)) {
3027                                 if (base->object->type == OB_ARMATURE)
3028                                         if (base->object->mode & OB_MODE_POSE)
3029                                                 break;
3030                         }
3031                 }
3032                 if (base)
3033                         ob = base->object;
3034         }
3035
3036
3037         if (obedit) {
3038                 ok = ED_view3d_minmax_verts(obedit, min, max);    /* only selected */
3039         }
3040         else if (ob && (ob->mode & OB_MODE_POSE)) {
3041                 ok = BKE_pose_minmax(ob, min, max, true, true);
3042         }
3043         else if (BKE_paint_select_face_test(ob)) {
3044                 ok = paintface_minmax(ob, min, max);
3045         }
3046         else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
3047                 ok = PE_minmax(scene, min, max);
3048         }
3049         else if (ob && (ob->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT))) {
3050                 BKE_paint_stroke_get_average(scene, ob, min);
3051                 copy_v3_v3(max, min);
3052                 ok = true;
3053                 ok_dist = 0; /* don't zoom */
3054         }
3055         else {
3056                 Base *base;
3057                 for (base = FIRSTBASE; base; base = base->next) {
3058                         if (TESTBASE(v3d, base)) {
3059
3060                                 if (skip_camera && base->object == v3d->camera) {
3061                                         continue;
3062                                 }
3063
3064                                 /* account for duplis */
3065                                 if (BKE_object_minmax_dupli(scene, base->object, min, max, false) == 0)
3066                                         BKE_object_minmax(base->object, min, max, false);  /* use if duplis not found */
3067
3068                                 ok = 1;
3069                         }
3070                 }
3071         }
3072
3073         if (ok == 0) {
3074                 return OPERATOR_FINISHED;
3075         }
3076
3077         if (use_all_regions) {
3078                 view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx);
3079         }
3080         else {
3081                 view3d_from_minmax(C, v3d, ar, min, max, ok_dist, smooth_viewtx);
3082         }
3083
3084         return OPERATOR_FINISHED;
3085 }
3086
3087 void VIEW3D_OT_view_selected(wmOperatorType *ot)
3088 {
3089         PropertyRNA *prop;
3090
3091         /* identifiers */
3092         ot->name = "View Selected";
3093         ot->description = "Move the view to the selection center";
3094         ot->idname = "VIEW3D_OT_view_selected";
3095
3096         /* api callbacks */
3097         ot->exec = viewselected_exec;
3098         ot->poll = ED_operator_region_view3d_active;
3099
3100         /* flags */
3101         ot->flag = 0;
3102
3103         /* rna later */
3104         prop = RNA_def_boolean(ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
3105         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3106 }
3107
3108 static int view_lock_clear_exec(bContext *C, wmOperator *UNUSED(op))
3109 {
3110         View3D *v3d = CTX_wm_view3d(C);
3111
3112         if (v3d) {
3113                 ED_view3D_lock_clear(v3d);
3114
3115                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
3116
3117                 return OPERATOR_FINISHED;
3118         }
3119         else {
3120                 return OPERATOR_CANCELLED;
3121         }
3122 }
3123
3124 void VIEW3D_OT_view_lock_clear(wmOperatorType *ot)
3125 {
3126
3127         /* identifiers */
3128         ot->name = "View Lock Clear";
3129         ot->description = "Clear all view locking";
3130         ot->idname = "VIEW3D_OT_view_lock_clear";
3131
3132         /* api callbacks */
3133         ot->exec = view_lock_clear_exec;
3134         ot->poll = ED_operator_region_view3d_active;
3135
3136         /* flags */
3137         ot->flag = 0;
3138 }
3139
3140 static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op))
3141 {
3142         View3D *v3d = CTX_wm_view3d(C);
3143         Object *obact = CTX_data_active_object(C);
3144
3145         if (v3d) {
3146
3147                 ED_view3D_lock_clear(v3d);
3148
3149                 v3d->ob_centre = obact; /* can be NULL */
3150
3151                 if (obact && obact->type == OB_ARMATURE) {
3152                         if (obact->mode & OB_MODE_POSE) {
3153                                 bPoseChannel *pcham_act = BKE_pose_channel_active(obact);
3154                                 if (pcham_act) {
3155                                         BLI_strncpy(v3d->ob_centre_bone, pcham_act->name, sizeof(v3d->ob_centre_bone));
3156                                 }
3157                         }
3158                         else {
3159                                 EditBone *ebone_act = ((bArmature *)obact->data)->act_edbone;
3160                                 if (ebone_act) {
3161                                         BLI_strncpy(v3d->ob_centre_bone, ebone_act->name, sizeof(v3d->ob_centre_bone));
3162                                 }
3163                         }
3164                 }
3165
3166                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
3167
3168                 return OPERATOR_FINISHED;
3169         }
3170         else {
3171                 return OPERATOR_CANCELLED;
3172         }
3173 }
3174
3175 void VIEW3D_OT_view_lock_to_active(wmOperatorType *ot)
3176 {
3177
3178         /* identifiers */
3179         ot->name = "View Lock to Active";
3180         ot->description = "Lock the view to the active object/bone";
3181         ot->idname = "VIEW3D_OT_view_lock_to_active";
3182
3183         /* api callbacks */
3184         ot->exec = view_lock_to_active_exec;
3185         ot->poll = ED_operator_region_view3d_active;
3186
3187         /* flags */
3188         ot->flag = 0;
3189 }
3190
3191 static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
3192 {
3193         View3D *v3d = CTX_wm_view3d(C);
3194         RegionView3D *rv3d = CTX_wm_region_view3d(C);
3195         Scene *scene = CTX_data_scene(C);
3196         
3197         if (rv3d) {
3198                 ARegion *ar = CTX_wm_region(C);
3199                 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
3200
3201                 ED_view3d_smooth_view_force_finish(C, v3d, ar);
3202