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