2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/space_view3d/view3d_edit.c
37 #include "DNA_armature_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_camera_types.h"
41 #include "DNA_lamp_types.h"
43 #include "MEM_guardedalloc.h"
45 #include "BLI_blenlib.h"
48 #include "BLI_utildefines.h"
50 #include "BKE_camera.h"
51 #include "BKE_context.h"
52 #include "BKE_image.h"
53 #include "BKE_library.h"
54 #include "BKE_object.h"
55 #include "BKE_paint.h"
56 #include "BKE_report.h"
57 #include "BKE_scene.h"
58 #include "BKE_screen.h"
59 #include "BKE_action.h"
60 #include "BKE_armature.h"
61 #include "BKE_depsgraph.h" /* for ED_view3d_camera_lock_sync */
65 #include "BIF_glutil.h"
70 #include "RNA_access.h"
71 #include "RNA_define.h"
73 #include "ED_armature.h"
74 #include "ED_particle.h"
75 #include "ED_screen.h"
76 #include "ED_transform.h"
78 #include "ED_view3d.h"
79 #include "ED_sculpt.h"
82 #include "PIL_time.h" /* smoothview */
84 #include "view3d_intern.h" /* own include */
86 /* ********************** view3d_edit: view manipulations ********************* */
88 int ED_view3d_camera_lock_check(View3D *v3d, RegionView3D *rv3d)
90 return ((v3d->camera) &&
91 (v3d->camera->id.lib == NULL) &&
92 (v3d->flag2 & V3D_LOCK_CAMERA) &&
93 (rv3d->persp == RV3D_CAMOB));
96 void ED_view3d_camera_lock_init(View3D *v3d, RegionView3D *rv3d)
98 if (ED_view3d_camera_lock_check(v3d, rv3d)) {
99 /* using a fallback dist is OK here since ED_view3d_from_object() compensates for it */
100 rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
101 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
105 /* return TRUE if the camera is moved */
106 int ED_view3d_camera_lock_sync(View3D *v3d, RegionView3D *rv3d)
108 if (ED_view3d_camera_lock_check(v3d, rv3d)) {
109 ObjectTfmProtectedChannels obtfm;
112 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
114 float view_mat[4][4];
115 float diff_mat[4][4];
116 float parent_mat[4][4];
118 while (root_parent->parent) {
119 root_parent = root_parent->parent;
122 ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
124 invert_m4_m4(v3d->camera->imat, v3d->camera->obmat);
125 mult_m4_m4m4(diff_mat, view_mat, v3d->camera->imat);
127 mult_m4_m4m4(parent_mat, diff_mat, root_parent->obmat);
129 BKE_object_tfm_protected_backup(root_parent, &obtfm);
130 BKE_object_apply_mat4(root_parent, parent_mat, TRUE, FALSE);
131 BKE_object_tfm_protected_restore(root_parent, &obtfm, root_parent->protectflag);
133 ob_update = v3d->camera;
135 DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
136 WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, ob_update);
137 ob_update = ob_update->parent;
141 BKE_object_tfm_protected_backup(v3d->camera, &obtfm);
142 ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
143 BKE_object_tfm_protected_restore(v3d->camera, &obtfm, v3d->camera->protectflag);
145 DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
146 WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, v3d->camera);
157 /* ********************* box view support ***************** */
159 static void view3d_boxview_clip(ScrArea *sa)
162 BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb");
164 float x1 = 0.0f, y1 = 0.0f, z1 = 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f};
167 /* create bounding box */
168 for (ar = sa->regionbase.first; ar; ar = ar->next) {
169 if (ar->regiontype == RGN_TYPE_WINDOW) {
170 RegionView3D *rv3d = ar->regiondata;
172 if (rv3d->viewlock & RV3D_BOXCLIP) {
173 if (ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
174 if (ar->winx > ar->winy) x1 = rv3d->dist;
175 else x1 = ar->winx * rv3d->dist / ar->winy;
177 if (ar->winx > ar->winy) y1 = ar->winy * rv3d->dist / ar->winx;
178 else y1 = rv3d->dist;
179 copy_v2_v2(ofs, rv3d->ofs);
181 else if (ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
182 ofs[2] = rv3d->ofs[2];
184 if (ar->winx > ar->winy) z1 = ar->winy * rv3d->dist / ar->winx;
185 else z1 = rv3d->dist;
191 for (val = 0; val < 8; val++) {
192 if (ELEM4(val, 0, 3, 4, 7))
193 bb->vec[val][0] = -x1 - ofs[0];
195 bb->vec[val][0] = x1 - ofs[0];
197 if (ELEM4(val, 0, 1, 4, 5))
198 bb->vec[val][1] = -y1 - ofs[1];
200 bb->vec[val][1] = y1 - ofs[1];
203 bb->vec[val][2] = -z1 - ofs[2];
205 bb->vec[val][2] = z1 - ofs[2];
208 /* normals for plane equations */
209 normal_tri_v3(clip[0], bb->vec[0], bb->vec[1], bb->vec[4]);
210 normal_tri_v3(clip[1], bb->vec[1], bb->vec[2], bb->vec[5]);
211 normal_tri_v3(clip[2], bb->vec[2], bb->vec[3], bb->vec[6]);
212 normal_tri_v3(clip[3], bb->vec[3], bb->vec[0], bb->vec[7]);
213 normal_tri_v3(clip[4], bb->vec[4], bb->vec[5], bb->vec[6]);
214 normal_tri_v3(clip[5], bb->vec[0], bb->vec[2], bb->vec[1]);
216 /* then plane equations */
217 for (val = 0; val < 6; val++) {
218 clip[val][3] = -dot_v3v3(clip[val], bb->vec[val % 5]);
221 /* create bounding box */
222 for (ar = sa->regionbase.first; ar; ar = ar->next) {
223 if (ar->regiontype == RGN_TYPE_WINDOW) {
224 RegionView3D *rv3d = ar->regiondata;
226 if (rv3d->viewlock & RV3D_BOXCLIP) {
227 rv3d->rflag |= RV3D_CLIPPING;
228 memcpy(rv3d->clip, clip, sizeof(clip));
229 if (rv3d->clipbb) MEM_freeN(rv3d->clipbb);
230 rv3d->clipbb = MEM_dupallocN(bb);
237 /* sync center/zoom view of region to others, for view transforms */
238 static void view3d_boxview_sync(ScrArea *sa, ARegion *ar)
241 RegionView3D *rv3d = ar->regiondata;
244 for (artest = sa->regionbase.first; artest; artest = artest->next) {
245 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
246 RegionView3D *rv3dtest = artest->regiondata;
248 if (rv3dtest->viewlock) {
249 rv3dtest->dist = rv3d->dist;
251 if (ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM) ) {
252 if (ELEM(rv3dtest->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK))
253 rv3dtest->ofs[0] = rv3d->ofs[0];
254 else if (ELEM(rv3dtest->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT))
255 rv3dtest->ofs[1] = rv3d->ofs[1];
257 else if (ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK) ) {
258 if (ELEM(rv3dtest->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM))
259 rv3dtest->ofs[0] = rv3d->ofs[0];
260 else if (ELEM(rv3dtest->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT))
261 rv3dtest->ofs[2] = rv3d->ofs[2];
263 else if (ELEM(rv3d->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT) ) {
264 if (ELEM(rv3dtest->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM))
265 rv3dtest->ofs[1] = rv3d->ofs[1];
266 if (ELEM(rv3dtest->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK))
267 rv3dtest->ofs[2] = rv3d->ofs[2];
270 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
272 ED_region_tag_redraw(artest);
278 view3d_boxview_clip(sa);
282 /* for home, center etc */
283 void view3d_boxview_copy(ScrArea *sa, ARegion *ar)
286 RegionView3D *rv3d = ar->regiondata;
289 for (artest = sa->regionbase.first; artest; artest = artest->next) {
290 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
291 RegionView3D *rv3dtest = artest->regiondata;
293 if (rv3dtest->viewlock) {
294 rv3dtest->dist = rv3d->dist;
295 copy_v3_v3(rv3dtest->ofs, rv3d->ofs);
296 ED_region_tag_redraw(artest);
298 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
304 view3d_boxview_clip(sa);
308 /* 'clip' is used to know if our clip setting has changed */
309 void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, short do_clip)
311 ARegion *ar_sync = NULL;
312 RegionView3D *rv3d = ar->regiondata;
314 /* this function copies flags from the first of the 3 other quadview
315 * regions to the 2 other, so it assumes this is the region whose
316 * properties are always being edited, weak */
317 viewlock = rv3d->viewlock;
319 if ((viewlock & RV3D_LOCKED) == 0)
321 else if ((viewlock & RV3D_BOXVIEW) == 0) {
322 viewlock &= ~RV3D_BOXCLIP;
326 for (; ar; ar = ar->prev) {
327 if (ar->alignment == RGN_ALIGN_QSPLIT) {
328 rv3d = ar->regiondata;
329 rv3d->viewlock = viewlock;
331 if (do_clip && (viewlock & RV3D_BOXCLIP) == 0) {
332 rv3d->rflag &= ~RV3D_BOXCLIP;
335 /* use ar_sync so we sync with one of the aligned views below
336 * else the view jumps on changing view settings like 'clip'
337 * since it copies from the perspective view */
342 if (rv3d->viewlock & RV3D_BOXVIEW) {
343 view3d_boxview_copy(sa, ar_sync ? ar_sync : sa->regionbase.last);
346 ED_area_tag_redraw(sa);
349 /* ************************** init for view ops **********************************/
351 typedef struct ViewOpsData {
357 /* needed for continuous zoom */
359 double timer_lastdraw;
362 float viewquat[4]; /* working copy of rv3d->viewquat */
364 float mousevec[3]; /* dolly only */
365 float reverse, dist0, camzoom0;
367 short axis_snap; /* view rotate only */
369 /* use for orbit selection and auto-dist */
370 float ofs[3], dyn_ofs[3];
373 int origx, origy, oldx, oldy;
374 int origkey; /* the key that triggered the operator */
378 #define TRACKBALLSIZE (1.1)
380 static void calctrackballvec(const rcti *rect, int mx, int my, float vec[3])
382 float x, y, radius, d, z, t;
384 radius = TRACKBALLSIZE;
386 /* normalize x and y */
387 x = BLI_rcti_cent_x(rect) - mx;
388 x /= (float)(BLI_rcti_size_x(rect) / 4);
389 y = BLI_rcti_cent_y(rect) - my;
390 y /= (float)(BLI_rcti_size_y(rect) / 2);
392 d = sqrt(x * x + y * y);
393 if (d < radius * (float)M_SQRT1_2) { /* Inside sphere */
394 z = sqrt(radius * radius - d * d);
396 else { /* On hyperbola */
397 t = radius / (float)M_SQRT2;
403 vec[2] = -z; /* yah yah! */
407 static void viewops_data_create(bContext *C, wmOperator *op, wmEvent *event)
409 static float lastofs[3] = {0, 0, 0};
411 ViewOpsData *vod = MEM_callocN(sizeof(ViewOpsData), "viewops data");
414 op->customdata = vod;
415 vod->sa = CTX_wm_area(C);
416 vod->ar = CTX_wm_region(C);
417 vod->v3d = vod->sa->spacedata.first;
418 vod->rv3d = rv3d = vod->ar->regiondata;
420 /* set the view from the camera, if view locking is enabled.
421 * we may want to make this optional but for now its needed always */
422 ED_view3d_camera_lock_init(vod->v3d, vod->rv3d);
424 vod->dist0 = rv3d->dist;
425 vod->camzoom0 = rv3d->camzoom;
426 copy_qt_qt(vod->viewquat, rv3d->viewquat);
427 copy_qt_qt(vod->oldquat, rv3d->viewquat);
428 vod->origx = vod->oldx = event->x;
429 vod->origy = vod->oldy = event->y;
430 vod->origkey = event->type; /* the key that triggered the operator. */
431 vod->use_dyn_ofs = (U.uiflag & USER_ORBIT_SELECTION) ? 1 : 0;
432 copy_v3_v3(vod->ofs, rv3d->ofs);
434 if (vod->use_dyn_ofs) {
435 Scene *scene = CTX_data_scene(C);
438 if (ob && ob->mode & OB_MODE_ALL_PAINT) {
439 /* transformation is disabled for painting modes, which will make it
440 * so previous offset is used. This is annoying when you open file
441 * saved with active object in painting mode
443 copy_v3_v3(lastofs, ob->obmat[3]);
446 /* If there's no selection, lastofs is unmodified and last value since static */
447 calculateTransformCenter(C, V3D_CENTROID, lastofs, NULL);
450 negate_v3_v3(vod->dyn_ofs, lastofs);
452 else if (U.uiflag & USER_ZBUF_ORBIT) {
454 view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
456 if ((vod->use_dyn_ofs = ED_view3d_autodist(CTX_data_scene(C), vod->ar, vod->v3d, event->mval, vod->dyn_ofs))) {
457 if (rv3d->is_persp) {
458 float my_origin[3]; /* original G.vd->ofs */
459 float my_pivot[3]; /* view */
462 /* locals for dist correction */
466 negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
468 /* Set the dist value to be the distance from this 3d point
469 * this means youll always be able to zoom into it and panning wont go bad when dist was zero */
471 /* remove dist value */
472 upvec[0] = upvec[1] = 0;
473 upvec[2] = rv3d->dist;
474 copy_m3_m4(mat, rv3d->viewinv);
476 mul_m3_v3(mat, upvec);
477 sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
478 negate_v3(my_pivot); /* ofs is flipped */
480 /* find a new ofs value that is along the view axis (rather than the mouse location) */
481 closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
482 vod->dist0 = rv3d->dist = len_v3v3(my_pivot, dvec);
484 negate_v3_v3(rv3d->ofs, dvec);
486 negate_v3(vod->dyn_ofs);
487 copy_v3_v3(vod->ofs, rv3d->ofs);
493 const float mval_f[2] = {(float)event->mval[0],
494 (float)event->mval[1]};
495 ED_view3d_win_to_vector(vod->ar, mval_f, vod->mousevec);
498 /* lookup, we don't pass on v3d to prevent confusement */
499 vod->grid = vod->v3d->grid;
500 vod->far = vod->v3d->far;
502 calctrackballvec(&vod->ar->winrct, event->x, event->y, vod->trackvec);
504 initgrabz(rv3d, -rv3d->ofs[0], -rv3d->ofs[1], -rv3d->ofs[2]);
507 if (rv3d->persmat[2][1] < 0.0f)
508 vod->reverse = -1.0f;
510 rv3d->rflag |= RV3D_NAVIGATING;
513 static void viewops_data_free(bContext *C, wmOperator *op)
516 Paint *p = paint_get_active_from_context(C);
518 if (op->customdata) {
519 ViewOpsData *vod = op->customdata;
521 vod->rv3d->rflag &= ~RV3D_NAVIGATING;
524 WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
527 op->customdata = NULL;
530 ar = CTX_wm_region(C);
533 if (p && (p->flags & PAINT_FAST_NAVIGATE))
534 ED_region_tag_redraw(ar);
537 /* ************************** viewrotate **********************************/
539 #define COS45 0.7071068
542 #define NUM_SNAP_QUATS 39
544 static const float snapquats[NUM_SNAP_QUATS][5] = {
545 /*{q0, q1, q3, q4, view}*/
546 {COS45, -SIN45, 0.0, 0.0, RV3D_VIEW_FRONT},
547 {0.0, 0.0, -SIN45, -SIN45, RV3D_VIEW_BACK},
548 {1.0, 0.0, 0.0, 0.0, RV3D_VIEW_TOP},
549 {0.0, -1.0, 0.0, 0.0, RV3D_VIEW_BOTTOM},
550 {0.5, -0.5, -0.5, -0.5, RV3D_VIEW_RIGHT},
551 {0.5, -0.5, 0.5, 0.5, RV3D_VIEW_LEFT},
553 /* some more 45 deg snaps */
554 { 0.6532815, -0.6532815, 0.2705981, 0.2705981, 0},
555 { 0.9238795, 0.0, 0.0, 0.3826834, 0},
556 { 0.0, -0.9238795, 0.3826834, 0.0, 0},
557 { 0.3535534, -0.8535534, 0.3535534, 0.1464466, 0},
558 { 0.8535534, -0.3535534, 0.1464466, 0.3535534, 0},
559 { 0.4999999, -0.4999999, 0.5, 0.5, 0},
560 { 0.2705980, -0.6532815, 0.6532815, 0.2705980, 0},
561 { 0.6532815, -0.2705980, 0.2705980, 0.6532815, 0},
562 { 0.2705978, -0.2705980, 0.6532814, 0.6532814, 0},
563 { 0.3826834, 0.0, 0.0, 0.9238794, 0},
564 { 0.0, -0.3826834, 0.9238794, 0.0, 0},
565 { 0.1464466, -0.3535534, 0.8535534, 0.3535534, 0},
566 { 0.3535534, -0.1464466, 0.3535534, 0.8535534, 0},
567 { 0.0, 0.0, 0.9238794, 0.3826834, 0},
568 {-0.0, 0.0, 0.3826834, 0.9238794, 0},
569 {-0.2705980, 0.2705980, 0.6532813, 0.6532813, 0},
570 {-0.3826834, 0.0, 0.0, 0.9238794, 0},
571 { 0.0, 0.3826834, 0.9238794, 0.0, 0},
572 {-0.1464466, 0.3535534, 0.8535533, 0.3535533, 0},
573 {-0.3535534, 0.1464466, 0.3535533, 0.8535533, 0},
574 {-0.4999999, 0.4999999, 0.4999999, 0.4999999, 0},
575 {-0.2705980, 0.6532815, 0.6532814, 0.2705980, 0},
576 {-0.6532815, 0.2705980, 0.2705980, 0.6532814, 0},
577 {-0.6532813, 0.6532813, 0.2705979, 0.2705979, 0},
578 {-0.9238793, 0.0, 0.0, 0.3826833, 0},
579 { 0.0, 0.9238793, 0.3826833, 0.0, 0},
580 {-0.3535533, 0.8535533, 0.3535533, 0.1464466, 0},
581 {-0.8535533, 0.3535533, 0.1464466, 0.3535533, 0},
582 {-0.3826833, 0.9238794, 0.0, 0.0, 0},
583 {-0.9238794, 0.3826833, 0.0, 0.0, 0},
584 {-COS45, 0.0, 0.0, SIN45, 0},
585 { COS45, 0.0, 0.0, SIN45, 0},
586 { 0.0, 0.0, 0.0, 1.0, 0}
595 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
596 #define VIEW_MODAL_CONFIRM 1 /* used for all view operations */
597 #define VIEWROT_MODAL_AXIS_SNAP_ENABLE 2
598 #define VIEWROT_MODAL_AXIS_SNAP_DISABLE 3
599 #define VIEWROT_MODAL_SWITCH_ZOOM 4
600 #define VIEWROT_MODAL_SWITCH_MOVE 5
601 #define VIEWROT_MODAL_SWITCH_ROTATE 6
603 /* called in transform_ops.c, on each regeneration of keymaps */
604 void viewrotate_modal_keymap(wmKeyConfig *keyconf)
606 static EnumPropertyItem modal_items[] = {
607 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
609 {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""},
610 {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""},
612 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
613 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
615 {0, NULL, 0, NULL, NULL}
618 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Rotate Modal");
620 /* this function is called for each spacetype, only needs to add map once */
621 if (keymap && keymap->modal_items) return;
623 keymap = WM_modalkeymap_add(keyconf, "View3D Rotate Modal", modal_items);
625 /* items for modal map */
626 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
627 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
629 WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_ENABLE);
630 WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_DISABLE);
632 /* disabled mode switching for now, can re-implement better, later on */
634 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
635 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
636 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
639 /* assign map to operators */
640 WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
644 static void viewrotate_apply(ViewOpsData *vod, int x, int y)
646 RegionView3D *rv3d = vod->rv3d;
648 rv3d->view = RV3D_VIEW_USER; /* need to reset everytime because of view snapping */
650 if (U.flag & USER_TRACKBALL) {
651 float phi, si, q1[4], dvec[3], newvec[3];
653 calctrackballvec(&vod->ar->winrct, x, y, newvec);
655 sub_v3_v3v3(dvec, newvec, vod->trackvec);
658 si /= (float)(2.0 * TRACKBALLSIZE);
660 cross_v3_v3v3(q1 + 1, vod->trackvec, newvec);
661 normalize_v3(q1 + 1);
663 /* Allow for rotation beyond the interval [-pi, pi] */
667 /* This relation is used instead of
668 * - phi = asin(si) so that the angle
669 * - of rotation is linearly proportional
670 * - to the distance that the mouse is
672 phi = si * (float)(M_PI / 2.0);
675 mul_v3_fl(q1 + 1, sin(phi));
676 mul_qt_qtqt(vod->viewquat, q1, vod->oldquat);
678 if (vod->use_dyn_ofs) {
679 /* compute the post multiplication quat, to rotate the offset correctly */
680 conjugate_qt_qt(q1, vod->oldquat);
681 mul_qt_qtqt(q1, q1, vod->viewquat);
683 conjugate_qt(q1); /* conj == inv for unit quat */
684 copy_v3_v3(rv3d->ofs, vod->ofs);
685 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
686 mul_qt_v3(q1, rv3d->ofs);
687 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
691 /* New turntable view code by John Aughey */
695 const float zvec_global[3] = {0.0f, 0.0f, 1.0f};
698 /* Sensitivity will control how fast the viewport rotates. 0.007 was
699 * obtained experimentally by looking at viewport rotation sensitivities
700 * on other modeling programs. */
701 /* Perhaps this should be a configurable user parameter. */
702 const float sensitivity = 0.007f;
704 /* Get the 3x3 matrix and its inverse from the quaternion */
705 quat_to_mat3(m, vod->viewquat);
706 invert_m3_m3(m_inv, m);
708 /* avoid gimble lock */
710 if (len_squared_v3v3(zvec_global, m_inv[2]) > 0.001f) {
712 cross_v3_v3v3(xaxis, zvec_global, m_inv[2]);
713 if (dot_v3v3(xaxis, m_inv[0]) < 0) {
716 fac = angle_normalized_v3v3(zvec_global, m_inv[2]) / (float)M_PI;
717 fac = fabsf(fac - 0.5f) * 2;
719 interp_v3_v3v3(xaxis, xaxis, m_inv[0], fac);
722 copy_v3_v3(xaxis, m_inv[0]);
725 copy_v3_v3(xaxis, m_inv[0]);
728 /* Determine the direction of the x vector (for rotating up and down) */
729 /* This can likely be computed directly from the quaternion. */
731 /* Perform the up/down rotation */
732 axis_angle_to_quat(q1, xaxis, sensitivity * -(y - vod->oldy));
733 mul_qt_qtqt(vod->viewquat, vod->viewquat, q1);
735 if (vod->use_dyn_ofs) {
736 conjugate_qt(q1); /* conj == inv for unit quat */
737 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
738 mul_qt_v3(q1, rv3d->ofs);
739 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
742 /* Perform the orbital rotation */
743 axis_angle_to_quat(q1, zvec_global, sensitivity * vod->reverse * (x - vod->oldx));
744 mul_qt_qtqt(vod->viewquat, vod->viewquat, q1);
746 if (vod->use_dyn_ofs) {
748 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
749 mul_qt_v3(q1, rv3d->ofs);
750 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
754 /* check for view snap */
755 if (vod->axis_snap) {
757 float viewquat_inv[4];
758 float zaxis[3] = {0, 0, 1};
759 invert_qt_qt(viewquat_inv, vod->viewquat);
761 mul_qt_v3(viewquat_inv, zaxis);
763 for (i = 0; i < NUM_SNAP_QUATS; i++) {
765 float view = (int)snapquats[i][4];
766 float viewquat_inv_test[4];
767 float zaxis_test[3] = {0, 0, 1};
769 invert_qt_qt(viewquat_inv_test, snapquats[i]);
770 mul_qt_v3(viewquat_inv_test, zaxis_test);
772 if (angle_v3v3(zaxis_test, zaxis) < DEG2RADF(45 / 3)) {
773 /* find the best roll */
774 float quat_roll[4], quat_final[4], quat_best[4];
775 float viewquat_align[4]; /* viewquat aligned to zaxis_test */
776 float viewquat_align_inv[4]; /* viewquat aligned to zaxis_test */
777 float best_angle = FLT_MAX;
780 /* viewquat_align is the original viewquat aligned to the snapped axis
781 * for testing roll */
782 rotation_between_vecs_to_quat(viewquat_align, zaxis_test, zaxis);
783 normalize_qt(viewquat_align);
784 mul_qt_qtqt(viewquat_align, vod->viewquat, viewquat_align);
785 normalize_qt(viewquat_align);
786 invert_qt_qt(viewquat_align_inv, viewquat_align);
789 for (j = 0; j < 8; j++) {
791 float xaxis1[3] = {1, 0, 0};
792 float xaxis2[3] = {1, 0, 0};
793 float quat_final_inv[4];
795 axis_angle_to_quat(quat_roll, zaxis_test, (float)j * DEG2RADF(45.0f));
796 normalize_qt(quat_roll);
798 mul_qt_qtqt(quat_final, snapquats[i], quat_roll);
799 normalize_qt(quat_final);
801 /* compare 2 vector angles to find the least roll */
802 invert_qt_qt(quat_final_inv, quat_final);
803 mul_qt_v3(viewquat_align_inv, xaxis1);
804 mul_qt_v3(quat_final_inv, xaxis2);
805 angle = angle_v3v3(xaxis1, xaxis2);
807 if (angle <= best_angle) {
809 copy_qt_qt(quat_best, quat_final);
810 if (j) view = 0; /* view grid assumes certain up axis */
814 copy_qt_qt(vod->viewquat, quat_best);
815 rv3d->view = view; /* if we snap to a rolled camera the grid is invalid */
824 /* avoid precision loss over time */
825 normalize_qt(vod->viewquat);
827 /* use a working copy so view rotation locking doesnt overwrite the locked
828 * rotation back into the view we calculate with */
829 copy_qt_qt(rv3d->viewquat, vod->viewquat);
831 ED_view3d_camera_lock_sync(vod->v3d, rv3d);
833 ED_region_tag_redraw(vod->ar);
836 static int viewrotate_modal(bContext *C, wmOperator *op, wmEvent *event)
838 ViewOpsData *vod = op->customdata;
839 short event_code = VIEW_PASS;
841 /* execute the events */
842 if (event->type == MOUSEMOVE) {
843 event_code = VIEW_APPLY;
845 else if (event->type == EVT_MODAL_MAP) {
846 switch (event->val) {
847 case VIEW_MODAL_CONFIRM:
848 event_code = VIEW_CONFIRM;
850 case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
851 vod->axis_snap = TRUE;
852 event_code = VIEW_APPLY;
854 case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
855 vod->axis_snap = FALSE;
856 event_code = VIEW_APPLY;
858 case VIEWROT_MODAL_SWITCH_ZOOM:
859 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
860 event_code = VIEW_CONFIRM;
862 case VIEWROT_MODAL_SWITCH_MOVE:
863 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
864 event_code = VIEW_CONFIRM;
868 else if (event->type == vod->origkey && event->val == KM_RELEASE) {
869 event_code = VIEW_CONFIRM;
872 if (event_code == VIEW_APPLY) {
873 viewrotate_apply(vod, event->x, event->y);
875 else if (event_code == VIEW_CONFIRM) {
876 ED_view3d_depth_tag_update(vod->rv3d);
877 viewops_data_free(C, op);
879 return OPERATOR_FINISHED;
882 return OPERATOR_RUNNING_MODAL;
885 static int viewrotate_invoke(bContext *C, wmOperator *op, wmEvent *event)
890 /* makes op->customdata */
891 viewops_data_create(C, op, event);
892 vod = op->customdata;
895 if (rv3d->viewlock) { /* poll should check but in some cases fails, see poll func for details */
896 viewops_data_free(C, op);
897 return OPERATOR_PASS_THROUGH;
900 /* switch from camera view when: */
901 if (rv3d->persp != RV3D_PERSP) {
903 if (U.uiflag & USER_AUTOPERSP) {
904 if (!ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
905 rv3d->persp = RV3D_PERSP;
908 else if (rv3d->persp == RV3D_CAMOB) {
910 /* changed since 2.4x, use the camera view */
911 if (vod->v3d->camera) {
912 rv3d->dist = ED_view3d_offset_distance(vod->v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
913 ED_view3d_from_object(vod->v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
916 if (!ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
917 rv3d->persp = rv3d->lpersp;
920 ED_region_tag_redraw(vod->ar);
923 if (event->type == MOUSEPAN) {
924 /* Rotate direction we keep always same */
925 if (U.uiflag2 & USER_TRACKPAD_NATURAL)
926 viewrotate_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy);
928 viewrotate_apply(vod, event->prevx, event->prevy);
930 ED_view3d_depth_tag_update(rv3d);
932 viewops_data_free(C, op);
934 return OPERATOR_FINISHED;
936 else if (event->type == MOUSEROTATE) {
937 /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
938 viewrotate_apply(vod, event->prevx, event->y);
939 ED_view3d_depth_tag_update(rv3d);
941 viewops_data_free(C, op);
943 return OPERATOR_FINISHED;
946 /* add temp handler */
947 WM_event_add_modal_handler(C, op);
949 return OPERATOR_RUNNING_MODAL;
953 /* test for unlocked camera view in quad view */
954 static int view3d_camera_user_poll(bContext *C)
959 if (ED_view3d_context_user_region(C, &v3d, &ar)) {
960 RegionView3D *rv3d = ar->regiondata;
961 if (rv3d->persp == RV3D_CAMOB) {
969 static int viewrotate_cancel(bContext *C, wmOperator *op)
971 viewops_data_free(C, op);
973 return OPERATOR_CANCELLED;
976 void VIEW3D_OT_rotate(wmOperatorType *ot)
979 ot->name = "Rotate View";
980 ot->description = "Rotate the view";
981 ot->idname = "VIEW3D_OT_rotate";
984 ot->invoke = viewrotate_invoke;
985 ot->modal = viewrotate_modal;
986 ot->poll = ED_operator_region_view3d_active;
987 ot->cancel = viewrotate_cancel;
990 ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
993 /* NDOF utility functions
994 * (should these functions live in this file?)
996 float ndof_to_axis_angle(struct wmNDOFMotionData *ndof, float axis[3])
998 return ndof->dt * normalize_v3_v3(axis, ndof->rvec);
1001 void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4])
1006 angle = ndof_to_axis_angle(ndof, axis);
1007 axis_angle_to_quat(q, axis, angle);
1010 /* -- "orbit" navigation (trackball/turntable)
1012 * -- panning in rotationally-locked views
1014 static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
1017 if (event->type != NDOF_MOTION)
1018 return OPERATOR_CANCELLED;
1020 View3D *v3d = CTX_wm_view3d(C);
1022 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1023 wmNDOFMotionData *ndof = (wmNDOFMotionData *) event->customdata;
1025 ED_view3d_camera_lock_init(v3d, rv3d);
1027 viewops_data_create(C, op, event);
1028 vod = op->customdata;
1030 rv3d->rot_angle = 0.f; /* off by default, until changed later this function */
1032 if (ndof->progress != P_FINISHING) {
1033 const float dt = ndof->dt;
1035 /* tune these until everything feels right */
1036 const float rot_sensitivity = 1.f;
1038 const float zoom_sensitivity = 1.f;
1040 const float pan_sensitivity = 1.f;
1041 const int has_rotation = rv3d->viewlock != RV3D_LOCKED && !is_zero_v3(ndof->rvec);
1044 invert_qt_qt(view_inv, rv3d->viewquat);
1046 /* #define DEBUG_NDOF_MOTION */
1047 #ifdef DEBUG_NDOF_MOTION
1048 printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f delivered to 3D view\n",
1049 ndof->tx, ndof->ty, ndof->tz, ndof->rx, ndof->ry, ndof->rz, ndof->dt);
1052 if (rv3d->viewlock == RV3D_LOCKED) {
1053 /* rotation not allowed -- explore panning options instead */
1054 float pan_vec[3] = {ndof->tx, ndof->ty, 0.0f};
1055 mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
1057 /* transform motion from view to world coordinates */
1058 invert_qt_qt(view_inv, rv3d->viewquat);
1059 mul_qt_v3(view_inv, pan_vec);
1061 /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1062 sub_v3_v3(rv3d->ofs, pan_vec);
1067 rv3d->view = RV3D_VIEW_USER;
1069 if (U.ndof_flag & NDOF_TURNTABLE) {
1071 /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
1072 float angle, rot[4];
1073 float xvec[3] = {1, 0, 0};
1075 /* Determine the direction of the x vector (for rotating up and down) */
1076 mul_qt_v3(view_inv, xvec);
1078 /* Perform the up/down rotation */
1079 angle = rot_sensitivity * dt * ndof->rx;
1080 if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
1082 rot[0] = cos(angle);
1083 mul_v3_v3fl(rot + 1, xvec, sin(angle));
1084 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1086 /* Perform the orbital rotation */
1087 angle = rot_sensitivity * dt * ndof->ry;
1088 if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
1091 /* update the onscreen doo-dad */
1092 rv3d->rot_angle = angle;
1093 rv3d->rot_axis[0] = 0;
1094 rv3d->rot_axis[1] = 0;
1095 rv3d->rot_axis[2] = 1;
1097 rot[0] = cos(angle);
1098 rot[1] = rot[2] = 0.0;
1099 rot[3] = sin(angle);
1100 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1106 float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
1108 if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS)
1111 if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
1114 if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
1118 /* transform rotation axis from view to world coordinates */
1119 mul_qt_v3(view_inv, axis);
1121 /* update the onscreen doo-dad */
1122 rv3d->rot_angle = angle;
1123 copy_v3_v3(rv3d->rot_axis, axis);
1125 axis_angle_to_quat(rot, axis, angle);
1127 /* apply rotation */
1128 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1132 /* rotate around custom center */
1133 if (vod && vod->use_dyn_ofs) {
1136 /* compute the post multiplication quat, to rotate the offset correctly */
1137 conjugate_qt_qt(q1, vod->oldquat);
1138 mul_qt_qtqt(q1, q1, rv3d->viewquat);
1140 conjugate_qt(q1); /* conj == inv for unit quat */
1141 copy_v3_v3(rv3d->ofs, vod->ofs);
1142 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
1143 mul_qt_v3(q1, rv3d->ofs);
1144 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
1149 viewops_data_free(C, op);
1151 ED_view3d_camera_lock_sync(v3d, rv3d);
1153 ED_region_tag_redraw(CTX_wm_region(C));
1155 return OPERATOR_FINISHED;
1159 void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
1162 ot->name = "NDOF Orbit View";
1163 ot->description = "Explore every angle of an object using the 3D mouse";
1164 ot->idname = "VIEW3D_OT_ndof_orbit";
1167 ot->invoke = ndof_orbit_invoke;
1168 ot->poll = ED_operator_view3d_active;
1175 static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
1178 if (event->type != NDOF_MOTION)
1179 return OPERATOR_CANCELLED;
1182 View3D *v3d = CTX_wm_view3d(C);
1183 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1184 wmNDOFMotionData *ndof = (wmNDOFMotionData *) event->customdata;
1186 ED_view3d_camera_lock_init(v3d, rv3d);
1188 rv3d->rot_angle = 0.f; /* off by default, until changed later this function */
1190 viewops_data_create(C, op, event);
1191 vod = op->customdata;
1193 if (ndof->progress != P_FINISHING) {
1194 const float dt = ndof->dt;
1196 /* tune these until everything feels right */
1197 const float rot_sensitivity = 1.f;
1199 const float zoom_sensitivity = 1.f;
1201 const float pan_sensitivity = 1.f;
1202 const int has_rotation = rv3d->viewlock != RV3D_LOCKED && !is_zero_v3(ndof->rvec);
1205 invert_qt_qt(view_inv, rv3d->viewquat);
1207 /* #define DEBUG_NDOF_MOTION */
1208 #ifdef DEBUG_NDOF_MOTION
1209 printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f delivered to 3D view\n",
1210 ndof->tx, ndof->ty, ndof->tz, ndof->rx, ndof->ry, ndof->rz, ndof->dt);
1215 * velocity should be proportional to the linear velocity attained by rotational motion of same strength
1217 * proportional to arclength = radius * angle
1219 float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
1221 if (U.ndof_flag & NDOF_ZOOM_INVERT)
1222 zoom_distance = -zoom_distance;
1224 rv3d->dist += zoom_distance;
1227 if (rv3d->viewlock == RV3D_LOCKED) {
1228 /* rotation not allowed -- explore panning options instead */
1229 float pan_vec[3] = {ndof->tx, ndof->ty, 0.0f};
1230 mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
1232 /* transform motion from view to world coordinates */
1233 invert_qt_qt(view_inv, rv3d->viewquat);
1234 mul_qt_v3(view_inv, pan_vec);
1236 /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1237 sub_v3_v3(rv3d->ofs, pan_vec);
1242 rv3d->view = RV3D_VIEW_USER;
1244 if (U.ndof_flag & NDOF_TURNTABLE) {
1246 /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
1247 float angle, rot[4];
1248 float xvec[3] = {1, 0, 0};
1250 /* Determine the direction of the x vector (for rotating up and down) */
1251 mul_qt_v3(view_inv, xvec);
1253 /* Perform the up/down rotation */
1254 angle = rot_sensitivity * dt * ndof->rx;
1255 if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
1257 rot[0] = cos(angle);
1258 mul_v3_v3fl(rot + 1, xvec, sin(angle));
1259 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1261 /* Perform the orbital rotation */
1262 angle = rot_sensitivity * dt * ndof->ry;
1263 if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
1266 /* update the onscreen doo-dad */
1267 rv3d->rot_angle = angle;
1268 rv3d->rot_axis[0] = 0;
1269 rv3d->rot_axis[1] = 0;
1270 rv3d->rot_axis[2] = 1;
1272 rot[0] = cos(angle);
1273 rot[1] = rot[2] = 0.0;
1274 rot[3] = sin(angle);
1275 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1281 float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
1283 if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS)
1286 if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
1289 if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
1293 /* transform rotation axis from view to world coordinates */
1294 mul_qt_v3(view_inv, axis);
1296 /* update the onscreen doo-dad */
1297 rv3d->rot_angle = angle;
1298 copy_v3_v3(rv3d->rot_axis, axis);
1300 axis_angle_to_quat(rot, axis, angle);
1302 /* apply rotation */
1303 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1307 /* rotate around custom center */
1308 if (vod && vod->use_dyn_ofs) {
1311 /* compute the post multiplication quat, to rotate the offset correctly */
1312 conjugate_qt_qt(q1, vod->oldquat);
1313 mul_qt_qtqt(q1, q1, rv3d->viewquat);
1315 conjugate_qt(q1); /* conj == inv for unit quat */
1316 copy_v3_v3(rv3d->ofs, vod->ofs);
1317 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
1318 mul_qt_v3(q1, rv3d->ofs);
1319 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
1324 viewops_data_free(C, op);
1326 ED_view3d_camera_lock_sync(v3d, rv3d);
1328 ED_region_tag_redraw(CTX_wm_region(C));
1330 return OPERATOR_FINISHED;
1334 void VIEW3D_OT_ndof_orbit_zoom(struct wmOperatorType *ot)
1337 ot->name = "NDOF Orbit View with Zoom";
1338 ot->description = "Explore every angle of an object using the 3D mouse";
1339 ot->idname = "VIEW3D_OT_ndof_orbit_zoom";
1342 ot->invoke = ndof_orbit_zoom_invoke;
1343 ot->poll = ED_operator_view3d_active;
1349 /* -- "pan" navigation
1352 static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
1354 if (event->type != NDOF_MOTION)
1355 return OPERATOR_CANCELLED;
1357 View3D *v3d = CTX_wm_view3d(C);
1358 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1359 wmNDOFMotionData *ndof = (wmNDOFMotionData *) event->customdata;
1361 ED_view3d_camera_lock_init(v3d, rv3d);
1363 rv3d->rot_angle = 0.f; /* we're panning here! so erase any leftover rotation from other operators */
1365 if (ndof->progress != P_FINISHING) {
1366 const float dt = ndof->dt;
1368 #if 0 /* ------------------------------------------- zoom with Z */
1369 /* tune these until everything feels right */
1370 const float zoom_sensitivity = 1.f;
1371 const float pan_sensitivity = 1.f;
1373 float pan_vec[3] = {
1374 ndof->tx, ndof->ty, 0
1377 /* "zoom in" or "translate"? depends on zoom mode in user settings? */
1379 float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
1380 rv3d->dist += zoom_distance;
1383 mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
1384 #else /* ------------------------------------------------------- dolly with Z */
1385 float speed = rv3d->dist; /* uses distance from pivot to define dolly */
1387 /* tune these until everything feels right */
1388 const float forward_sensitivity = 1.f;
1389 const float vertical_sensitivity = 0.4f;
1390 const float lateral_sensitivity = 0.6f;
1394 if (U.ndof_flag & NDOF_PANX_INVERT_AXIS)
1395 pan_vec[0] = -lateral_sensitivity * ndof->tvec[0];
1397 pan_vec[0] = lateral_sensitivity * ndof->tvec[0];
1399 if (U.ndof_flag & NDOF_PANZ_INVERT_AXIS)
1400 pan_vec[1] = -vertical_sensitivity * ndof->tvec[1];
1402 pan_vec[1] = vertical_sensitivity * ndof->tvec[1];
1404 if (U.ndof_flag & NDOF_PANY_INVERT_AXIS)
1405 pan_vec[2] = -forward_sensitivity * ndof->tvec[2];
1407 pan_vec[2] = forward_sensitivity * ndof->tvec[2];
1409 mul_v3_fl(pan_vec, speed * dt);
1411 /* transform motion from view to world coordinates */
1412 invert_qt_qt(view_inv, rv3d->viewquat);
1413 mul_qt_v3(view_inv, pan_vec);
1415 /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1416 sub_v3_v3(rv3d->ofs, pan_vec);
1419 ED_view3d_camera_lock_sync(v3d, rv3d);
1421 ED_region_tag_redraw(CTX_wm_region(C));
1423 return OPERATOR_FINISHED;
1427 void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
1430 ot->name = "NDOF Pan View";
1431 ot->description = "Position your viewpoint with the 3D mouse";
1432 ot->idname = "VIEW3D_OT_ndof_pan";
1435 ot->invoke = ndof_pan_invoke;
1436 ot->poll = ED_operator_view3d_active;
1444 * this is basically just the pan only code + the rotate only code crammed into one function that does both
1446 static int ndof_all_invoke(bContext *C, wmOperator *op, wmEvent *event)
1448 if (event->type != NDOF_MOTION) {
1449 return OPERATOR_CANCELLED;
1456 View3D *v3d = CTX_wm_view3d(C);
1457 wmNDOFMotionData *ndof = (wmNDOFMotionData *) event->customdata;
1459 viewops_data_create(C, op, event);
1460 vod = op->customdata;
1463 ED_view3d_camera_lock_init(v3d, rv3d);
1465 if (ndof->progress != P_FINISHING) {
1467 const float dt = ndof->dt;
1470 float speed = rv3d->dist; /* uses distance from pivot to define dolly */
1472 /* tune these until everything feels right */
1473 const float forward_sensitivity = 1.f;
1474 const float vertical_sensitivity = 0.4f;
1475 const float lateral_sensitivity = 0.6f;
1477 const float rot_sensitivity = 1.f;
1480 invert_qt_qt(view_inv, rv3d->viewquat);
1482 if (U.ndof_flag & NDOF_PANX_INVERT_AXIS)
1483 pan_vec[0] = -lateral_sensitivity * ndof->tvec[0];
1485 pan_vec[0] = lateral_sensitivity * ndof->tvec[0];
1487 if (U.ndof_flag & NDOF_PANZ_INVERT_AXIS)
1488 pan_vec[1] = -vertical_sensitivity * ndof->tvec[1];
1490 pan_vec[1] = vertical_sensitivity * ndof->tvec[1];
1492 if (U.ndof_flag & NDOF_PANY_INVERT_AXIS)
1493 pan_vec[2] = -forward_sensitivity * ndof->tvec[2];
1495 pan_vec[2] = forward_sensitivity * ndof->tvec[2];
1497 mul_v3_fl(pan_vec, speed * dt);
1499 /* transform motion from view to world coordinates */
1500 mul_qt_v3(view_inv, pan_vec);
1502 /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1503 sub_v3_v3(rv3d->ofs, pan_vec);
1505 if (U.ndof_flag & NDOF_TURNTABLE) {
1506 /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
1507 float angle, rot[4];
1508 float xvec[3] = {1, 0, 0};
1510 /* Determine the direction of the x vector (for rotating up and down) */
1511 mul_qt_v3(view_inv, xvec);
1513 /* Perform the up/down rotation */
1514 angle = rot_sensitivity * dt * ndof->rx;
1515 if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
1517 rot[0] = cos(angle);
1518 mul_v3_v3fl(rot + 1, xvec, sin(angle));
1519 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1521 /* Perform the orbital rotation */
1522 angle = rot_sensitivity * dt * ndof->ry;
1523 if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
1526 /* update the onscreen doo-dad */
1527 rv3d->rot_angle = angle;
1528 rv3d->rot_axis[0] = 0;
1529 rv3d->rot_axis[1] = 0;
1530 rv3d->rot_axis[2] = 1;
1532 rot[0] = cos(angle);
1533 rot[1] = rot[2] = 0.0;
1534 rot[3] = sin(angle);
1535 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1542 float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis);
1544 if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS)
1547 if (U.ndof_flag & NDOF_TILT_INVERT_AXIS)
1550 if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS)
1553 /* transform rotation axis from view to world coordinates */
1554 mul_qt_v3(view_inv, axis);
1556 /* update the onscreen doo-dad */
1557 rv3d->rot_angle = angle;
1558 copy_v3_v3(rv3d->rot_axis, axis);
1560 axis_angle_to_quat(rot, axis, angle);
1562 /* apply rotation */
1563 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
1567 /* rotate around custom center */
1568 if (vod->use_dyn_ofs) {
1571 /* compute the post multiplication quat, to rotate the offset correctly */
1572 conjugate_qt_qt(q1, vod->oldquat);
1573 mul_qt_qtqt(q1, q1, rv3d->viewquat);
1575 conjugate_qt(q1); /* conj == inv for unit quat */
1576 copy_v3_v3(rv3d->ofs, vod->ofs);
1577 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
1578 mul_qt_v3(q1, rv3d->ofs);
1579 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
1584 viewops_data_free(C, op);
1586 ED_view3d_camera_lock_sync(v3d, rv3d);
1588 ED_region_tag_redraw(CTX_wm_region(C));
1590 return OPERATOR_FINISHED;
1594 void VIEW3D_OT_ndof_all(struct wmOperatorType *ot)
1597 ot->name = "NDOF Move View";
1598 ot->description = "Position your viewpoint with the 3D mouse";
1599 ot->idname = "VIEW3D_OT_ndof_all";
1602 ot->invoke = ndof_all_invoke;
1603 ot->poll = ED_operator_view3d_active;
1609 /* ************************ viewmove ******************************** */
1612 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
1614 /* called in transform_ops.c, on each regeneration of keymaps */
1615 void viewmove_modal_keymap(wmKeyConfig *keyconf)
1617 static EnumPropertyItem modal_items[] = {
1618 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
1620 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
1621 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1623 {0, NULL, 0, NULL, NULL}
1626 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Move Modal");
1628 /* this function is called for each spacetype, only needs to add map once */
1629 if (keymap && keymap->modal_items) return;
1631 keymap = WM_modalkeymap_add(keyconf, "View3D Move Modal", modal_items);
1633 /* items for modal map */
1634 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1635 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1637 /* disabled mode switching for now, can re-implement better, later on */
1639 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
1640 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
1641 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1644 /* assign map to operators */
1645 WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
1649 static void viewmove_apply(ViewOpsData *vod, int x, int y)
1651 if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
1652 const float zoomfac = BKE_screen_view3d_zoom_to_fac((float)vod->rv3d->camzoom) * 2.0f;
1653 vod->rv3d->camdx += (vod->oldx - x) / (vod->ar->winx * zoomfac);
1654 vod->rv3d->camdy += (vod->oldy - y) / (vod->ar->winy * zoomfac);
1655 CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
1656 CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
1662 mval_f[0] = x - vod->oldx;
1663 mval_f[1] = y - vod->oldy;
1664 ED_view3d_win_to_delta(vod->ar, mval_f, dvec);
1666 add_v3_v3(vod->rv3d->ofs, dvec);
1668 if (vod->rv3d->viewlock & RV3D_BOXVIEW)
1669 view3d_boxview_sync(vod->sa, vod->ar);
1675 ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
1677 ED_region_tag_redraw(vod->ar);
1681 static int viewmove_modal(bContext *C, wmOperator *op, wmEvent *event)
1684 ViewOpsData *vod = op->customdata;
1685 short event_code = VIEW_PASS;
1687 /* execute the events */
1688 if (event->type == MOUSEMOVE) {
1689 event_code = VIEW_APPLY;
1691 else if (event->type == EVT_MODAL_MAP) {
1692 switch (event->val) {
1693 case VIEW_MODAL_CONFIRM:
1694 event_code = VIEW_CONFIRM;
1696 case VIEWROT_MODAL_SWITCH_ZOOM:
1697 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
1698 event_code = VIEW_CONFIRM;
1700 case VIEWROT_MODAL_SWITCH_ROTATE:
1701 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
1702 event_code = VIEW_CONFIRM;
1706 else if (event->type == vod->origkey && event->val == KM_RELEASE) {
1707 event_code = VIEW_CONFIRM;
1710 if (event_code == VIEW_APPLY) {
1711 viewmove_apply(vod, event->x, event->y);
1713 else if (event_code == VIEW_CONFIRM) {
1714 ED_view3d_depth_tag_update(vod->rv3d);
1716 viewops_data_free(C, op);
1718 return OPERATOR_FINISHED;
1721 return OPERATOR_RUNNING_MODAL;
1724 static int viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event)
1728 /* makes op->customdata */
1729 viewops_data_create(C, op, event);
1730 vod = op->customdata;
1732 if (event->type == MOUSEPAN) {
1733 /* invert it, trackpad scroll follows same principle as 2d windows this way */
1734 viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy);
1735 ED_view3d_depth_tag_update(vod->rv3d);
1737 viewops_data_free(C, op);
1739 return OPERATOR_FINISHED;
1742 /* add temp handler */
1743 WM_event_add_modal_handler(C, op);
1745 return OPERATOR_RUNNING_MODAL;
1749 static int viewmove_cancel(bContext *C, wmOperator *op)
1751 viewops_data_free(C, op);
1753 return OPERATOR_CANCELLED;
1756 void VIEW3D_OT_move(wmOperatorType *ot)
1760 ot->name = "Move View";
1761 ot->description = "Move the view";
1762 ot->idname = "VIEW3D_OT_move";
1765 ot->invoke = viewmove_invoke;
1766 ot->modal = viewmove_modal;
1767 ot->poll = ED_operator_view3d_active;
1768 ot->cancel = viewmove_cancel;
1771 ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
1774 /* ************************ viewzoom ******************************** */
1776 /* viewdolly_modal_keymap has an exact copy of this, apply fixes to both */
1777 /* called in transform_ops.c, on each regeneration of keymaps */
1778 void viewzoom_modal_keymap(wmKeyConfig *keyconf)
1780 static EnumPropertyItem modal_items[] = {
1781 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
1783 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1784 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
1786 {0, NULL, 0, NULL, NULL}
1789 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Zoom Modal");
1791 /* this function is called for each spacetype, only needs to add map once */
1792 if (keymap && keymap->modal_items) return;
1794 keymap = WM_modalkeymap_add(keyconf, "View3D Zoom Modal", modal_items);
1796 /* items for modal map */
1797 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1798 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1800 /* disabled mode switching for now, can re-implement better, later on */
1802 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1803 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1804 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
1807 /* assign map to operators */
1808 WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
1811 static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my)
1813 RegionView3D *rv3d = ar->regiondata;
1815 if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1822 negate_v3_v3(tpos, rv3d->ofs);
1824 /* Project cursor position into 3D space */
1825 initgrabz(rv3d, tpos[0], tpos[1], tpos[2]);
1827 mval_f[0] = (float)(((mx - ar->winrct.xmin) * 2) - ar->winx) / 2.0f;
1828 mval_f[1] = (float)(((my - ar->winrct.ymin) * 2) - ar->winy) / 2.0f;
1829 ED_view3d_win_to_delta(ar, mval_f, dvec);
1831 /* Calculate view target position for dolly */
1832 add_v3_v3v3(tvec, tpos, dvec);
1835 /* Offset to target position and dolly */
1836 new_dist = rv3d->dist * dfac;
1838 copy_v3_v3(rv3d->ofs, tvec);
1839 rv3d->dist = new_dist;
1841 /* Calculate final offset */
1842 madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
1850 static void viewzoom_apply(ViewOpsData *vod, const int x, const int y, const short viewzoom, const short zoom_invert)
1855 use_cam_zoom = (vod->rv3d->persp == RV3D_CAMOB) && !(vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d));
1859 delta = (x - vod->origx + y - vod->origy) / 10.0f;
1860 vod->rv3d->camzoom = vod->camzoom0 + (zoom_invert ? -delta : delta);
1862 CLAMP(vod->rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
1865 if (viewzoom == USER_ZOOM_CONT) {
1866 double time = PIL_check_seconds_timer();
1867 float time_step = (float)(time - vod->timer_lastdraw);
1870 if (U.uiflag & USER_ZOOM_HORIZ) {
1871 fac = (float)(vod->origx - x);
1874 fac = (float)(vod->origy - y);
1882 zfac = 1.0f + ((fac / 20.0f) * time_step);
1883 vod->timer_lastdraw = time;
1885 else if (viewzoom == USER_ZOOM_SCALE) {
1886 int ctr[2], len1, len2;
1887 /* method which zooms based on how far you move the mouse */
1889 ctr[0] = BLI_rcti_cent_x(&vod->ar->winrct);
1890 ctr[1] = BLI_rcti_cent_y(&vod->ar->winrct);
1892 len1 = (int)sqrt((ctr[0] - x) * (ctr[0] - x) + (ctr[1] - y) * (ctr[1] - y)) + 5;
1893 len2 = (int)sqrt((ctr[0] - vod->origx) * (ctr[0] - vod->origx) + (ctr[1] - vod->origy) * (ctr[1] - vod->origy)) + 5;
1895 zfac = vod->dist0 * ((float)len2 / len1) / vod->rv3d->dist;
1897 else { /* USER_ZOOM_DOLLY */
1900 if (U.uiflag & USER_ZOOM_HORIZ) {
1901 len1 = (vod->ar->winrct.xmax - x) + 5;
1902 len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
1905 len1 = (vod->ar->winrct.ymax - y) + 5;
1906 len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
1909 SWAP(float, len1, len2);
1913 /* zfac is ignored in this case, see below */
1915 zfac = vod->camzoom0 * (2.0f * ((len1 / len2) - 1.0f) + 1.0f) / vod->rv3d->camzoom;
1919 zfac = vod->dist0 * (2.0f * ((len1 / len2) - 1.0f) + 1.0f) / vod->rv3d->dist;
1923 if (!use_cam_zoom) {
1924 if (zfac != 1.0f && zfac * vod->rv3d->dist > 0.001f * vod->grid &&
1925 zfac * vod->rv3d->dist < 10.0f * vod->far)
1927 view_zoom_mouseloc(vod->ar, zfac, vod->oldx, vod->oldy);
1931 /* these limits were in old code too */
1932 if (vod->rv3d->dist < 0.001f * vod->grid) vod->rv3d->dist = 0.001f * vod->grid;
1933 if (vod->rv3d->dist > 10.0f * vod->far) vod->rv3d->dist = 10.0f * vod->far;
1935 if (vod->rv3d->viewlock & RV3D_BOXVIEW)
1936 view3d_boxview_sync(vod->sa, vod->ar);
1938 ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
1940 ED_region_tag_redraw(vod->ar);
1944 static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event)
1946 ViewOpsData *vod = op->customdata;
1947 short event_code = VIEW_PASS;
1949 /* execute the events */
1950 if (event->type == TIMER && event->customdata == vod->timer) {
1951 /* continuous zoom */
1952 event_code = VIEW_APPLY;
1954 else if (event->type == MOUSEMOVE) {
1955 event_code = VIEW_APPLY;
1957 else if (event->type == EVT_MODAL_MAP) {
1958 switch (event->val) {
1959 case VIEW_MODAL_CONFIRM:
1960 event_code = VIEW_CONFIRM;
1962 case VIEWROT_MODAL_SWITCH_MOVE:
1963 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
1964 event_code = VIEW_CONFIRM;
1966 case VIEWROT_MODAL_SWITCH_ROTATE:
1967 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
1968 event_code = VIEW_CONFIRM;
1972 else if (event->type == vod->origkey && event->val == KM_RELEASE) {
1973 event_code = VIEW_CONFIRM;
1976 if (event_code == VIEW_APPLY) {
1977 viewzoom_apply(vod, event->x, event->y, U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0);
1979 else if (event_code == VIEW_CONFIRM) {
1980 ED_view3d_depth_tag_update(vod->rv3d);
1981 viewops_data_free(C, op);
1983 return OPERATOR_FINISHED;
1986 return OPERATOR_RUNNING_MODAL;
1989 static int viewzoom_exec(bContext *C, wmOperator *op)
1997 int delta = RNA_int_get(op->ptr, "delta");
2000 if (op->customdata) {
2001 ViewOpsData *vod = op->customdata;
2007 sa = CTX_wm_area(C);
2008 ar = CTX_wm_region(C);
2011 v3d = sa->spacedata.first;
2012 rv3d = ar->regiondata;
2014 mx = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : ar->winx / 2;
2015 my = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : ar->winy / 2;
2017 use_cam_zoom = (rv3d->persp == RV3D_CAMOB) && !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
2020 /* this min and max is also in viewmove() */
2022 rv3d->camzoom -= 10.0f;
2023 if (rv3d->camzoom < RV3D_CAMZOOM_MIN) rv3d->camzoom = RV3D_CAMZOOM_MIN;
2025 else if (rv3d->dist < 10.0f * v3d->far) {
2026 view_zoom_mouseloc(ar, 1.2f, mx, my);
2031 rv3d->camzoom += 10.0f;
2032 if (rv3d->camzoom > RV3D_CAMZOOM_MAX) rv3d->camzoom = RV3D_CAMZOOM_MAX;
2034 else if (rv3d->dist > 0.001f * v3d->grid) {
2035 view_zoom_mouseloc(ar, 0.83333f, mx, my);
2039 if (rv3d->viewlock & RV3D_BOXVIEW)
2040 view3d_boxview_sync(sa, ar);
2042 ED_view3d_depth_tag_update(rv3d);
2044 ED_view3d_camera_lock_sync(v3d, rv3d);
2046 ED_region_tag_redraw(ar);
2048 viewops_data_free(C, op);
2050 return OPERATOR_FINISHED;
2053 /* this is an exact copy of viewzoom_modal_keymap */
2054 /* called in transform_ops.c, on each regeneration of keymaps */
2055 void viewdolly_modal_keymap(wmKeyConfig *keyconf)
2057 static EnumPropertyItem modal_items[] = {
2058 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
2060 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
2061 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
2063 {0, NULL, 0, NULL, NULL}
2066 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Dolly Modal");
2068 /* this function is called for each spacetype, only needs to add map once */
2069 if (keymap && keymap->modal_items) return;
2071 keymap = WM_modalkeymap_add(keyconf, "View3D Dolly Modal", modal_items);
2073 /* items for modal map */
2074 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2075 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
2077 /* disabled mode switching for now, can re-implement better, later on */
2079 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2080 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
2081 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
2084 /* assign map to operators */
2085 WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
2088 /* viewdolly_invoke() copied this function, changes here may apply there */
2089 static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
2093 /* makes op->customdata */
2094 viewops_data_create(C, op, event);
2095 vod = op->customdata;
2097 /* if one or the other zoom position aren't set, set from event */
2098 if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
2099 RNA_int_set(op->ptr, "mx", event->x);
2100 RNA_int_set(op->ptr, "my", event->y);
2103 if (RNA_struct_property_is_set(op->ptr, "delta")) {
2104 viewzoom_exec(C, op);
2107 if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
2109 if (U.uiflag & USER_ZOOM_HORIZ) {
2110 vod->origx = vod->oldx = event->x;
2111 viewzoom_apply(vod, event->prevx, event->prevy, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
2114 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
2115 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
2116 viewzoom_apply(vod, event->prevx, event->prevy, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
2118 ED_view3d_depth_tag_update(vod->rv3d);
2120 viewops_data_free(C, op);
2121 return OPERATOR_FINISHED;
2124 if (U.viewzoom == USER_ZOOM_CONT) {
2125 /* needs a timer to continue redrawing */
2126 vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
2127 vod->timer_lastdraw = PIL_check_seconds_timer();
2130 /* add temp handler */
2131 WM_event_add_modal_handler(C, op);
2133 return OPERATOR_RUNNING_MODAL;
2136 return OPERATOR_FINISHED;
2139 static int viewzoom_cancel(bContext *C, wmOperator *op)
2141 viewops_data_free(C, op);
2143 return OPERATOR_CANCELLED;
2146 void VIEW3D_OT_zoom(wmOperatorType *ot)
2149 ot->name = "Zoom View";
2150 ot->description = "Zoom in/out in the view";
2151 ot->idname = "VIEW3D_OT_zoom";
2154 ot->invoke = viewzoom_invoke;
2155 ot->exec = viewzoom_exec;
2156 ot->modal = viewzoom_modal;
2157 ot->poll = ED_operator_region_view3d_active;
2158 ot->cancel = viewzoom_cancel;
2161 ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
2163 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2164 RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
2165 RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
2169 /* ************************ viewdolly ******************************** */
2170 static void view_dolly_mouseloc(ARegion *ar, float orig_ofs[3], float dvec[3], float dfac)
2172 RegionView3D *rv3d = ar->regiondata;
2173 madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
2176 static void viewdolly_apply(ViewOpsData *vod, int x, int y, const short zoom_invert)
2183 if (U.uiflag & USER_ZOOM_HORIZ) {
2184 len1 = (vod->ar->winrct.xmax - x) + 5;
2185 len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
2188 len1 = (vod->ar->winrct.ymax - y) + 5;
2189 len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
2192 SWAP(float, len1, len2);
2194 zfac = 1.0f + ((len1 - len2) * 0.01f * vod->rv3d->dist);
2198 view_dolly_mouseloc(vod->ar, vod->ofs, vod->mousevec, zfac);
2200 if (vod->rv3d->viewlock & RV3D_BOXVIEW)
2201 view3d_boxview_sync(vod->sa, vod->ar);
2203 ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
2205 ED_region_tag_redraw(vod->ar);
2209 static int viewdolly_modal(bContext *C, wmOperator *op, wmEvent *event)
2211 ViewOpsData *vod = op->customdata;
2212 short event_code = VIEW_PASS;
2214 /* execute the events */
2215 if (event->type == MOUSEMOVE) {
2216 event_code = VIEW_APPLY;
2218 else if (event->type == EVT_MODAL_MAP) {
2219 switch (event->val) {
2220 case VIEW_MODAL_CONFIRM:
2221 event_code = VIEW_CONFIRM;
2223 case VIEWROT_MODAL_SWITCH_MOVE:
2224 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
2225 event_code = VIEW_CONFIRM;
2227 case VIEWROT_MODAL_SWITCH_ROTATE:
2228 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
2229 event_code = VIEW_CONFIRM;
2233 else if (event->type == vod->origkey && event->val == KM_RELEASE) {
2234 event_code = VIEW_CONFIRM;
2237 if (event_code == VIEW_APPLY) {
2238 viewdolly_apply(vod, event->x, event->y, (U.uiflag & USER_ZOOM_INVERT) != 0);
2240 else if (event_code == VIEW_CONFIRM) {
2241 ED_view3d_depth_tag_update(vod->rv3d);
2242 viewops_data_free(C, op);
2244 return OPERATOR_FINISHED;
2247 return OPERATOR_RUNNING_MODAL;
2250 static int viewdolly_exec(bContext *C, wmOperator *op)
2258 int delta = RNA_int_get(op->ptr, "delta");
2260 if (op->customdata) {
2261 ViewOpsData *vod = op->customdata;
2265 copy_v3_v3(mousevec, vod->mousevec);
2268 sa = CTX_wm_area(C);
2269 ar = CTX_wm_region(C);
2270 negate_v3_v3(mousevec, ((RegionView3D *)ar->regiondata)->viewinv[2]);
2271 normalize_v3(mousevec);
2274 /* v3d = sa->spacedata.first; */ /* UNUSED */
2275 rv3d = ar->regiondata;
2277 /* overwrite the mouse vector with the view direction (zoom into the center) */
2278 if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
2279 normalize_v3_v3(mousevec, rv3d->viewinv[2]);
2283 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 1.2f);
2286 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 0.83333f);
2289 if (rv3d->viewlock & RV3D_BOXVIEW)
2290 view3d_boxview_sync(sa, ar);
2292 ED_view3d_depth_tag_update(rv3d);
2293 ED_region_tag_redraw(ar);
2295 viewops_data_free(C, op);
2297 return OPERATOR_FINISHED;
2300 /* copied from viewzoom_invoke(), changes here may apply there */
2301 static int viewdolly_invoke(bContext *C, wmOperator *op, wmEvent *event)
2305 /* makes op->customdata */
2306 viewops_data_create(C, op, event);
2307 vod = op->customdata;
2309 /* if one or the other zoom position aren't set, set from event */
2310 if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
2311 RNA_int_set(op->ptr, "mx", event->x);
2312 RNA_int_set(op->ptr, "my", event->y);
2315 if (RNA_struct_property_is_set(op->ptr, "delta")) {
2316 viewdolly_exec(C, op);
2319 /* overwrite the mouse vector with the view direction (zoom into the center) */
2320 if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
2321 negate_v3_v3(vod->mousevec, vod->rv3d->viewinv[2]);
2322 normalize_v3(vod->mousevec);
2325 if (event->type == MOUSEZOOM) {
2326 /* Bypass Zoom invert flag for track pads (pass FALSE always) */
2328 if (U.uiflag & USER_ZOOM_HORIZ) {
2329 vod->origx = vod->oldx = event->x;
2330 viewdolly_apply(vod, event->prevx, event->prevy, (U.uiflag & USER_ZOOM_INVERT) == 0);
2334 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
2335 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
2336 viewdolly_apply(vod, event->prevx, event->prevy, (U.uiflag & USER_ZOOM_INVERT) == 0);
2338 ED_view3d_depth_tag_update(vod->rv3d);
2340 viewops_data_free(C, op);
2341 return OPERATOR_FINISHED;
2344 /* add temp handler */
2345 WM_event_add_modal_handler(C, op);
2347 return OPERATOR_RUNNING_MODAL;
2350 return OPERATOR_FINISHED;
2353 /* like ED_operator_region_view3d_active but check its not in ortho view */
2354 static int viewdolly_poll(bContext *C)
2356 RegionView3D *rv3d = CTX_wm_region_view3d(C);
2359 if (rv3d->persp == RV3D_PERSP) {
2363 View3D *v3d = CTX_wm_view3d(C);
2364 if (ED_view3d_camera_lock_check(v3d, rv3d)) {
2372 static int viewdolly_cancel(bContext *C, wmOperator *op)
2374 viewops_data_free(C, op);
2376 return OPERATOR_CANCELLED;
2379 void VIEW3D_OT_dolly(wmOperatorType *ot)
2382 ot->name = "Dolly View";
2383 ot->description = "Dolly in/out in the view";
2384 ot->idname = "VIEW3D_OT_dolly";
2387 ot->invoke = viewdolly_invoke;
2388 ot->exec = viewdolly_exec;
2389 ot->modal = viewdolly_modal;
2390 ot->poll = viewdolly_poll;
2391 ot->cancel = viewdolly_cancel;
2394 ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
2396 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2397 RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
2398 RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
2401 static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar,
2402 const float min[3], const float max[3],
2405 RegionView3D *rv3d = ar->regiondata;
2413 sub_v3_v3v3(afm, max, min);
2414 size = max_fff(afm[0], afm[1], afm[2]);
2417 /* fix up zoom distance if needed */
2419 if (rv3d->is_persp) {
2420 float lens, sensor_size;
2421 /* offset the view based on the lens */
2422 if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) {
2423 CameraParams params;
2424 BKE_camera_params_init(¶ms);
2425 BKE_camera_params_from_object(¶ms, v3d->camera);
2428 sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y);
2432 sensor_size = DEFAULT_SENSOR_WIDTH;
2434 size = ED_view3d_radius_to_persp_dist(focallength_to_fov(lens, sensor_size), size / 2.0f) * VIEW3D_MARGIN;
2436 /* do not zoom closer than the near clipping plane */
2437 size = max_ff(size, v3d->near * 1.5f);
2440 if (size < 0.0001f) {
2441 /* bounding box was a single point so do not zoom */
2445 /* adjust zoom so it looks nicer */
2446 size = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN;
2451 mid_v3_v3v3(new_ofs, min, max);
2456 /* correction for window aspect ratio */
2457 if (ar->winy > 2 && ar->winx > 2) {
2458 size = (float)ar->winx / (float)ar->winy;
2459 if (size < 1.0f) size = 1.0f / size;
2463 if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) {
2464 rv3d->persp = RV3D_PERSP;
2465 view3d_smooth_view(C, v3d, ar, v3d->camera, NULL, new_ofs, NULL, ok_dist ? &new_dist : NULL, NULL);
2468 view3d_smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, ok_dist ? &new_dist : NULL, NULL);
2471 /* smooth view does viewlock RV3D_BOXVIEW copy */
2474 /* same as view3d_from_minmax but for all regions (except cameras) */
2475 static void view3d_from_minmax_multi(bContext *C, View3D *v3d,
2476 const float min[3], const float max[3],
2479 ScrArea *sa = CTX_wm_area(C);
2481 for (ar = sa->regionbase.first; ar; ar = ar->next) {
2482 if (ar->regiontype == RGN_TYPE_WINDOW) {
2483 RegionView3D *rv3d = ar->regiondata;
2484 /* when using all regions, don't jump out of camera view,
2485 * but _do_ allow locked cameras to be moved */
2486 if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
2487 view3d_from_minmax(C, v3d, ar, min, max, ok_dist);
2493 static int view3d_all_exec(bContext *C, wmOperator *op) /* was view3d_home() in 2.4x */
2495 ARegion *ar = CTX_wm_region(C);
2496 View3D *v3d = CTX_wm_view3d(C);
2497 Scene *scene = CTX_data_scene(C);
2500 const short use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
2501 const short skip_camera = (ED_view3d_camera_lock_check(v3d, ar->regiondata) ||
2502 /* any one of the regions may be locked */
2503 (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
2504 int center = RNA_boolean_get(op->ptr, "center");
2506 float min[3], max[3];
2507 int ok = 1, onedone = FALSE;
2510 /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
2511 curs = give_cursor(scene, v3d);
2517 INIT_MINMAX(min, max);
2520 for (base = scene->base.first; base; base = base->next) {
2521 if (BASE_VISIBLE(v3d, base)) {
2524 if (skip_camera && base->object == v3d->camera) {
2528 BKE_object_minmax(base->object, min, max, FALSE);
2532 ED_region_tag_redraw(ar);
2533 /* TODO - should this be cancel?
2534 * I think no, because we always move the cursor, with or without
2535 * object, but in this case there is no change in the scene,
2536 * only the cursor so I choice a ED_region_tag like
2537 * view3d_smooth_view do for the center_cursor.
2540 return OPERATOR_FINISHED;
2544 return OPERATOR_FINISHED;
2547 if (use_all_regions) {
2548 view3d_from_minmax_multi(C, v3d, min, max, TRUE);
2551 view3d_from_minmax(C, v3d, ar, min, max, TRUE);
2554 return OPERATOR_FINISHED;
2558 void VIEW3D_OT_view_all(wmOperatorType *ot)
2563 ot->name = "View All";
2564 ot->description = "View all objects in scene";
2565 ot->idname = "VIEW3D_OT_view_all";
2568 ot->exec = view3d_all_exec;
2569 ot->poll = ED_operator_region_view3d_active;
2574 prop = RNA_def_boolean(ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
2575 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2576 RNA_def_boolean(ot->srna, "center", 0, "Center", "");
2579 /* like a localview without local!, was centerview() in 2.4x */
2580 static int viewselected_exec(bContext *C, wmOperator *op)
2582 ARegion *ar = CTX_wm_region(C);
2583 View3D *v3d = CTX_wm_view3d(C);
2584 Scene *scene = CTX_data_scene(C);
2586 Object *obedit = CTX_data_edit_object(C);
2587 float min[3], max[3];
2588 int ok = 0, ok_dist = 1;
2589 const short use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
2590 const short skip_camera = (ED_view3d_camera_lock_check(v3d, ar->regiondata) ||
2591 /* any one of the regions may be locked */
2592 (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA));
2594 INIT_MINMAX(min, max);
2596 if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) {
2597 /* hard-coded exception, we look for the one selected armature */
2598 /* this is weak code this way, we should make a generic active/selection callback interface once... */
2600 for (base = scene->base.first; base; base = base->next) {
2601 if (TESTBASELIB(v3d, base)) {
2602 if (base->object->type == OB_ARMATURE)
2603 if (base->object->mode & OB_MODE_POSE)
2613 ok = ED_view3d_minmax_verts(obedit, min, max); /* only selected */
2615 else if (ob && (ob->mode & OB_MODE_POSE)) {
2617 bArmature *arm = ob->data;
2618 bPoseChannel *pchan;
2621 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
2622 if (pchan->bone->flag & BONE_SELECTED) {
2623 if (pchan->bone->layer & arm->layer) {
2624 bPoseChannel *pchan_tx = pchan->custom_tx ? pchan->custom_tx : pchan;
2626 mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_head);
2627 minmax_v3v3_v3(min, max, vec);
2628 mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_tail);
2629 minmax_v3v3_v3(min, max, vec);
2635 else if (paint_facesel_test(ob)) {
2636 ok = paintface_minmax(ob, min, max);
2638 else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
2639 ok = PE_minmax(scene, min, max);
2641 else if (ob && (ob->mode & OB_MODE_SCULPT)) {
2642 ok = ED_sculpt_minmax(C, min, max);
2643 ok_dist = 0; /* don't zoom */
2647 for (base = FIRSTBASE; base; base = base->next) {
2648 if (TESTBASE(v3d, base)) {
2650 if (skip_camera && base->object == v3d->camera) {
2654 /* account for duplis */
2655 if (BKE_object_minmax_dupli(scene, base->object, min, max, FALSE) == 0)
2656 BKE_object_minmax(base->object, min, max, FALSE); /* use if duplis not found */
2664 return OPERATOR_FINISHED;
2667 if (use_all_regions) {
2668 view3d_from_minmax_multi(C, v3d, min, max, ok_dist);
2671 view3d_from_minmax(C, v3d, ar, min, max, ok_dist);
2674 // XXX BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
2676 return OPERATOR_FINISHED;
2679 void VIEW3D_OT_view_selected(wmOperatorType *ot)
2684 ot->name = "View Selected";
2685 ot->description = "Move the view to the selection center";
2686 ot->idname = "VIEW3D_OT_view_selected";
2689 ot->exec = viewselected_exec;
2690 ot->poll = ED_operator_region_view3d_active;
2696 prop = RNA_def_boolean(ot->srna, "use_all_regions", 0, "All Regions", "View selected for all regions");
2697 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2700 static int view_lock_clear_exec(bContext *C, wmOperator *UNUSED(op))
2702 View3D *v3d = CTX_wm_view3d(C);
2705 ED_view3D_lock_clear(v3d);
2707 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
2709 return OPERATOR_FINISHED;
2712 return OPERATOR_CANCELLED;
2716 void VIEW3D_OT_view_lock_clear(wmOperatorType *ot)
2720 ot->name = "View Lock Clear";
2721 ot->description = "Clear all view locking";
2722 ot->idname = "VIEW3D_OT_view_lock_clear";
2725 ot->exec = view_lock_clear_exec;
2726 ot->poll = ED_operator_region_view3d_active;
2732 static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op))
2734 View3D *v3d = CTX_wm_view3d(C);
2735 Object *obact = CTX_data_active_object(C);
2739 ED_view3D_lock_clear(v3d);
2741 v3d->ob_centre = obact; /* can be NULL */
2743 if (obact && obact->type == OB_ARMATURE) {
2744 if (obact->mode & OB_MODE_POSE) {
2745 bPoseChannel *pcham_act = BKE_pose_channel_active(obact);
2747 BLI_strncpy(v3d->ob_centre_bone, pcham_act->name, sizeof(v3d->ob_centre_bone));
2751 EditBone *ebone_act = ((bArmature *)obact->data)->act_edbone;
2753 BLI_strncpy(v3d->ob_centre_bone, ebone_act->name, sizeof(v3d->ob_centre_bone));
2758 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
2760 return OPERATOR_FINISHED;
2763 return OPERATOR_CANCELLED;
2767 void VIEW3D_OT_view_lock_to_active(wmOperatorType *ot)
2771 ot->name = "View Lock to Active";
2772 ot->description = "Lock the view to the active object/bone";
2773 ot->idname = "VIEW3D_OT_view_lock_to_active";
2776 ot->exec = view_lock_to_active_exec;
2777 ot->poll = ED_operator_region_view3d_active;
2783 static int viewcenter_cursor_exec(bContext *C, wmOperator *UNUSED(op))
2785 View3D *v3d = CTX_wm_view3d(C);
2786 RegionView3D *rv3d = CTX_wm_region_view3d(C);
2787 Scene *scene = CTX_data_scene(C);
2790 ARegion *ar = CTX_wm_region(C);
2792 /* non camera center */
2794 negate_v3_v3(new_ofs, give_cursor(scene, v3d));
2795 view3d_smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, NULL, NULL);
2797 /* smooth view does viewlock RV3D_BOXVIEW copy */
2800 return OPERATOR_FINISHED;
2803 void VIEW3D_OT_view_center_cursor(wmOperatorType *ot)
2806 ot->name = "Center View to Cursor";
2807 ot->description = "Center the view so that the cursor is in the middle of the view";
2808 ot->idname = "VIEW3D_OT_view_center_cursor";
2811 ot->exec = viewcenter_cursor_exec;
2812 ot->poll = ED_operator_view3d_active;
2818 static int view3d_center_camera_exec(bContext *C, wmOperator *UNUSED(op)) /* was view3d_home() in 2.4x */
2820 Scene *scene = CTX_data_scene(C);
2828 /* no NULL check is needed, poll checks */
2829 ED_view3d_context_user_region(C, &v3d, &ar);
2830 rv3d = ar->regiondata;
2832 rv3d->camdx = rv3d->camdy = 0.0f;
2834 ED_view3d_calc_camera_border_size(scene, ar, v3d, rv3d, size);
2836 /* 4px is just a little room from the edge of the area */
2837 xfac = (float)ar->winx / (float)(size[0] + 4);
2838 yfac = (float)ar->winy / (float)(size[1] + 4);
2840 rv3d->camzoom = BKE_screen_view3d_zoom_from_fac(min_ff(xfac, yfac));
2841 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
2843 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
2845 return OPERATOR_FINISHED;
2848 void VIEW3D_OT_view_center_camera(wmOperatorType *ot)
2851 ot->name = "View Camera Center";
2852 ot->description = "Center the camera view";
2853 ot->idname = "VIEW3D_OT_view_center_camera";
2856 ot->exec = view3d_center_camera_exec;
2857 ot->poll = view3d_camera_user_poll;
2863 /* ********************* Set render border operator ****************** */
2865 static int render_border_exec(bContext *C, wmOperator *op)
2867 View3D *v3d = CTX_wm_view3d(C);
2868 ARegion *ar = CTX_wm_region(C);
2869 RegionView3D *rv3d = ED_view3d_context_rv3d(C);
2871 Scene *scene = CTX_data_scene(C);
2876 int camera_only = RNA_boolean_get(op->ptr, "camera_only");
2878 if (camera_only && rv3d->persp != RV3D_CAMOB)
2879 return OPERATOR_PASS_THROUGH;
2881 /* get border select values using rna */
2882 WM_operator_properties_border_to_rcti(op, &rect);
2884 /* calculate range */
2886 if (rv3d->persp == RV3D_CAMOB) {
2887 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &vb, FALSE);
2896 border.xmin = ((float)rect.xmin - vb.xmin) / BLI_rctf_size_x(&vb);
2897 border.ymin = ((float)rect.ymin - vb.ymin) / BLI_rctf_size_y(&vb);
2898 border.xmax = ((float)rect.xmax - vb.xmin) / BLI_rctf_size_x(&vb);
2899 border.ymax = ((float)rect.ymax - vb.ymin) / BLI_rctf_size_y(&vb);
2901 /* actually set border */
2902 CLAMP(border.xmin, 0.0f, 1.0f);
2903 CLAMP(border.ymin, 0.0f, 1.0f);
2904 CLAMP(border.xmax, 0.0f, 1.0f);
2905 CLAMP(border.ymax, 0.0f, 1.0f);
2907 if (rv3d->persp == RV3D_CAMOB) {
2908 scene->r.border = border;
2910 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, NULL);
2913 v3d->render_border = border;
2915 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
2918 /* drawing a border surrounding the entire camera view switches off border rendering
2919 * or the border covers no pixels */
2920 if ((border.xmin <= 0.0f && border.xmax >= 1.0f &&
2921 border.ymin <= 0.0f && border.ymax >= 1.0f) ||
2922 (border.xmin == border.xmax || border.ymin == border.ymax))
2924 if (rv3d->persp == RV3D_CAMOB)
2925 scene->r.mode &= ~R_BORDER;
2927 v3d->flag2 &= ~V3D_RENDER_BORDER;
2930 if (rv3d->persp == RV3D_CAMOB)
2931 scene->r.mode |= R_BORDER;
2933 v3d->flag2 |= V3D_RENDER_BORDER;
2936 return OPERATOR_FINISHED;
2940 void VIEW3D_OT_render_border(wmOperatorType *ot)
2943 ot->name = "Set Render Border";
2944 ot->description = "Set the boundaries of the border render and enable border render";
2945 ot->idname = "VIEW3D_OT_render_border";
2948 ot->invoke = WM_border_select_invoke;
2949 ot->exec = render_border_exec;
2950 ot->modal = WM_border_select_modal;
2951 ot->cancel = WM_border_select_cancel;
2953 ot->poll = ED_operator_view3d_active;
2956 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2959 WM_operator_properties_border(ot);
2961 RNA_def_boolean(ot->srna, "camera_only", 0, "Camera Only", "Set render border for camera view and final render only");
2964 /* ********************* Clear render border operator ****************** */
2966 static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op))
2968 View3D *v3d = CTX_wm_view3d(C);
2969 RegionView3D *rv3d = ED_view3d_context_rv3d(C);
2971 Scene *scene = CTX_data_scene(C);
2972 rctf *border = NULL;
2974 if (rv3d->persp == RV3D_CAMOB) {
2975 scene->r.mode &= ~R_BORDER;
2976 border = &scene->r.border;
2978 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, NULL);
2981 v3d->flag2 &= ~V3D_RENDER_BORDER;
2982 border = &v3d->render_border;
2984 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
2987 border->xmin = 0.0f;
2988 border->ymin = 0.0f;
2989 border->xmax = 1.0f;
2990 border->ymax = 1.0f;
2992 return OPERATOR_FINISHED;
2996 void VIEW3D_OT_clear_render_border(wmOperatorType *ot)
2999 ot->name = "Clear Render Border";
3000 ot->description = "Clear the boundaries of the border render and disable border render";
3001 ot->idname = "VIEW3D_OT_clear_render_border";
3004 ot->exec = clear_render_border_exec;
3005 ot->poll = ED_operator_view3d_active;
3008 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3011 /* ********************* Border Zoom operator ****************** */
3013 static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
3015 ARegion *ar = CTX_wm_region(C);
3016 View3D *v3d = CTX_wm_view3d(C);
3017 RegionView3D *rv3d = CTX_wm_region_view3d(C);
3018 Scene *scene = CTX_data_scene(C);
3021 /* Zooms in on a border drawn by the user */
3023 float dvec[3], vb[2], xscale, yscale;
3024 float dist_range_min;
3030 /* ZBuffer depth vars */
3032 float depth_close = FLT_MAX;
3033 double cent[2], p[3];
3035 /* note; otherwise opengl won't work */
3036 view3d_operator_needs_opengl(C);
3038 /* get border select values using rna */
3039 WM_operator_properties_border_to_rcti(op, &rect);
3041 /* check if zooming in/out view */
3042 gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
3044 /* Get Z Depths, needed for perspective, nice for ortho */
3045 bgl_get_mats(&mats);
3046 draw_depth(scene, ar, v3d, NULL);
3049 /* avoid allocating the whole depth buffer */
3050 ViewDepths depth_temp = {0};
3052 /* avoid view3d_update_depths() for speed. */
3053 view3d_update_depths_rect(ar, &depth_temp, &rect);
3055 /* find the closest Z pixel */
3056 depth_close = view3d_depth_near(&depth_temp);
3058 MEM_freeN(depth_temp.depths);
3061 cent[0] = (((double)rect.xmin) + ((double)rect.xmax)) / 2;
3062 cent[1] = (((double)rect.ymin) + ((double)rect.ymax)) / 2;
3064 if (rv3d->is_persp) {
3067 /* no depths to use, we cant do anything! */
3068 if (depth_close == FLT_MAX) {