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