4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Blender Foundation
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file blender/editors/space_view3d/view3d_edit.c
39 #include "DNA_armature_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_scene_types.h"
42 #include "DNA_camera_types.h"
43 #include "DNA_lamp_types.h"
45 #include "MEM_guardedalloc.h"
47 #include "BLI_blenlib.h"
50 #include "BLI_utildefines.h"
52 #include "BKE_context.h"
53 #include "BKE_image.h"
54 #include "BKE_library.h"
55 #include "BKE_object.h"
56 #include "BKE_paint.h"
57 #include "BKE_report.h"
58 #include "BKE_scene.h"
59 #include "BKE_screen.h"
60 #include "BKE_depsgraph.h" /* for ED_view3d_camera_lock_sync */
64 #include "BIF_glutil.h"
69 #include "RNA_access.h"
70 #include "RNA_define.h"
72 #include "ED_particle.h"
73 #include "ED_screen.h"
74 #include "ED_transform.h"
76 #include "ED_view3d.h"
79 #include "PIL_time.h" /* smoothview */
81 #include "view3d_intern.h" // own include
83 /* ********************** view3d_edit: view manipulations ********************* */
85 void ED_view3d_camera_lock_init(View3D *v3d, RegionView3D *rv3d)
87 if(v3d->camera && (v3d->flag2 & V3D_LOCK_CAMERA) && (rv3d->persp==RV3D_CAMOB)) {
88 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
92 void ED_view3d_camera_lock_sync(View3D *v3d, RegionView3D *rv3d)
94 if(v3d->camera && (v3d->flag2 & V3D_LOCK_CAMERA) && (rv3d->persp==RV3D_CAMOB)) {
95 ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
96 DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
97 WM_main_add_notifier(NC_OBJECT|ND_TRANSFORM, v3d->camera);
102 /* ********************* box view support ***************** */
104 static void view3d_boxview_clip(ScrArea *sa)
107 BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb");
109 float x1= 0.0f, y1= 0.0f, z1= 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f};
112 /* create bounding box */
113 for(ar= sa->regionbase.first; ar; ar= ar->next) {
114 if(ar->regiontype==RGN_TYPE_WINDOW) {
115 RegionView3D *rv3d= ar->regiondata;
117 if(rv3d->viewlock & RV3D_BOXCLIP) {
118 if(ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
119 if(ar->winx>ar->winy) x1= rv3d->dist;
120 else x1= ar->winx*rv3d->dist/ar->winy;
122 if(ar->winx>ar->winy) y1= ar->winy*rv3d->dist/ar->winx;
124 copy_v2_v2(ofs, rv3d->ofs);
126 else if(ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
127 ofs[2]= rv3d->ofs[2];
129 if(ar->winx>ar->winy) z1= ar->winy*rv3d->dist/ar->winx;
136 for(val=0; val<8; val++) {
137 if(ELEM4(val, 0, 3, 4, 7))
138 bb->vec[val][0]= -x1 - ofs[0];
140 bb->vec[val][0]= x1 - ofs[0];
142 if(ELEM4(val, 0, 1, 4, 5))
143 bb->vec[val][1]= -y1 - ofs[1];
145 bb->vec[val][1]= y1 - ofs[1];
148 bb->vec[val][2]= -z1 - ofs[2];
150 bb->vec[val][2]= z1 - ofs[2];
153 /* normals for plane equations */
154 normal_tri_v3( clip[0],bb->vec[0], bb->vec[1], bb->vec[4]);
155 normal_tri_v3( clip[1],bb->vec[1], bb->vec[2], bb->vec[5]);
156 normal_tri_v3( clip[2],bb->vec[2], bb->vec[3], bb->vec[6]);
157 normal_tri_v3( clip[3],bb->vec[3], bb->vec[0], bb->vec[7]);
158 normal_tri_v3( clip[4],bb->vec[4], bb->vec[5], bb->vec[6]);
159 normal_tri_v3( clip[5],bb->vec[0], bb->vec[2], bb->vec[1]);
161 /* then plane equations */
162 for(val=0; val<5; val++) {
163 clip[val][3]= - clip[val][0]*bb->vec[val][0] - clip[val][1]*bb->vec[val][1] - clip[val][2]*bb->vec[val][2];
165 clip[5][3]= - clip[5][0]*bb->vec[0][0] - clip[5][1]*bb->vec[0][1] - clip[5][2]*bb->vec[0][2];
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 rv3d->rflag |= RV3D_CLIPPING;
174 memcpy(rv3d->clip, clip, sizeof(clip));
175 if(rv3d->clipbb) MEM_freeN(rv3d->clipbb);
176 rv3d->clipbb= MEM_dupallocN(bb);
183 /* sync center/zoom view of region to others, for view transforms */
184 static void view3d_boxview_sync(ScrArea *sa, ARegion *ar)
187 RegionView3D *rv3d= ar->regiondata;
190 for(artest= sa->regionbase.first; artest; artest= artest->next) {
191 if(artest!=ar && artest->regiontype==RGN_TYPE_WINDOW) {
192 RegionView3D *rv3dtest= artest->regiondata;
194 if(rv3dtest->viewlock) {
195 rv3dtest->dist= rv3d->dist;
197 if( ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM) ) {
198 if( ELEM(rv3dtest->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK))
199 rv3dtest->ofs[0]= rv3d->ofs[0];
200 else if( ELEM(rv3dtest->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT))
201 rv3dtest->ofs[1]= rv3d->ofs[1];
203 else if( ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK) ) {
204 if( ELEM(rv3dtest->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM))
205 rv3dtest->ofs[0]= rv3d->ofs[0];
206 else if( ELEM(rv3dtest->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT))
207 rv3dtest->ofs[2]= rv3d->ofs[2];
209 else if( ELEM(rv3d->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT) ) {
210 if( ELEM(rv3dtest->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM))
211 rv3dtest->ofs[1]= rv3d->ofs[1];
212 if( ELEM(rv3dtest->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK))
213 rv3dtest->ofs[2]= rv3d->ofs[2];
216 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
218 ED_region_tag_redraw(artest);
224 view3d_boxview_clip(sa);
228 /* for home, center etc */
229 void view3d_boxview_copy(ScrArea *sa, ARegion *ar)
232 RegionView3D *rv3d= ar->regiondata;
235 for(artest= sa->regionbase.first; artest; artest= artest->next) {
236 if(artest!=ar && artest->regiontype==RGN_TYPE_WINDOW) {
237 RegionView3D *rv3dtest= artest->regiondata;
239 if(rv3dtest->viewlock) {
240 rv3dtest->dist= rv3d->dist;
241 copy_v3_v3(rv3dtest->ofs, rv3d->ofs);
242 ED_region_tag_redraw(artest);
244 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
250 view3d_boxview_clip(sa);
254 /* 'clip' is used to know if our clip setting has changed */
255 void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, short do_clip)
257 ARegion *arsync= NULL;
258 RegionView3D *rv3d= ar->regiondata;
260 /* this function copies flags from the first of the 3 other quadview
261 regions to the 2 other, so it assumes this is the region whose
262 properties are always being edited, weak */
263 viewlock= rv3d->viewlock;
265 if((viewlock & RV3D_LOCKED)==0)
267 else if((viewlock & RV3D_BOXVIEW)==0) {
268 viewlock &= ~RV3D_BOXCLIP;
272 for(; ar; ar= ar->prev) {
273 if(ar->alignment==RGN_ALIGN_QSPLIT) {
274 rv3d= ar->regiondata;
275 rv3d->viewlock= viewlock;
277 if(do_clip && (viewlock & RV3D_BOXCLIP)==0) {
278 rv3d->rflag &= ~RV3D_BOXCLIP;
281 /* use arsync so we sync with one of the aligned views below
282 * else the view jumps on changing view settings like 'clip'
283 * since it copies from the perspective view */
288 if(rv3d->viewlock & RV3D_BOXVIEW) {
289 view3d_boxview_copy(sa, arsync ? arsync : sa->regionbase.last);
292 ED_area_tag_redraw(sa);
295 /* ************************** init for view ops **********************************/
297 typedef struct ViewOpsData {
303 /* needed for continuous zoom */
305 double timer_lastdraw;
309 float mousevec[3]; /* dolly only */
310 float reverse, dist0;
312 short axis_snap; /* view rotate only */
314 /* use for orbit selection and auto-dist */
315 float ofs[3], dyn_ofs[3];
318 int origx, origy, oldx, oldy;
319 int origkey; /* the key that triggered the operator */
323 #define TRACKBALLSIZE (1.1)
325 static void calctrackballvec(rcti *rect, int mx, int my, float *vec)
327 float x, y, radius, d, z, t;
329 radius= TRACKBALLSIZE;
331 /* normalize x and y */
332 x= (rect->xmax + rect->xmin)/2 - mx;
333 x/= (float)((rect->xmax - rect->xmin)/4);
334 y= (rect->ymax + rect->ymin)/2 - my;
335 y/= (float)((rect->ymax - rect->ymin)/2);
338 if (d < radius * (float)M_SQRT1_2) /* Inside sphere */
339 z = sqrt(radius*radius - d*d);
342 t = radius / (float)M_SQRT2;
348 vec[2]= -z; /* yah yah! */
352 static void viewops_data_create(bContext *C, wmOperator *op, wmEvent *event)
354 static float lastofs[3] = {0,0,0};
356 ViewOpsData *vod= MEM_callocN(sizeof(ViewOpsData), "viewops data");
360 vod->sa= CTX_wm_area(C);
361 vod->ar= CTX_wm_region(C);
362 vod->v3d= vod->sa->spacedata.first;
363 vod->rv3d= rv3d= vod->ar->regiondata;
365 /* set the view from the camera, if view locking is enabled.
366 * we may want to make this optional but for now its needed always */
367 ED_view3d_camera_lock_init(vod->v3d, vod->rv3d);
369 vod->dist0= rv3d->dist;
370 copy_qt_qt(vod->oldquat, rv3d->viewquat);
371 vod->origx= vod->oldx= event->x;
372 vod->origy= vod->oldy= event->y;
373 vod->origkey= event->type; /* the key that triggered the operator. */
374 vod->use_dyn_ofs= (U.uiflag & USER_ORBIT_SELECTION) ? 1:0;
375 copy_v3_v3(vod->ofs, rv3d->ofs);
377 if (vod->use_dyn_ofs) {
378 /* If there's no selection, lastofs is unmodified and last value since static */
379 calculateTransformCenter(C, V3D_CENTROID, lastofs);
380 negate_v3_v3(vod->dyn_ofs, lastofs);
382 else if (U.uiflag & USER_ORBIT_ZBUF) {
384 view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
386 if((vod->use_dyn_ofs=ED_view3d_autodist(CTX_data_scene(C), vod->ar, vod->v3d, event->mval, vod->dyn_ofs))) {
387 if (rv3d->is_persp) {
388 float my_origin[3]; /* original G.vd->ofs */
389 float my_pivot[3]; /* view */
392 // locals for dist correction
396 negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
398 /* Set the dist value to be the distance from this 3d point */
399 /* this means youll always be able to zoom into it and panning wont go bad when dist was zero */
401 /* remove dist value */
402 upvec[0] = upvec[1] = 0;
403 upvec[2] = rv3d->dist;
404 copy_m3_m4(mat, rv3d->viewinv);
406 mul_m3_v3(mat, upvec);
407 sub_v3_v3v3(my_pivot, rv3d->ofs, upvec);
408 negate_v3(my_pivot); /* ofs is flipped */
410 /* find a new ofs value that is allong the view axis (rather then the mouse location) */
411 closest_to_line_v3(dvec, vod->dyn_ofs, my_pivot, my_origin);
412 vod->dist0 = rv3d->dist = len_v3v3(my_pivot, dvec);
414 negate_v3_v3(rv3d->ofs, dvec);
416 negate_v3(vod->dyn_ofs);
417 copy_v3_v3(vod->ofs, rv3d->ofs);
424 VECCOPY2D(mval_f, event->mval);
425 ED_view3d_win_to_vector(vod->ar, mval_f, vod->mousevec);
428 /* lookup, we dont pass on v3d to prevent confusement */
429 vod->grid= vod->v3d->grid;
430 vod->far= vod->v3d->far;
432 calctrackballvec(&vod->ar->winrct, event->x, event->y, vod->trackvec);
434 initgrabz(rv3d, -rv3d->ofs[0], -rv3d->ofs[1], -rv3d->ofs[2]);
437 if (rv3d->persmat[2][1] < 0.0f)
440 rv3d->rflag |= RV3D_NAVIGATING;
443 static void viewops_data_free(bContext *C, wmOperator *op)
446 Paint *p = paint_get_active(CTX_data_scene(C));
449 ViewOpsData *vod= op->customdata;
451 vod->rv3d->rflag &= ~RV3D_NAVIGATING;
454 WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
457 op->customdata= NULL;
460 ar= CTX_wm_region(C);
463 if(p && (p->flags & PAINT_FAST_NAVIGATE))
464 ED_region_tag_redraw(ar);
467 /* ************************** viewrotate **********************************/
469 static const float thres = 0.93f; //cos(20 deg);
471 #define COS45 0.70710678118654746
474 static float snapquats[39][5] = {
475 /*{q0, q1, q3, q4, view}*/
476 {COS45, -SIN45, 0.0, 0.0, RV3D_VIEW_FRONT}, //front
477 {0.0, 0.0, -SIN45, -SIN45, RV3D_VIEW_BACK}, //back
478 {1.0, 0.0, 0.0, 0.0, RV3D_VIEW_TOP}, //top
479 {0.0, -1.0, 0.0, 0.0, RV3D_VIEW_BOTTOM}, //bottom
480 {0.5, -0.5, -0.5, -0.5, RV3D_VIEW_RIGHT}, //left
481 {0.5, -0.5, 0.5, 0.5, RV3D_VIEW_LEFT}, //right
483 /* some more 45 deg snaps */
484 {0.65328145027160645, -0.65328145027160645, 0.27059805393218994, 0.27059805393218994, 0},
485 {0.92387950420379639, 0.0, 0.0, 0.38268342614173889, 0},
486 {0.0, -0.92387950420379639, 0.38268342614173889, 0.0, 0},
487 {0.35355335474014282, -0.85355335474014282, 0.35355338454246521, 0.14644660055637360, 0},
488 {0.85355335474014282, -0.35355335474014282, 0.14644660055637360, 0.35355338454246521, 0},
489 {0.49999994039535522, -0.49999994039535522, 0.49999997019767761, 0.49999997019767761, 0},
490 {0.27059802412986755, -0.65328145027160645, 0.65328145027160645, 0.27059802412986755, 0},
491 {0.65328145027160645, -0.27059802412986755, 0.27059802412986755, 0.65328145027160645, 0},
492 {0.27059799432754517, -0.27059799432754517, 0.65328139066696167, 0.65328139066696167, 0},
493 {0.38268336653709412, 0.0, 0.0, 0.92387944459915161, 0},
494 {0.0, -0.38268336653709412, 0.92387944459915161, 0.0, 0},
495 {0.14644658565521240, -0.35355335474014282, 0.85355335474014282, 0.35355335474014282, 0},
496 {0.35355335474014282, -0.14644658565521240, 0.35355335474014282, 0.85355335474014282, 0},
497 {0.0, 0.0, 0.92387944459915161, 0.38268336653709412, 0},
498 {-0.0, 0.0, 0.38268336653709412, 0.92387944459915161, 0},
499 {-0.27059802412986755, 0.27059802412986755, 0.65328133106231689, 0.65328133106231689, 0},
500 {-0.38268339633941650, 0.0, 0.0, 0.92387938499450684, 0},
501 {0.0, 0.38268339633941650, 0.92387938499450684, 0.0, 0},
502 {-0.14644658565521240, 0.35355338454246521, 0.85355329513549805, 0.35355332493782043, 0},
503 {-0.35355338454246521, 0.14644658565521240, 0.35355332493782043, 0.85355329513549805, 0},
504 {-0.49999991059303284, 0.49999991059303284, 0.49999985098838806, 0.49999985098838806, 0},
505 {-0.27059799432754517, 0.65328145027160645, 0.65328139066696167, 0.27059799432754517, 0},
506 {-0.65328145027160645, 0.27059799432754517, 0.27059799432754517, 0.65328139066696167, 0},
507 {-0.65328133106231689, 0.65328133106231689, 0.27059793472290039, 0.27059793472290039, 0},
508 {-0.92387932538986206, 0.0, 0.0, 0.38268333673477173, 0},
509 {0.0, 0.92387932538986206, 0.38268333673477173, 0.0, 0},
510 {-0.35355329513549805, 0.85355329513549805, 0.35355329513549805, 0.14644657075405121, 0},
511 {-0.85355329513549805, 0.35355329513549805, 0.14644657075405121, 0.35355329513549805, 0},
512 {-0.38268330693244934, 0.92387938499450684, 0.0, 0.0, 0},
513 {-0.92387938499450684, 0.38268330693244934, 0.0, 0.0, 0},
514 {-COS45, 0.0, 0.0, SIN45, 0},
515 {COS45, 0.0, 0.0, SIN45, 0},
516 {0.0, 0.0, 0.0, 1.0, 0}
525 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
526 #define VIEW_MODAL_CONFIRM 1 /* used for all view operations */
527 #define VIEWROT_MODAL_AXIS_SNAP_ENABLE 2
528 #define VIEWROT_MODAL_AXIS_SNAP_DISABLE 3
529 #define VIEWROT_MODAL_SWITCH_ZOOM 4
530 #define VIEWROT_MODAL_SWITCH_MOVE 5
531 #define VIEWROT_MODAL_SWITCH_ROTATE 6
533 /* called in transform_ops.c, on each regeneration of keymaps */
534 void viewrotate_modal_keymap(wmKeyConfig *keyconf)
536 static EnumPropertyItem modal_items[] = {
537 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
539 {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""},
540 {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""},
542 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
543 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
545 {0, NULL, 0, NULL, NULL}};
547 wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Rotate Modal");
549 /* this function is called for each spacetype, only needs to add map once */
552 keymap= WM_modalkeymap_add(keyconf, "View3D Rotate Modal", modal_items);
554 /* items for modal map */
555 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
556 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
558 WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_ENABLE);
559 WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_AXIS_SNAP_DISABLE);
561 /* disabled mode switching for now, can re-implement better, later on
562 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
563 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
564 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
567 /* assign map to operators */
568 WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
572 static void viewrotate_apply(ViewOpsData *vod, int x, int y)
574 RegionView3D *rv3d= vod->rv3d;
576 rv3d->view= RV3D_VIEW_USER; /* need to reset everytime because of view snapping */
578 if (U.flag & USER_TRACKBALL) {
579 float phi, si, q1[4], dvec[3], newvec[3];
581 calctrackballvec(&vod->ar->winrct, x, y, newvec);
583 sub_v3_v3v3(dvec, newvec, vod->trackvec);
585 si= sqrt(dvec[0]*dvec[0]+ dvec[1]*dvec[1]+ dvec[2]*dvec[2]);
586 si /= (float)(2.0 * TRACKBALLSIZE);
588 cross_v3_v3v3(q1+1, vod->trackvec, newvec);
591 /* Allow for rotation beyond the interval
596 /* This relation is used instead of
597 * phi = asin(si) so that the angle
598 * of rotation is linearly proportional
599 * to the distance that the mouse is
601 phi = si * (float)(M_PI / 2.0);
604 mul_v3_fl(q1+1, sin(phi));
605 mul_qt_qtqt(rv3d->viewquat, q1, vod->oldquat);
607 if (vod->use_dyn_ofs) {
608 /* compute the post multiplication quat, to rotate the offset correctly */
609 copy_qt_qt(q1, vod->oldquat);
611 mul_qt_qtqt(q1, q1, rv3d->viewquat);
613 conjugate_qt(q1); /* conj == inv for unit quat */
614 copy_v3_v3(rv3d->ofs, vod->ofs);
615 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
616 mul_qt_v3(q1, rv3d->ofs);
617 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
621 /* New turntable view code by John Aughey */
625 float xvec[3] = {1.0f, 0.0f, 0.0f};
626 /* Sensitivity will control how fast the viewport rotates. 0.0035 was
627 obtained experimentally by looking at viewport rotation sensitivities
628 on other modeling programs. */
629 /* Perhaps this should be a configurable user parameter. */
630 const float sensitivity = 0.0035f;
632 /* Get the 3x3 matrix and its inverse from the quaternion */
633 quat_to_mat3( m,rv3d->viewquat);
634 invert_m3_m3(m_inv,m);
636 /* Determine the direction of the x vector (for rotating up and down) */
637 /* This can likely be computed directly from the quaternion. */
638 mul_m3_v3(m_inv,xvec);
640 /* Perform the up/down rotation */
641 phi = sensitivity * -(y - vod->oldy);
643 mul_v3_v3fl(q1+1, xvec, sin(phi));
644 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
646 if (vod->use_dyn_ofs) {
647 conjugate_qt(q1); /* conj == inv for unit quat */
648 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
649 mul_qt_v3(q1, rv3d->ofs);
650 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
653 /* Perform the orbital rotation */
654 phi = sensitivity * vod->reverse * (x - vod->oldx);
658 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
660 if (vod->use_dyn_ofs) {
662 sub_v3_v3(rv3d->ofs, vod->dyn_ofs);
663 mul_qt_v3(q1, rv3d->ofs);
664 add_v3_v3(rv3d->ofs, vod->dyn_ofs);
668 /* check for view snap */
671 float viewquat_inv[4];
672 float zaxis[3]={0,0,1};
673 invert_qt_qt(viewquat_inv, rv3d->viewquat);
675 mul_qt_v3(viewquat_inv, zaxis);
677 for (i = 0 ; i < 39; i++){
679 float view = (int)snapquats[i][4];
680 float viewquat_inv_test[4];
681 float zaxis_test[3]={0,0,1};
683 invert_qt_qt(viewquat_inv_test, snapquats[i]);
684 mul_qt_v3(viewquat_inv_test, zaxis_test);
686 if(angle_v3v3(zaxis_test, zaxis) < DEG2RADF(45/3)) {
687 /* find the best roll */
688 float quat_roll[4], quat_final[4], quat_best[4];
689 float viewquat_align[4]; /* viewquat aligned to zaxis_test */
690 float viewquat_align_inv[4]; /* viewquat aligned to zaxis_test */
691 float best_angle = FLT_MAX;
694 /* viewquat_align is the original viewquat aligned to the snapped axis
695 * for testing roll */
696 rotation_between_vecs_to_quat(viewquat_align, zaxis_test, zaxis);
697 normalize_qt(viewquat_align);
698 mul_qt_qtqt(viewquat_align, rv3d->viewquat, viewquat_align);
699 normalize_qt(viewquat_align);
700 invert_qt_qt(viewquat_align_inv, viewquat_align);
703 for(j= 0; j<8; j++) {
705 float xaxis1[3]={1,0,0};
706 float xaxis2[3]={1,0,0};
707 float quat_final_inv[4];
709 axis_angle_to_quat(quat_roll, zaxis_test, (float)j * DEG2RADF(45.0f));
710 normalize_qt(quat_roll);
712 mul_qt_qtqt(quat_final, snapquats[i], quat_roll);
713 normalize_qt(quat_final);
715 /* compare 2 vector angles to find the least roll */
716 invert_qt_qt(quat_final_inv, quat_final);
717 mul_qt_v3(viewquat_align_inv, xaxis1);
718 mul_qt_v3(quat_final_inv, xaxis2);
719 angle= angle_v3v3(xaxis1, xaxis2);
721 if(angle <= best_angle) {
723 copy_qt_qt(quat_best, quat_final);
724 if(j) view= 0; /* view grid assumes certain up axis */
728 copy_qt_qt(rv3d->viewquat, quat_best);
729 rv3d->view= view; /* if we snap to a rolled camera the grid is invalid */
738 /* avoid precision loss over time */
739 normalize_qt(rv3d->viewquat);
741 ED_view3d_camera_lock_sync(vod->v3d, rv3d);
743 ED_region_tag_redraw(vod->ar);
746 static int viewrotate_modal(bContext *C, wmOperator *op, wmEvent *event)
748 ViewOpsData *vod= op->customdata;
749 short event_code= VIEW_PASS;
751 /* execute the events */
752 if(event->type==MOUSEMOVE) {
753 event_code= VIEW_APPLY;
755 else if(event->type==EVT_MODAL_MAP) {
756 switch (event->val) {
757 case VIEW_MODAL_CONFIRM:
758 event_code= VIEW_CONFIRM;
760 case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
761 vod->axis_snap= TRUE;
762 event_code= VIEW_APPLY;
764 case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
765 vod->axis_snap= FALSE;
766 event_code= VIEW_APPLY;
768 case VIEWROT_MODAL_SWITCH_ZOOM:
769 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
770 event_code= VIEW_CONFIRM;
772 case VIEWROT_MODAL_SWITCH_MOVE:
773 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
774 event_code= VIEW_CONFIRM;
778 else if(event->type==vod->origkey && event->val==KM_RELEASE) {
779 event_code= VIEW_CONFIRM;
782 if(event_code==VIEW_APPLY) {
783 viewrotate_apply(vod, event->x, event->y);
785 else if (event_code==VIEW_CONFIRM) {
786 ED_view3d_depth_tag_update(vod->rv3d);
787 viewops_data_free(C, op);
789 return OPERATOR_FINISHED;
792 return OPERATOR_RUNNING_MODAL;
795 static int viewrotate_invoke(bContext *C, wmOperator *op, wmEvent *event)
800 /* makes op->customdata */
801 viewops_data_create(C, op, event);
805 if(rv3d->viewlock) { /* poll should check but in some cases fails, see poll func for details */
806 viewops_data_free(C, op);
807 return OPERATOR_PASS_THROUGH;
810 /* switch from camera view when: */
811 if(rv3d->persp != RV3D_PERSP) {
813 if (U.uiflag & USER_AUTOPERSP) {
814 if(!((rv3d->persp==RV3D_CAMOB) && (vod->v3d->flag2 & V3D_LOCK_CAMERA))) {
815 rv3d->persp= RV3D_PERSP;
818 else if(rv3d->persp==RV3D_CAMOB) {
820 /* changed since 2.4x, use the camera view */
821 if(vod->v3d->camera) {
822 ED_view3d_from_object(vod->v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
825 if(!(vod->v3d->flag2 & V3D_LOCK_CAMERA)) {
826 rv3d->persp= rv3d->lpersp;
829 ED_region_tag_redraw(vod->ar);
832 if (event->type == MOUSEPAN) {
833 viewrotate_apply(vod, event->prevx, event->prevy);
834 ED_view3d_depth_tag_update(rv3d);
836 viewops_data_free(C, op);
838 return OPERATOR_FINISHED;
840 else if (event->type == MOUSEROTATE) {
841 /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
842 viewrotate_apply(vod, event->prevx, event->y);
843 ED_view3d_depth_tag_update(rv3d);
845 viewops_data_free(C, op);
847 return OPERATOR_FINISHED;
850 /* add temp handler */
851 WM_event_add_modal_handler(C, op);
853 return OPERATOR_RUNNING_MODAL;
857 static int view3d_camera_active_poll(bContext *C)
859 if(ED_operator_view3d_active(C)) {
860 RegionView3D *rv3d= CTX_wm_region_view3d(C);
861 if(rv3d && rv3d->persp==RV3D_CAMOB) {
869 void VIEW3D_OT_rotate(wmOperatorType *ot)
873 ot->name= "Rotate view";
874 ot->description = "Rotate the view";
875 ot->idname= "VIEW3D_OT_rotate";
878 ot->invoke= viewrotate_invoke;
879 ot->modal= viewrotate_modal;
880 ot->poll= ED_operator_region_view3d_active;
883 ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
886 /* ************************ viewmove ******************************** */
889 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
891 /* called in transform_ops.c, on each regeneration of keymaps */
892 void viewmove_modal_keymap(wmKeyConfig *keyconf)
894 static EnumPropertyItem modal_items[] = {
895 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
897 {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
898 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
900 {0, NULL, 0, NULL, NULL}};
902 wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Move Modal");
904 /* this function is called for each spacetype, only needs to add map once */
907 keymap= WM_modalkeymap_add(keyconf, "View3D Move Modal", modal_items);
909 /* items for modal map */
910 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
911 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
913 /* disabled mode switching for now, can re-implement better, later on
914 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
915 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
916 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
919 /* assign map to operators */
920 WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
924 static void viewmove_apply(ViewOpsData *vod, int x, int y)
926 if((vod->rv3d->persp==RV3D_CAMOB) && !(vod->v3d->flag2 & V3D_LOCK_CAMERA)) {
927 const float zoomfac= BKE_screen_view3d_zoom_to_fac((float)vod->rv3d->camzoom) * 2.0f;
928 vod->rv3d->camdx += (vod->oldx - x)/(vod->ar->winx * zoomfac);
929 vod->rv3d->camdy += (vod->oldy - y)/(vod->ar->winy * zoomfac);
930 CLAMP(vod->rv3d->camdx, -1.0f, 1.0f);
931 CLAMP(vod->rv3d->camdy, -1.0f, 1.0f);
937 mval_f[0]= x - vod->oldx;
938 mval_f[1]= y - vod->oldy;
939 ED_view3d_win_to_delta(vod->ar, mval_f, dvec);
941 add_v3_v3(vod->rv3d->ofs, dvec);
943 if(vod->rv3d->viewlock & RV3D_BOXVIEW)
944 view3d_boxview_sync(vod->sa, vod->ar);
950 ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
952 ED_region_tag_redraw(vod->ar);
956 static int viewmove_modal(bContext *C, wmOperator *op, wmEvent *event)
959 ViewOpsData *vod= op->customdata;
960 short event_code= VIEW_PASS;
962 /* execute the events */
963 if(event->type==MOUSEMOVE) {
964 event_code= VIEW_APPLY;
966 else if(event->type==EVT_MODAL_MAP) {
967 switch (event->val) {
968 case VIEW_MODAL_CONFIRM:
969 event_code= VIEW_CONFIRM;
971 case VIEWROT_MODAL_SWITCH_ZOOM:
972 WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL);
973 event_code= VIEW_CONFIRM;
975 case VIEWROT_MODAL_SWITCH_ROTATE:
976 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
977 event_code= VIEW_CONFIRM;
981 else if(event->type==vod->origkey && event->val==KM_RELEASE) {
982 event_code= VIEW_CONFIRM;
985 if(event_code==VIEW_APPLY) {
986 viewmove_apply(vod, event->x, event->y);
988 else if (event_code==VIEW_CONFIRM) {
989 ED_view3d_depth_tag_update(vod->rv3d);
991 viewops_data_free(C, op);
993 return OPERATOR_FINISHED;
996 return OPERATOR_RUNNING_MODAL;
999 static int viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event)
1003 /* makes op->customdata */
1004 viewops_data_create(C, op, event);
1005 vod= op->customdata;
1007 if (event->type == MOUSEPAN) {
1008 viewmove_apply(vod, event->prevx, event->prevy);
1009 ED_view3d_depth_tag_update(vod->rv3d);
1011 viewops_data_free(C, op);
1013 return OPERATOR_FINISHED;
1016 /* add temp handler */
1017 WM_event_add_modal_handler(C, op);
1019 return OPERATOR_RUNNING_MODAL;
1023 void VIEW3D_OT_move(wmOperatorType *ot)
1027 ot->name= "Move view";
1028 ot->description = "Move the view";
1029 ot->idname= "VIEW3D_OT_move";
1032 ot->invoke= viewmove_invoke;
1033 ot->modal= viewmove_modal;
1034 ot->poll= ED_operator_view3d_active;
1037 ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
1040 /* ************************ viewzoom ******************************** */
1042 /* viewdolly_modal_keymap has an exact copy of this, apply fixes to both */
1043 /* called in transform_ops.c, on each regeneration of keymaps */
1044 void viewzoom_modal_keymap(wmKeyConfig *keyconf)
1046 static EnumPropertyItem modal_items[] = {
1047 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
1049 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1050 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
1052 {0, NULL, 0, NULL, NULL}};
1054 wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Zoom Modal");
1056 /* this function is called for each spacetype, only needs to add map once */
1059 keymap= WM_modalkeymap_add(keyconf, "View3D Zoom Modal", modal_items);
1061 /* items for modal map */
1062 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1063 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1065 /* disabled mode switching for now, can re-implement better, later on
1066 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1067 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1068 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
1071 /* assign map to operators */
1072 WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
1075 static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my)
1077 RegionView3D *rv3d= ar->regiondata;
1079 if(U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
1085 int vb[2], mouseloc[2];
1087 mouseloc[0]= mx - ar->winrct.xmin;
1088 mouseloc[1]= my - ar->winrct.ymin;
1090 /* find the current window width and height */
1094 negate_v3_v3(tpos, rv3d->ofs);
1096 /* Project cursor position into 3D space */
1097 initgrabz(rv3d, tpos[0], tpos[1], tpos[2]);
1099 mval_f[0]= (mouseloc[0] - vb[0]) / 2.0f;
1100 mval_f[1]= (mouseloc[1] - vb[1]) / 2.0f;
1101 ED_view3d_win_to_delta(ar, mval_f, dvec);
1103 /* Calculate view target position for dolly */
1104 add_v3_v3v3(tvec, tpos, dvec);
1107 /* Offset to target position and dolly */
1108 new_dist = rv3d->dist * dfac;
1110 copy_v3_v3(rv3d->ofs, tvec);
1111 rv3d->dist = new_dist;
1113 /* Calculate final offset */
1114 madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
1121 static void viewzoom_apply(ViewOpsData *vod, int x, int y, const short viewzoom, const short zoom_invert)
1125 if(viewzoom==USER_ZOOM_CONT) {
1126 double time= PIL_check_seconds_timer();
1127 float time_step= (float)(time - vod->timer_lastdraw);
1130 if (U.uiflag & USER_ZOOM_HORIZ) {
1131 fac= (float)(x - vod->origx);
1134 fac= (float)(y - vod->origy);
1142 zfac = 1.0f + ((fac / 20.0f) * time_step);
1143 vod->timer_lastdraw= time;
1145 else if(viewzoom==USER_ZOOM_SCALE) {
1146 int ctr[2], len1, len2;
1147 // method which zooms based on how far you move the mouse
1149 ctr[0] = (vod->ar->winrct.xmax + vod->ar->winrct.xmin)/2;
1150 ctr[1] = (vod->ar->winrct.ymax + vod->ar->winrct.ymin)/2;
1152 len1 = (int)sqrt((ctr[0] - x)*(ctr[0] - x) + (ctr[1] - y)*(ctr[1] - y)) + 5;
1153 len2 = (int)sqrt((ctr[0] - vod->origx)*(ctr[0] - vod->origx) + (ctr[1] - vod->origy)*(ctr[1] - vod->origy)) + 5;
1155 zfac = vod->dist0 * ((float)len2/len1) / vod->rv3d->dist;
1157 else { /* USER_ZOOM_DOLLY */
1160 if (U.uiflag & USER_ZOOM_HORIZ) {
1161 len1 = (vod->ar->winrct.xmax - x) + 5;
1162 len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
1165 len1 = (vod->ar->winrct.ymax - y) + 5;
1166 len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
1169 SWAP(float, len1, len2);
1172 zfac = vod->dist0 * (2.0f * ((len2/len1)-1.0f) + 1.0f) / vod->rv3d->dist;
1175 if(zfac != 1.0f && zfac*vod->rv3d->dist > 0.001f * vod->grid &&
1176 zfac * vod->rv3d->dist < 10.0f * vod->far)
1177 view_zoom_mouseloc(vod->ar, zfac, vod->oldx, vod->oldy);
1179 /* these limits were in old code too */
1180 if(vod->rv3d->dist<0.001f * vod->grid) vod->rv3d->dist= 0.001f * vod->grid;
1181 if(vod->rv3d->dist>10.0f * vod->far) vod->rv3d->dist=10.0f * vod->far;
1183 if(vod->rv3d->viewlock & RV3D_BOXVIEW)
1184 view3d_boxview_sync(vod->sa, vod->ar);
1186 ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
1188 ED_region_tag_redraw(vod->ar);
1192 static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event)
1194 ViewOpsData *vod= op->customdata;
1195 short event_code= VIEW_PASS;
1197 /* execute the events */
1198 if (event->type == TIMER && event->customdata == vod->timer) {
1199 /* continuous zoom */
1200 event_code= VIEW_APPLY;
1202 else if(event->type==MOUSEMOVE) {
1203 event_code= VIEW_APPLY;
1205 else if(event->type==EVT_MODAL_MAP) {
1206 switch (event->val) {
1207 case VIEW_MODAL_CONFIRM:
1208 event_code= VIEW_CONFIRM;
1210 case VIEWROT_MODAL_SWITCH_MOVE:
1211 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
1212 event_code= VIEW_CONFIRM;
1214 case VIEWROT_MODAL_SWITCH_ROTATE:
1215 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
1216 event_code= VIEW_CONFIRM;
1220 else if(event->type==vod->origkey && event->val==KM_RELEASE) {
1221 event_code= VIEW_CONFIRM;
1224 if(event_code==VIEW_APPLY) {
1225 viewzoom_apply(vod, event->x, event->y, U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0);
1227 else if (event_code==VIEW_CONFIRM) {
1228 ED_view3d_depth_tag_update(vod->rv3d);
1229 viewops_data_free(C, op);
1231 return OPERATOR_FINISHED;
1234 return OPERATOR_RUNNING_MODAL;
1237 static int viewzoom_exec(bContext *C, wmOperator *op)
1245 int delta= RNA_int_get(op->ptr, "delta");
1248 if(op->customdata) {
1249 ViewOpsData *vod= op->customdata;
1256 ar= CTX_wm_region(C);
1259 v3d= sa->spacedata.first;
1260 rv3d= ar->regiondata;
1262 mx= RNA_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") : ar->winx / 2;
1263 my= RNA_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") : ar->winy / 2;
1265 use_cam_zoom= (rv3d->persp==RV3D_CAMOB) && !((v3d->flag2 & V3D_LOCK_CAMERA) && rv3d->is_persp);
1268 /* this min and max is also in viewmove() */
1271 if(rv3d->camzoom < RV3D_CAMZOOM_MIN) rv3d->camzoom= RV3D_CAMZOOM_MIN;
1273 else if(rv3d->dist < 10.0f * v3d->far) {
1274 view_zoom_mouseloc(ar, 1.2f, mx, my);
1280 if(rv3d->camzoom > RV3D_CAMZOOM_MAX) rv3d->camzoom= RV3D_CAMZOOM_MAX;
1282 else if(rv3d->dist> 0.001f * v3d->grid) {
1283 view_zoom_mouseloc(ar, .83333f, mx, my);
1287 if(rv3d->viewlock & RV3D_BOXVIEW)
1288 view3d_boxview_sync(sa, ar);
1290 ED_view3d_depth_tag_update(rv3d);
1292 ED_view3d_camera_lock_sync(v3d, rv3d);
1294 ED_region_tag_redraw(ar);
1296 viewops_data_free(C, op);
1298 return OPERATOR_FINISHED;
1301 /* this is an exact copy of viewzoom_modal_keymap */
1302 /* called in transform_ops.c, on each regeneration of keymaps */
1303 void viewdolly_modal_keymap(wmKeyConfig *keyconf)
1305 static EnumPropertyItem modal_items[] = {
1306 {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
1308 {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
1309 {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
1311 {0, NULL, 0, NULL, NULL}};
1313 wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Dolly Modal");
1315 /* this function is called for each spacetype, only needs to add map once */
1318 keymap= WM_modalkeymap_add(keyconf, "View3D Dolly Modal", modal_items);
1320 /* items for modal map */
1321 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1322 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM);
1324 /* disabled mode switching for now, can re-implement better, later on
1325 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1326 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
1327 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
1330 /* assign map to operators */
1331 WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
1334 /* viewdolly_invoke() copied this function, changes here may apply there */
1335 static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
1339 /* makes op->customdata */
1340 viewops_data_create(C, op, event);
1341 vod= op->customdata;
1343 /* if one or the other zoom position aren't set, set from event */
1344 if (!RNA_property_is_set(op->ptr, "mx") || !RNA_property_is_set(op->ptr, "my"))
1346 RNA_int_set(op->ptr, "mx", event->x);
1347 RNA_int_set(op->ptr, "my", event->y);
1350 if(RNA_property_is_set(op->ptr, "delta")) {
1351 viewzoom_exec(C, op);
1354 if (event->type == MOUSEZOOM) {
1355 /* Bypass Zoom invert flag for track pads (pass FALSE always) */
1357 if (U.uiflag & USER_ZOOM_HORIZ) {
1358 vod->origx = vod->oldx = event->x;
1359 viewzoom_apply(vod, event->prevx, event->prevy, USER_ZOOM_DOLLY, FALSE);
1362 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
1363 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
1364 viewzoom_apply(vod, event->prevx, event->prevy, USER_ZOOM_DOLLY, FALSE);
1366 ED_view3d_depth_tag_update(vod->rv3d);
1368 viewops_data_free(C, op);
1369 return OPERATOR_FINISHED;
1372 if(U.viewzoom == USER_ZOOM_CONT) {
1373 /* needs a timer to continue redrawing */
1374 vod->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
1375 vod->timer_lastdraw= PIL_check_seconds_timer();
1378 /* add temp handler */
1379 WM_event_add_modal_handler(C, op);
1381 return OPERATOR_RUNNING_MODAL;
1384 return OPERATOR_FINISHED;
1388 void VIEW3D_OT_zoom(wmOperatorType *ot)
1391 ot->name= "Zoom View";
1392 ot->description = "Zoom in/out in the view";
1393 ot->idname= "VIEW3D_OT_zoom";
1396 ot->invoke= viewzoom_invoke;
1397 ot->exec= viewzoom_exec;
1398 ot->modal= viewzoom_modal;
1399 ot->poll= ED_operator_region_view3d_active;
1402 ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
1404 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1405 RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
1406 RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
1410 /* ************************ viewdolly ******************************** */
1411 static void view_dolly_mouseloc(ARegion *ar, float orig_ofs[3], float dvec[3], float dfac)
1413 RegionView3D *rv3d= ar->regiondata;
1414 madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0 - dfac));
1417 static void viewdolly_apply(ViewOpsData *vod, int x, int y, const short zoom_invert)
1424 if (U.uiflag & USER_ZOOM_HORIZ) {
1425 len1 = (vod->ar->winrct.xmax - x) + 5;
1426 len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
1429 len1 = (vod->ar->winrct.ymax - y) + 5;
1430 len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
1433 SWAP(float, len1, len2);
1435 zfac = 1.0 + ((len2 - len1) * 0.01 * vod->rv3d->dist);
1439 view_dolly_mouseloc(vod->ar, vod->ofs, vod->mousevec, zfac);
1441 if(vod->rv3d->viewlock & RV3D_BOXVIEW)
1442 view3d_boxview_sync(vod->sa, vod->ar);
1444 ED_view3d_camera_lock_sync(vod->v3d, vod->rv3d);
1446 ED_region_tag_redraw(vod->ar);
1450 static int viewdolly_modal(bContext *C, wmOperator *op, wmEvent *event)
1452 ViewOpsData *vod= op->customdata;
1453 short event_code= VIEW_PASS;
1455 /* execute the events */
1456 if(event->type==MOUSEMOVE) {
1457 event_code= VIEW_APPLY;
1459 else if(event->type==EVT_MODAL_MAP) {
1460 switch (event->val) {
1461 case VIEW_MODAL_CONFIRM:
1462 event_code= VIEW_CONFIRM;
1464 case VIEWROT_MODAL_SWITCH_MOVE:
1465 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
1466 event_code= VIEW_CONFIRM;
1468 case VIEWROT_MODAL_SWITCH_ROTATE:
1469 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
1470 event_code= VIEW_CONFIRM;
1474 else if(event->type==vod->origkey && event->val==KM_RELEASE) {
1475 event_code= VIEW_CONFIRM;
1478 if(event_code==VIEW_APPLY) {
1479 viewdolly_apply(vod, event->x, event->y, (U.uiflag & USER_ZOOM_INVERT) != 0);
1481 else if (event_code==VIEW_CONFIRM) {
1482 ED_view3d_depth_tag_update(vod->rv3d);
1483 viewops_data_free(C, op);
1485 return OPERATOR_FINISHED;
1488 return OPERATOR_RUNNING_MODAL;
1491 static int viewdolly_exec(bContext *C, wmOperator *op)
1499 int delta= RNA_int_get(op->ptr, "delta");
1501 if(op->customdata) {
1502 ViewOpsData *vod= op->customdata;
1506 copy_v3_v3(mousevec, vod->mousevec);
1510 ar= CTX_wm_region(C);
1511 negate_v3_v3(mousevec, ((RegionView3D *)ar->regiondata)->viewinv[2]);
1512 normalize_v3(mousevec);
1515 /* v3d= sa->spacedata.first; */ /* UNUSED */
1516 rv3d= ar->regiondata;
1518 /* overwrite the mouse vector with the view direction (zoom into the center) */
1519 if((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
1520 normalize_v3_v3(mousevec, rv3d->viewinv[2]);
1524 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, 1.2f);
1527 view_dolly_mouseloc(ar, rv3d->ofs, mousevec, .83333f);
1530 if(rv3d->viewlock & RV3D_BOXVIEW)
1531 view3d_boxview_sync(sa, ar);
1533 ED_view3d_depth_tag_update(rv3d);
1534 ED_region_tag_redraw(ar);
1536 viewops_data_free(C, op);
1538 return OPERATOR_FINISHED;
1541 /* copied from viewzoom_invoke(), changes here may apply there */
1542 static int viewdolly_invoke(bContext *C, wmOperator *op, wmEvent *event)
1546 /* makes op->customdata */
1547 viewops_data_create(C, op, event);
1548 vod= op->customdata;
1550 /* if one or the other zoom position aren't set, set from event */
1551 if (!RNA_property_is_set(op->ptr, "mx") || !RNA_property_is_set(op->ptr, "my"))
1553 RNA_int_set(op->ptr, "mx", event->x);
1554 RNA_int_set(op->ptr, "my", event->y);
1557 if(RNA_property_is_set(op->ptr, "delta")) {
1558 viewdolly_exec(C, op);
1561 /* overwrite the mouse vector with the view direction (zoom into the center) */
1562 if((U.uiflag & USER_ZOOM_TO_MOUSEPOS) == 0) {
1563 negate_v3_v3(vod->mousevec, vod->rv3d->viewinv[2]);
1564 normalize_v3(vod->mousevec);
1567 if (event->type == MOUSEZOOM) {
1568 /* Bypass Zoom invert flag for track pads (pass FALSE always) */
1570 if (U.uiflag & USER_ZOOM_HORIZ) {
1571 vod->origx = vod->oldx = event->x;
1572 viewdolly_apply(vod, event->prevx, event->prevy, FALSE);
1576 /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
1577 vod->origy = vod->oldy = vod->origy + event->x - event->prevx;
1578 viewdolly_apply(vod, event->prevx, event->prevy, FALSE);
1580 ED_view3d_depth_tag_update(vod->rv3d);
1582 viewops_data_free(C, op);
1583 return OPERATOR_FINISHED;
1586 /* add temp handler */
1587 WM_event_add_modal_handler(C, op);
1589 return OPERATOR_RUNNING_MODAL;
1592 return OPERATOR_FINISHED;
1595 /* like ED_operator_region_view3d_active but check its not in ortho view */
1596 static int viewdolly_poll(bContext *C)
1598 RegionView3D *rv3d= CTX_wm_region_view3d(C);
1601 if (rv3d->persp == RV3D_PERSP) {
1605 View3D *v3d= CTX_wm_view3d(C);
1606 if ((rv3d->persp == RV3D_CAMOB) && (v3d->flag2 & V3D_LOCK_CAMERA)) {
1614 void VIEW3D_OT_dolly(wmOperatorType *ot)
1617 ot->name= "Dolly view";
1618 ot->description = "Dolly in/out in the view";
1619 ot->idname= "VIEW3D_OT_dolly";
1622 ot->invoke= viewdolly_invoke;
1623 ot->exec= viewdolly_exec;
1624 ot->modal= viewdolly_modal;
1625 ot->poll= viewdolly_poll;
1628 ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
1630 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1631 RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
1632 RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);
1637 static int view3d_all_exec(bContext *C, wmOperator *op) /* was view3d_home() in 2.4x */
1639 ARegion *ar= CTX_wm_region(C);
1640 View3D *v3d = CTX_wm_view3d(C);
1641 RegionView3D *rv3d= CTX_wm_region_view3d(C);
1642 Scene *scene= CTX_data_scene(C);
1645 const short skip_camera= ((rv3d->persp==RV3D_CAMOB) && (v3d->flag2 & V3D_LOCK_CAMERA));
1647 int center= RNA_boolean_get(op->ptr, "center");
1649 float size, min[3], max[3], afm[3];
1650 int ok= 1, onedone=0;
1653 /* in 2.4x this also move the cursor to (0, 0, 0) (with shift+c). */
1654 curs= give_cursor(scene, v3d);
1660 INIT_MINMAX(min, max);
1663 for(base= scene->base.first; base; base= base->next) {
1664 if(BASE_VISIBLE(v3d, base)) {
1667 if(skip_camera && base->object == v3d->camera) {
1671 minmax_object(base->object, min, max);
1675 ED_region_tag_redraw(ar);
1676 /* TODO - should this be cancel?
1677 * I think no, because we always move the cursor, with or without
1678 * object, but in this case there is no change in the scene,
1679 * only the cursor so I choice a ED_region_tag like
1680 * smooth_view do for the center_cursor.
1683 return OPERATOR_FINISHED;
1686 sub_v3_v3v3(afm, max, min);
1687 size= 0.7f*MAX3(afm[0], afm[1], afm[2]);
1688 if(size == 0.0f) ok= 0;
1695 new_ofs[0]= -(min[0]+max[0])/2.0f;
1696 new_ofs[1]= -(min[1]+max[1])/2.0f;
1697 new_ofs[2]= -(min[2]+max[2])/2.0f;
1699 // correction for window aspect ratio
1700 if(ar->winy>2 && ar->winx>2) {
1701 size= (float)ar->winx/(float)ar->winy;
1702 if(size < 1.0f) size= 1.0f/size;
1706 if ((rv3d->persp==RV3D_CAMOB) && !(v3d->flag2 & V3D_LOCK_CAMERA)) {
1707 rv3d->persp= RV3D_PERSP;
1708 smooth_view(C, v3d, ar, v3d->camera, NULL, new_ofs, NULL, &new_dist, NULL);
1711 smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, &new_dist, NULL);
1714 // XXX BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1716 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
1718 return OPERATOR_FINISHED;
1722 void VIEW3D_OT_view_all(wmOperatorType *ot)
1725 ot->name= "View All";
1726 ot->description = "View all objects in scene";
1727 ot->idname= "VIEW3D_OT_view_all";
1730 ot->exec= view3d_all_exec;
1731 ot->poll= ED_operator_region_view3d_active;
1736 RNA_def_boolean(ot->srna, "center", 0, "Center", "");
1740 static int viewselected_exec(bContext *C, wmOperator *UNUSED(op)) /* like a localview without local!, was centerview() in 2.4x */
1742 ARegion *ar= CTX_wm_region(C);
1743 View3D *v3d = CTX_wm_view3d(C);
1744 RegionView3D *rv3d= CTX_wm_region_view3d(C);
1745 Scene *scene= CTX_data_scene(C);
1747 Object *obedit= CTX_data_edit_object(C);
1748 float size, min[3], max[3], afm[3];
1749 int ok=0, ok_dist=1;
1750 const short skip_camera= ((rv3d->persp==RV3D_CAMOB) && (v3d->flag2 & V3D_LOCK_CAMERA));
1756 INIT_MINMAX(min, max);
1758 if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) {
1759 /* hardcoded exception, we look for the one selected armature */
1760 /* this is weak code this way, we should make a generic active/selection callback interface once... */
1762 for(base=scene->base.first; base; base= base->next) {
1763 if(TESTBASELIB(v3d, base)) {
1764 if(base->object->type==OB_ARMATURE)
1765 if(base->object->mode & OB_MODE_POSE)
1775 ok = minmax_verts(obedit, min, max); /* only selected */
1777 else if(ob && (ob->mode & OB_MODE_POSE)) {
1779 bArmature *arm= ob->data;
1780 bPoseChannel *pchan;
1783 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
1784 if(pchan->bone->flag & BONE_SELECTED) {
1785 if(pchan->bone->layer & arm->layer) {
1786 bPoseChannel *pchan_tx= pchan->custom_tx ? pchan->custom_tx : pchan;
1788 mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_head);
1789 DO_MINMAX(vec, min, max);
1790 mul_v3_m4v3(vec, ob->obmat, pchan_tx->pose_tail);
1791 DO_MINMAX(vec, min, max);
1797 else if (paint_facesel_test(ob)) {
1798 ok= paintface_minmax(ob, min, max);
1800 else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1801 ok= PE_minmax(scene, min, max);
1805 for(base= FIRSTBASE; base; base = base->next) {
1806 if(TESTBASE(v3d, base)) {
1808 if(skip_camera && base->object == v3d->camera) {
1812 /* account for duplis */
1813 if (minmax_object_duplis(scene, base->object, min, max)==0)
1814 minmax_object(base->object, min, max); /* use if duplis not found */
1821 if(ok==0) return OPERATOR_FINISHED;
1823 sub_v3_v3v3(afm, max, min);
1824 size= MAX3(afm[0], afm[1], afm[2]);
1826 if(!rv3d->is_persp) {
1827 if(size < 0.0001f) { /* if its a sinble point. dont even re-scale */
1831 /* perspective should be a bit farther away to look nice */
1836 if(size <= v3d->near*1.5f) {
1837 size= v3d->near*1.5f;
1841 add_v3_v3v3(new_ofs, min, max);
1842 mul_v3_fl(new_ofs, -0.5f);
1846 /* correction for window aspect ratio */
1847 if(ar->winy>2 && ar->winx>2) {
1848 size= (float)ar->winx/(float)ar->winy;
1849 if(size<1.0f) size= 1.0f/size;
1853 if (rv3d->persp==RV3D_CAMOB && !(v3d->flag2 & V3D_LOCK_CAMERA)) {
1854 rv3d->persp= RV3D_PERSP;
1855 smooth_view(C, v3d, ar, v3d->camera, NULL, new_ofs, NULL, &new_dist, NULL);
1858 smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, ok_dist ? &new_dist : NULL, NULL);
1861 /* smooth view does viewlock RV3D_BOXVIEW copy */
1863 // XXX BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1865 return OPERATOR_FINISHED;
1868 void VIEW3D_OT_view_selected(wmOperatorType *ot)
1872 ot->name= "View Selected";
1873 ot->description = "Move the view to the selection center";
1874 ot->idname= "VIEW3D_OT_view_selected";
1877 ot->exec= viewselected_exec;
1878 ot->poll= ED_operator_region_view3d_active;
1884 static int viewcenter_cursor_exec(bContext *C, wmOperator *UNUSED(op))
1886 View3D *v3d = CTX_wm_view3d(C);
1887 RegionView3D *rv3d= CTX_wm_region_view3d(C);
1888 Scene *scene= CTX_data_scene(C);
1891 ARegion *ar= CTX_wm_region(C);
1893 /* non camera center */
1895 negate_v3_v3(new_ofs, give_cursor(scene, v3d));
1896 smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, NULL, NULL);
1898 /* smooth view does viewlock RV3D_BOXVIEW copy */
1901 return OPERATOR_FINISHED;
1904 void VIEW3D_OT_view_center_cursor(wmOperatorType *ot)
1907 ot->name= "Center View to Cursor";
1908 ot->description= "Centers the view so that the cursor is in the middle of the view";
1909 ot->idname= "VIEW3D_OT_view_center_cursor";
1912 ot->exec= viewcenter_cursor_exec;
1913 ot->poll= ED_operator_view3d_active;
1919 static int view3d_center_camera_exec(bContext *C, wmOperator *UNUSED(op)) /* was view3d_home() in 2.4x */
1921 ARegion *ar= CTX_wm_region(C);
1922 RegionView3D *rv3d= CTX_wm_region_view3d(C);
1923 Scene *scene= CTX_data_scene(C);
1927 rv3d->camdx= rv3d->camdy= 0.0f;
1929 view3d_viewborder_size_get(scene, ar, size);
1931 /* 4px is just a little room from the edge of the area */
1932 xfac= (float)ar->winx / (float)(size[0] + 4);
1933 yfac= (float)ar->winy / (float)(size[1] + 4);
1935 rv3d->camzoom= BKE_screen_view3d_zoom_from_fac(MIN2(xfac, yfac));
1936 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
1938 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, CTX_wm_view3d(C));
1940 return OPERATOR_FINISHED;
1943 void VIEW3D_OT_view_center_camera(wmOperatorType *ot)
1946 ot->name= "View Camera Center";
1947 ot->description = "Center the camera view";
1948 ot->idname= "VIEW3D_OT_view_center_camera";
1951 ot->exec= view3d_center_camera_exec;
1952 ot->poll= view3d_camera_active_poll;
1958 /* ********************* Set render border operator ****************** */
1960 static int render_border_exec(bContext *C, wmOperator *op)
1962 View3D *v3d = CTX_wm_view3d(C);
1963 ARegion *ar= CTX_wm_region(C);
1964 RegionView3D *rv3d= ED_view3d_context_rv3d(C);
1965 Scene *scene= CTX_data_scene(C);
1970 /* get border select values using rna */
1971 rect.xmin= RNA_int_get(op->ptr, "xmin");
1972 rect.ymin= RNA_int_get(op->ptr, "ymin");
1973 rect.xmax= RNA_int_get(op->ptr, "xmax");
1974 rect.ymax= RNA_int_get(op->ptr, "ymax");
1976 /* calculate range */
1977 ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &vb, FALSE);
1979 scene->r.border.xmin= ((float)rect.xmin-vb.xmin)/(vb.xmax-vb.xmin);
1980 scene->r.border.ymin= ((float)rect.ymin-vb.ymin)/(vb.ymax-vb.ymin);
1981 scene->r.border.xmax= ((float)rect.xmax-vb.xmin)/(vb.xmax-vb.xmin);
1982 scene->r.border.ymax= ((float)rect.ymax-vb.ymin)/(vb.ymax-vb.ymin);
1984 /* actually set border */
1985 CLAMP(scene->r.border.xmin, 0.0f, 1.0f);
1986 CLAMP(scene->r.border.ymin, 0.0f, 1.0f);
1987 CLAMP(scene->r.border.xmax, 0.0f, 1.0f);
1988 CLAMP(scene->r.border.ymax, 0.0f, 1.0f);
1990 /* drawing a border surrounding the entire camera view switches off border rendering
1991 * or the border covers no pixels */
1992 if ((scene->r.border.xmin <= 0.0f && scene->r.border.xmax >= 1.0f &&
1993 scene->r.border.ymin <= 0.0f && scene->r.border.ymax >= 1.0f) ||
1994 (scene->r.border.xmin == scene->r.border.xmax ||
1995 scene->r.border.ymin == scene->r.border.ymax ))
1997 scene->r.mode &= ~R_BORDER;
1999 scene->r.mode |= R_BORDER;
2002 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS, NULL);
2004 return OPERATOR_FINISHED;
2008 void VIEW3D_OT_render_border(wmOperatorType *ot)
2011 ot->name= "Set Render Border";
2012 ot->description = "Set the boundaries of the border render and enables border render ";
2013 ot->idname= "VIEW3D_OT_render_border";
2016 ot->invoke= WM_border_select_invoke;
2017 ot->exec= render_border_exec;
2018 ot->modal= WM_border_select_modal;
2020 ot->poll= view3d_camera_active_poll;
2023 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2026 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2027 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2028 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2029 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2032 /* ********************* Border Zoom operator ****************** */
2034 static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
2036 ARegion *ar= CTX_wm_region(C);
2037 View3D *v3d = CTX_wm_view3d(C);
2038 RegionView3D *rv3d= CTX_wm_region_view3d(C);
2039 Scene *scene= CTX_data_scene(C);
2041 /* Zooms in on a border drawn by the user */
2043 float dvec[3], vb[2], xscale, yscale, scale;
2049 /* ZBuffer depth vars */
2051 float depth_close= FLT_MAX;
2052 double cent[2], p[3];
2054 /* note; otherwise opengl won't work */
2055 view3d_operator_needs_opengl(C);
2057 /* get border select values using rna */
2058 rect.xmin= RNA_int_get(op->ptr, "xmin");
2059 rect.ymin= RNA_int_get(op->ptr, "ymin");
2060 rect.xmax= RNA_int_get(op->ptr, "xmax");
2061 rect.ymax= RNA_int_get(op->ptr, "ymax");
2063 /* Get Z Depths, needed for perspective, nice for ortho */
2064 bgl_get_mats(&mats);
2065 draw_depth(scene, ar, v3d, NULL);
2068 /* avoid allocating the whole depth buffer */
2069 ViewDepths depth_temp= {0};
2071 /* avoid view3d_update_depths() for speed. */
2072 view3d_update_depths_rect(ar, &depth_temp, &rect);
2074 /* find the closest Z pixel */
2075 depth_close= view3d_depth_near(&depth_temp);
2077 MEM_freeN(depth_temp.depths);
2080 cent[0] = (((double)rect.xmin)+((double)rect.xmax)) / 2;
2081 cent[1] = (((double)rect.ymin)+((double)rect.ymax)) / 2;
2083 if (rv3d->is_persp) {
2086 /* no depths to use, we cant do anything! */
2087 if (depth_close==FLT_MAX){
2088 BKE_report(op->reports, RPT_ERROR, "Depth Too Large");
2089 return OPERATOR_CANCELLED;
2091 /* convert border to 3d coordinates */
2092 if (( !gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2])) ||
2093 ( !gluUnProject((double)rect.xmin, (double)rect.ymin, depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p_corner[0], &p_corner[1], &p_corner[2])))
2094 return OPERATOR_CANCELLED;
2096 dvec[0] = p[0]-p_corner[0];
2097 dvec[1] = p[1]-p_corner[1];
2098 dvec[2] = p[2]-p_corner[2];
2100 new_dist = len_v3(dvec);
2101 if(new_dist <= v3d->near * 1.5f) new_dist= v3d->near * 1.5f;
2107 } else { /* othographic */
2108 /* find the current window width and height */
2112 new_dist = rv3d->dist;
2114 /* convert the drawn rectangle into 3d space */
2115 if (depth_close!=FLT_MAX && gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2])) {
2122 /* We cant use the depth, fallback to the old way that dosnt set the center depth */
2123 copy_v3_v3(new_ofs, rv3d->ofs);
2125 initgrabz(rv3d, -new_ofs[0], -new_ofs[1], -new_ofs[2]);
2127 mval_f[0]= (rect.xmin + rect.xmax - vb[0]) / 2.0f;
2128 mval_f[1]= (rect.ymin + rect.ymax - vb[1]) / 2.0f;
2129 ED_view3d_win_to_delta(ar, mval_f, dvec);
2130 /* center the view to the center of the rectangle */
2131 sub_v3_v3(new_ofs, dvec);
2134 /* work out the ratios, so that everything selected fits when we zoom */
2135 xscale = ((rect.xmax-rect.xmin)/vb[0]);
2136 yscale = ((rect.ymax-rect.ymin)/vb[1]);
2137 scale = (xscale >= yscale)?xscale:yscale;
2139 /* zoom in as required, or as far as we can go */
2140 new_dist = ((new_dist*scale) >= 0.001f * v3d->grid)? new_dist*scale:0.001f * v3d->grid;
2143 smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, &new_dist, NULL);
2145 if(rv3d->viewlock & RV3D_BOXVIEW)
2146 view3d_boxview_sync(CTX_wm_area(C), ar);
2148 return OPERATOR_FINISHED;
2151 static int view3d_zoom_border_invoke(bContext *C, wmOperator *op, wmEvent *event)
2153 View3D *v3d= CTX_wm_view3d(C);
2154 RegionView3D *rv3d= CTX_wm_region_view3d(C);
2156 /* if in camera view do not exec the operator so we do not conflict with set render border*/
2157 if ((rv3d->persp != RV3D_CAMOB) || (v3d->flag2 & V3D_LOCK_CAMERA))
2158 return WM_border_select_invoke(C, op, event);
2160 return OPERATOR_PASS_THROUGH;
2163 void VIEW3D_OT_zoom_border(wmOperatorType *ot)
2166 ot->name= "Border Zoom";
2167 ot->description = "Zoom in the view to the nearest object contained in the border";
2168 ot->idname= "VIEW3D_OT_zoom_border";
2171 ot->invoke= view3d_zoom_border_invoke;
2172 ot->exec= view3d_zoom_border_exec;
2173 ot->modal= WM_border_select_modal;
2175 ot->poll= ED_operator_region_view3d_active;
2181 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2182 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2183 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2184 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2188 /* sets the view to 1:1 camera/render-pixel */
2189 static void view3d_set_1_to_1_viewborder(Scene *scene, ARegion *ar)
2191 RegionView3D *rv3d= ar->regiondata;
2193 int im_width= (scene->r.size*scene->r.xsch)/100;
2195 view3d_viewborder_size_get(scene, ar, size);
2197 rv3d->camzoom= BKE_screen_view3d_zoom_from_fac((float)im_width/size[0]);
2198 CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
2201 static int view3d_zoom_1_to_1_camera_exec(bContext *C, wmOperator *UNUSED(op))
2203 Scene *scene= CTX_data_scene(C);
2204 ARegion *ar= CTX_wm_region(C);
2206 view3d_set_1_to_1_viewborder(scene, ar);
2208 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, CTX_wm_view3d(C));
2210 return OPERATOR_FINISHED;
2213 void VIEW3D_OT_zoom_camera_1_to_1(wmOperatorType *ot)
2216 ot->name= "Zoom Camera 1:1";
2217 ot->description = "Match the camera to 1:1 to the render output";
2218 ot->idname= "VIEW3D_OT_zoom_camera_1_to_1";
2221 ot->exec= view3d_zoom_1_to_1_camera_exec;
2222 ot->poll= view3d_camera_active_poll;
2228 /* ********************* Changing view operator ****************** */
2230 static EnumPropertyItem prop_view_items[] = {
2231 {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View From the Front"},
2232 {RV3D_VIEW_BACK, "BACK", 0, "Back", "View From the Back"},
2233 {RV3D_VIEW_LEFT, "LEFT", 0, "Left", "View From the Left"},
2234 {RV3D_VIEW_RIGHT, "RIGHT", 0, "Right", "View From the Right"},
2235 {RV3D_VIEW_TOP, "TOP", 0, "Top", "View From the Top"},
2236 {RV3D_VIEW_BOTTOM, "BOTTOM", 0, "Bottom", "View From the Bottom"},
2237 {RV3D_VIEW_CAMERA, "CAMERA", 0, "Camera", "View From the active amera"},
2238 {0, NULL, 0, NULL, NULL}};
2241 /* would like to make this a generic function - outside of transform */
2243 static void axis_set_view(bContext *C, View3D *v3d, ARegion *ar, float q1, float q2, float q3, float q4, short view, int perspo, int align_active)
2245 RegionView3D *rv3d= ar->regiondata; /* no NULL check is needed, poll checks */
2248 new_quat[0]= q1; new_quat[1]= q2;
2249 new_quat[2]= q3; new_quat[3]= q4;
2250 normalize_qt(new_quat);
2253 /* align to active object */
2254 Object *obact= CTX_data_active_object(C);
2256 /* no active object, ignore this option */
2257 align_active= FALSE;
2260 float obact_quat[4];
2263 /* same as transform manipulator when normal is set */
2264 ED_getTransformOrientationMatrix(C, twmat, FALSE);
2266 mat3_to_quat( obact_quat,twmat);
2267 invert_qt(obact_quat);
2268 mul_qt_qtqt(new_quat, new_quat, obact_quat);
2270 rv3d->view= view= RV3D_VIEW_USER;
2274 if(align_active==FALSE) {
2275 /* normal operation */
2276 if(rv3d->viewlock) {
2277 /* only pass on if */
2278 if(rv3d->view==RV3D_VIEW_FRONT && view==RV3D_VIEW_BACK);
2279 else if(rv3d->view==RV3D_VIEW_BACK && view==RV3D_VIEW_FRONT);
2280 else if(rv3d->view==RV3D_VIEW_RIGHT && view==RV3D_VIEW_LEFT);
2281 else if(rv3d->view==RV3D_VIEW_LEFT && view==RV3D_VIEW_RIGHT);
2282 else if(rv3d->view==RV3D_VIEW_BOTTOM && view==RV3D_VIEW_TOP);
2283 else if(rv3d->view==RV3D_VIEW_TOP && view==RV3D_VIEW_BOTTOM);
2290 if(rv3d->viewlock) {
2291 ED_region_tag_redraw(ar);
2295 if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
2297 if (U.uiflag & USER_AUTOPERSP) rv3d->persp= view ? RV3D_ORTHO : RV3D_PERSP;
2298 else if(rv3d->persp==RV3D_CAMOB) rv3d->persp= perspo;
2300 smooth_view(C, v3d, ar, v3d->camera, NULL, rv3d->ofs, new_quat, NULL, NULL);
2304 if (U.uiflag & USER_AUTOPERSP) rv3d->persp= view ? RV3D_ORTHO : RV3D_PERSP;
2305 else if(rv3d->persp==RV3D_CAMOB) rv3d->persp= perspo;
2307 smooth_view(C, v3d, ar, NULL, NULL, NULL, new_quat, NULL, NULL);
2312 static int viewnumpad_exec(bContext *C, wmOperator *op)
2314 View3D *v3d = CTX_wm_view3d(C);
2315 ARegion *ar= ED_view3d_context_region_unlock(C);
2316 RegionView3D *rv3d= ar->regiondata; /* no NULL check is needed, poll checks */
2317 Scene *scene= CTX_data_scene(C);
2318 static int perspo=RV3D_PERSP;
2319 int viewnum, align_active, nextperspo;
2321 viewnum = RNA_enum_get(op->ptr, "type");
2322 align_active = RNA_boolean_get(op->ptr, "align_active");
2324 /* set this to zero, gets handled in axis_set_view */
2328 /* Use this to test if we started out with a camera */
2330 if (rv3d->persp == RV3D_CAMOB) {
2331 nextperspo= rv3d->lpersp;
2337 case RV3D_VIEW_BOTTOM :
2338 axis_set_view(C, v3d, ar, 0.0, -1.0, 0.0, 0.0, viewnum, nextperspo, align_active);
2341 case RV3D_VIEW_BACK:
2342 axis_set_view(C, v3d, ar, 0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0), viewnum, nextperspo, align_active);
2345 case RV3D_VIEW_LEFT:
2346 axis_set_view(C, v3d, ar, 0.5, -0.5, 0.5, 0.5, viewnum, nextperspo, align_active);
2350 axis_set_view(C, v3d, ar, 1.0, 0.0, 0.0, 0.0, viewnum, nextperspo, align_active);
2353 case RV3D_VIEW_FRONT:
2354 axis_set_view(C, v3d, ar, (float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0, viewnum, nextperspo, align_active);
2357 case RV3D_VIEW_RIGHT:
2358 axis_set_view(C, v3d, ar, 0.5, -0.5, -0.5, -0.5, viewnum, nextperspo, align_active);
2361 case RV3D_VIEW_CAMERA:
2362 if(rv3d->viewlock==0) {
2365 if(rv3d->persp != RV3D_CAMOB) {
2368 if (!rv3d->smooth_timer) {
2369 /* store settings of current view before allowing overwriting with camera view
2370 * only if we're not currently in a view transition */
2371 copy_qt_qt(rv3d->lviewquat, rv3d->viewquat);
2372 rv3d->lview= rv3d->view;
2373 rv3d->lpersp= rv3d->persp;
2377 if(G.qual==LR_ALTKEY) {
2378 if(oldcamera && is_an_active_object(oldcamera)) {
2379 v3d->camera= oldcamera;
2381 handle_view3d_lock();
2385 /* first get the default camera for the view lock type */
2386 if(v3d->scenelock) {
2387 /* sets the camera view if available */
2388 v3d->camera= scene->camera;
2391 /* use scene camera if one is not set (even though we're unlocked) */
2392 if(v3d->camera==NULL) {
2393 v3d->camera= scene->camera;
2397 /* if the camera isnt found, check a number of options */
2398 if(v3d->camera==NULL && ob && ob->type==OB_CAMERA)
2401 if(v3d->camera==NULL)
2402 v3d->camera= scene_find_camera(scene);
2404 /* couldnt find any useful camera, bail out */
2405 if(v3d->camera==NULL)
2406 return OPERATOR_CANCELLED;
2408 /* important these dont get out of sync for locked scenes */
2410 scene->camera= v3d->camera;
2412 /* finally do snazzy view zooming */
2413 rv3d->persp= RV3D_CAMOB;
2414 smooth_view(C, v3d, ar, NULL, v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens);
2418 /* return to settings of last view */
2419 /* does smooth_view too */
2420 axis_set_view(C, v3d, ar, rv3d->lviewquat[0], rv3d->lviewquat[1], rv3d->lviewquat[2], rv3d->lviewquat[3], rv3d->lview, rv3d->lpersp, 0);
2429 if(rv3d->persp != RV3D_CAMOB) perspo= rv3d->persp;
2431 return OPERATOR_FINISHED;
2435 void VIEW3D_OT_viewnumpad(wmOperatorType *ot)
2438 ot->name= "View numpad";
2439 ot->description = "Set the view";
2440 ot->idname= "VIEW3D_OT_viewnumpad";
2443 ot->exec= viewnumpad_exec;
2444 ot->poll= ED_operator_rv3d_unlock_poll;
2449 RNA_def_enum(ot->srna, "type", prop_view_items, 0, "View", "The Type of view");
2450 RNA_def_boolean(ot->srna, "align_active", 0, "Align Active", "Align to the active objects axis");
2453 static EnumPropertyItem prop_view_orbit_items[] = {
2454 {V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the Left"},
2455 {V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the Right"},
2456 {V3D_VIEW_STEPUP, "ORBITUP", 0, "Orbit Up", "Orbit the view Up"},
2457 {V3D_VIEW_STEPDOWN, "ORBITDOWN", 0, "Orbit Down", "Orbit the view Down"},
2458 {0, NULL, 0, NULL, NULL}};
2460 static int vieworbit_exec(bContext *C, wmOperator *op)
2462 View3D *v3d= CTX_wm_view3d(C);
2463 ARegion *ar= ED_view3d_context_region_unlock(C);
2464 RegionView3D *rv3d= ar->regiondata; /* no NULL check is needed, poll checks */
2465 float phi, q1[4], new_quat[4];
2468 orbitdir = RNA_enum_get(op->ptr, "type");
2470 if(rv3d->viewlock==0) {
2471 if((rv3d->persp != RV3D_CAMOB) || (v3d->flag2 & V3D_LOCK_CAMERA)) {
2472 if(orbitdir == V3D_VIEW_STEPLEFT || orbitdir == V3D_VIEW_STEPRIGHT) {
2475 phi= (float)(M_PI/360.0)*U.pad_rot_angle;
2476 if(orbitdir == V3D_VIEW_STEPRIGHT) phi= -phi;
2477 si= (float)sin(phi);
2478 q1[0]= (float)cos(phi);
2481 mul_qt_qtqt(new_quat, rv3d->viewquat, q1);
2482 rv3d->view= RV3D_VIEW_USER;
2484 else if(orbitdir == V3D_VIEW_STEPDOWN || orbitdir == V3D_VIEW_STEPUP) {
2485 /* horizontal axis */
2486 copy_v3_v3(q1+1, rv3d->viewinv[0]);
2489 phi= (float)(M_PI/360.0)*U.pad_rot_angle;
2490 if(orbitdir == V3D_VIEW_STEPDOWN) phi= -phi;
2491 q1[0]= (float)cos(phi);
2492 mul_v3_fl(q1+1, sin(phi));
2493 mul_qt_qtqt(new_quat, rv3d->viewquat, q1);
2494 rv3d->view= RV3D_VIEW_USER;
2497 smooth_view(C, CTX_wm_view3d(C), ar, NULL, NULL, NULL, new_quat, NULL, NULL);
2501 return OPERATOR_FINISHED;
2504 void VIEW3D_OT_view_orbit(wmOperatorType *ot)
2507 ot->name= "View Orbit";
2508 ot->description = "Orbit the view";
2509 ot->idname= "VIEW3D_OT_view_orbit";
2512 ot->exec= vieworbit_exec;
2513 ot->poll= ED_operator_rv3d_unlock_poll;
2517 RNA_def_enum(ot->srna, "type", prop_view_orbit_items, 0, "Orbit", "Direction of View Orbit");
2520 static EnumPropertyItem prop_view_pan_items[] = {
2521 {V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the Left"},
2522 {V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the Right"},
2523 {V3D_VIEW_PANUP, "PANUP", 0, "Pan Up", "Pan the view Up"},
2524 {V3D_VIEW_PANDOWN, "PANDOWN", 0, "Pan Down", "Pan the view Down"},
2525 {0, NULL, 0, NULL, NULL}};
2527 static int viewpan_exec(bContext *C, wmOperator *op)
2529 ARegion *ar= CTX_wm_region(C);
2530 RegionView3D *rv3d= CTX_wm_region_view3d(C);
2532 float mval_f[2]= {0.0f, 0.0f};
2535 pandir = RNA_enum_get(op->ptr, "type");
2537 initgrabz(rv3d, 0.0, 0.0, 0.0);
2538 if(pandir == V3D_VIEW_PANRIGHT) { mval_f[0]= -32.0f; ED_view3d_win_to_delta(ar, mval_f, vec); }
2539 else if(pandir == V3D_VIEW_PANLEFT) { mval_f[0]= 32.0f; ED_view3d_win_to_delta(ar, mval_f, vec); }
2540 else if(pandir == V3D_VIEW_PANUP) { mval_f[1]= -25.0f; ED_view3d_win_to_delta(ar, mval_f, vec); }
2541 else if(pandir == V3D_VIEW_PANDOWN) { mval_f[1]= 25.0f; ED_view3d_win_to_delta(ar, mval_f, vec); }
2542 add_v3_v3(rv3d->ofs, vec);
2544 if(rv3d->viewlock & RV3D_BOXVIEW)
2545 view3d_boxview_sync(CTX_wm_area(C), ar);
2547 ED_region_tag_redraw(ar);
2549 return OPERATOR_FINISHED;
2552 void VIEW3D_OT_view_pan(wmOperatorType *ot)
2555 ot->name= "View Pan";
2556 ot->description = "Pan the view";
2557 ot->idname= "VIEW3D_OT_view_pan";
2560 ot->exec= viewpan_exec;
2561 ot->poll= ED_operator_region_view3d_active;
2565 RNA_def_enum(ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan");
2568 static int viewpersportho_exec(bContext *C, wmOperator *UNUSED(op))
2570 ARegion *ar= ED_view3d_context_region_unlock(C);
2571 RegionView3D *rv3d= ar->regiondata; /* no NULL check is needed, poll checks */
2573 if(rv3d->viewlock==0) {
2574 if(rv3d->persp!=RV3D_ORTHO)
2575 rv3d->persp=RV3D_ORTHO;
2576 else rv3d->persp=RV3D_PERSP;
2577 ED_region_tag_redraw(ar);
2580 return OPERATOR_FINISHED;
2584 void VIEW3D_OT_view_persportho(wmOperatorType *ot)
2587 ot->name= "View Persp/Ortho";
2588 ot->description = "Switch the current view from perspective/orthographic";
2589 ot->idname= "VIEW3D_OT_view_persportho";
2592 ot->exec= viewpersportho_exec;
2593 ot->poll= ED_operator_rv3d_unlock_poll;
2600 /* ******************** add background image operator **************** */
2602 static BGpic *background_image_add(bContext *C)
2604 View3D *v3d= CTX_wm_view3d(C);
2606 BGpic *bgpic= MEM_callocN(sizeof(BGpic), "Background Image");
2609 bgpic->iuser.fie_ima= 2;
2611 bgpic->view= 0; /* 0 for all */
2613 BLI_addtail(&v3d->bgpicbase, bgpic);
2618 static int background_image_add_exec(bContext *C, wmOperator *UNUSED(op))
2620 background_image_add(C);
2622 return OPERATOR_FINISHED;
2625 static int background_image_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2627 View3D *v3d= CTX_wm_view3d(C);
2632 /* check input variables */
2633 if(RNA_property_is_set(op->ptr, "filepath")) {
2634 char path[FILE_MAX];
2636 RNA_string_get(op->ptr, "filepath", path);
2637 ima= BKE_add_image_file(path);
2639 else if(RNA_property_is_set(op->ptr, "name")) {
2640 RNA_string_get(op->ptr, "name", name);
2641 ima= (Image *)find_id("IM", name);
2644 bgpic = background_image_add(C);
2649 if(ima->id.us==0) id_us_plus(&ima->id);
2650 else id_lib_extern(&ima->id);
2652 if (!(v3d->flag & V3D_DISPBGPICS))
2653 v3d->flag |= V3D_DISPBGPICS;
2656 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
2658 return OPERATOR_FINISHED;
2661 void VIEW3D_OT_background_image_add(wmOperatorType *ot)
2664 ot->name = "Add Background Image";
2665 ot->description= "Add a new background image";
2666 ot->idname = "VIEW3D_OT_background_image_add";
2669 ot->invoke = background_image_add_invoke;
2670 ot->exec = background_image_add_exec;
2671 ot->poll = ED_operator_view3d_active;
2677 RNA_def_string(ot->srna, "name", "Image", 24, "Name", "Image name to assign.");
2678 RNA_def_string(ot->srna, "filepath", "Path", FILE_MAX, "Filepath", "Path to image file");
2682 /* ***** remove image operator ******* */
2683 static int background_image_remove_exec(bContext *C, wmOperator *op)
2685 View3D *vd = CTX_wm_view3d(C);
2686 int index = RNA_int_get(op->ptr, "index");
2687 BGpic *bgpic_rem= BLI_findlink(&vd->bgpicbase, index);
2690 BLI_remlink(&vd->bgpicbase, bgpic_rem);
2691 if(bgpic_rem->ima) bgpic_rem->ima->id.us--;
2692 MEM_freeN(bgpic_rem);
2693 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, vd);
2694 return OPERATOR_FINISHED;
2697 return OPERATOR_CANCELLED;
2702 void VIEW3D_OT_background_image_remove(wmOperatorType *ot)
2705 ot->name = "Remove Background Image";
2706 ot->description= "Remove a background image from the 3D view";
2707 ot->idname = "VIEW3D_OT_background_image_remove";
2710 ot->exec = background_image_remove_exec;
2711 ot->poll = ED_operator_view3d_active;
2716 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Background image index to remove ", 0, INT_MAX);
2719 /* ********************* set clipping operator ****************** */
2721 static void calc_clipping_plane(float clip[6][4], BoundBox *clipbb)
2725 for(val=0; val<4; val++) {
2727 normal_tri_v3( clip[val],clipbb->vec[val], clipbb->vec[val==3?0:val+1], clipbb->vec[val+4]);
2730 - clip[val][0]*clipbb->vec[val][0]
2731 - clip[val][1]*clipbb->vec[val][1]
2732 - clip[val][2]*clipbb->vec[val][2];
2736 static void calc_local_clipping(float clip_local[][4], BoundBox *clipbb, float mat[][4])
2738 BoundBox clipbb_local;
2742 invert_m4_m4(imat, mat);
2744 for(i=0; i<8; i++) {
2745 mul_v3_m4v3(clipbb_local.vec[i], imat, clipbb->vec[i]);
2748 calc_clipping_plane(clip_local, &clipbb_local);
2751 void ED_view3d_local_clipping(RegionView3D *rv3d, float mat[][4])
2753 if(rv3d->rflag & RV3D_CLIPPING)
2754 calc_local_clipping(rv3d->clip_local, rv3d->clipbb, mat);
2757 static int view3d_clipping_exec(bContext *C, wmOperator *op)
2759 RegionView3D *rv3d= CTX_wm_region_view3d(C);
2764 rect.xmin= RNA_int_get(op->ptr, "xmin");
2765 rect.ymin= RNA_int_get(op->ptr, "ymin");
2766 rect.xmax= RNA_int_get(op->ptr, "xmax");
2767 rect.ymax= RNA_int_get(op->ptr, "ymax");
2769 rv3d->rflag |= RV3D_CLIPPING;
2770 rv3d->clipbb= MEM_callocN(sizeof(BoundBox), "clipbb");
2772 /* note; otherwise opengl won't work */
2773 view3d_operator_needs_opengl(C);
2775 view3d_set_viewcontext(C, &vc);
2776 view3d_get_transformation(vc.ar, vc.rv3d, NULL, &mats); /* NULL because we don't want it in object space */
2777 ED_view3d_calc_clipping(rv3d->clipbb, rv3d->clip, &mats, &rect);
2779 return OPERATOR_FINISHED;
2782 static int view3d_clipping_invoke(bContext *C, wmOperator *op, wmEvent *event)
2784 RegionView3D *rv3d= CTX_wm_region_view3d(C);
2785 ARegion *ar= CTX_wm_region(C);
2787 if(rv3d->rflag & RV3D_CLIPPING) {
2788 rv3d->rflag &= ~RV3D_CLIPPING;
2789 ED_region_tag_redraw(ar);
2790 if(rv3d->clipbb) MEM_freeN(rv3d->clipbb);
2792 return OPERATOR_FINISHED;
2795 return WM_border_select_invoke(C, op, event);
2800 void VIEW3D_OT_clip_border(wmOperatorType *ot)
2804 ot->name= "Clipping Border";
2805 ot->description = "Set the view clipping border";
2806 ot->idname= "VIEW3D_OT_clip_border";
2809 ot->invoke= view3d_clipping_invoke;
2810 ot->exec= view3d_clipping_exec;
2811 ot->modal= WM_border_select_modal;
2813 ot->poll= ED_operator_region_view3d_active;
2819 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
2820 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
2821 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
2822 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
2825 /* ***************** 3d cursor cursor op ******************* */
2827 /* mx my in region coords */
2828 static int set_3dcursor_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
2830 Scene *scene= CTX_data_scene(C);
2831 ARegion *ar= CTX_wm_region(C);
2832 View3D *v3d = CTX_wm_view3d(C);
2833 RegionView3D *rv3d= CTX_wm_region_view3d(C);
2834 float dx, dy, fz, *fp = NULL, dvec[3], oldcurs[3];
2836 // short ctrl= 0; // XXX
2838 fp= give_cursor(scene, v3d);
2840 // if(obedit && ctrl) lr_click= 1;
2841 copy_v3_v3(oldcurs, fp);
2843 project_int_noclip(ar, fp, mval);
2844 flip= initgrabz(rv3d, fp[0], fp[1], fp[2]);
2846 /* reset the depth based on the view offset */
2848 negate_v3_v3(fp, rv3d->ofs);
2851 project_int_noclip(ar, fp, mval);
2852 flip= initgrabz(rv3d, fp[0], fp[1], fp[2]);
2855 if(mval[0]!=IS_CLIPPED) {
2856 short depth_used = 0;
2858 if (U.uiflag & USER_ORBIT_ZBUF) { /* maybe this should be accessed some other way */
2859 view3d_operator_needs_opengl(C);
2860 if (ED_view3d_autodist(scene, ar, v3d, event->mval, fp))
2866 VECSUB2D(mval_f, mval, event->mval);
2867 ED_view3d_win_to_delta(ar, mval_f, dvec);
2868 sub_v3_v3(fp, dvec);
2873 dx= ((float)(event->mval[0]-(ar->winx/2)))*rv3d->zfac/(ar->winx/2);
2874 dy= ((float)(event->mval[1]-(ar->winy/2)))*rv3d->zfac/(ar->winy/2);
2876 fz= rv3d->persmat[0][3]*fp[0]+ rv3d->persmat[1][3]*fp[1]+ rv3d->persmat[2][3]*fp[2]+ rv3d->persmat[3][3];
2879 fp[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy+ rv3d->persinv[2][0]*fz)-rv3d->ofs[0];
2880 fp[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy+ rv3d->persinv[2][1]*fz)-rv3d->ofs[1];
2881 fp[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy+ rv3d->persinv[2][2]*fz)-rv3d->ofs[2];
2884 if(v3d && v3d->localvd)
2885 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
2887 WM_event_add_notifier(C, NC_SCENE|NA_EDITED, scene);
2889 return OPERATOR_FINISHED;
2892 void VIEW3D_OT_cursor3d(wmOperatorType *ot)
2896 ot->name= "Set 3D Cursor";
2897 ot->description = "Set the location of the 3D cursor";
2898 ot->idname= "VIEW3D_OT_cursor3d";
2901 ot->invoke= set_3dcursor_invoke;
2903 ot->poll= ED_operator_view3d_active;
2906 // ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2912 /* ***************** manipulator op ******************* */
2915 static int manipulator_invoke(bContext *C, wmOperator *op, wmEvent *event)
2917 View3D *v3d = CTX_wm_view3d(C);
2919 if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return OPERATOR_PASS_THROUGH;
2920 if(!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return OPERATOR_PASS_THROUGH;
2922 /* only no modifier or shift */
2923 if(event->keymodifier != 0 && event->keymodifier != KM_SHIFT) return OPERATOR_PASS_THROUGH;
2925 /* note; otherwise opengl won't work */
2926 view3d_operator_needs_opengl(C);
2928 if(0==BIF_do_manipulator(C, event, op))
2929 return OPERATOR_PASS_THROUGH;
2931 return OPERATOR_FINISHED;
2934 void VIEW3D_OT_manipulator(wmOperatorType *ot)
2938 ot->name= "3D Manipulator";
2939 ot->description = "Manipulate selected item by axis";
2940 ot->idname= "VIEW3D_OT_manipulator";
2943 ot->invoke= manipulator_invoke;
2945 ot->poll= ED_operator_view3d_active;
2947 /* properties to pass to transform */
2948 Transform_Properties(ot, P_CONSTRAINT);
2951 static int enable_manipulator_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2953 View3D *v3d = CTX_wm_view3d(C);
2957 if (RNA_boolean_get(op->ptr, "translate"))
2958 v3d->twtype |= V3D_MANIP_TRANSLATE;
2959 if (RNA_boolean_get(op->ptr, "rotate"))
2960 v3d->twtype |= V3D_MANIP_ROTATE;
2961 if (RNA_boolean_get(op->ptr, "scale"))
2962 v3d->twtype |= V3D_MANIP_SCALE;
2964 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
2966 return OPERATOR_FINISHED;
2969 void VIEW3D_OT_enable_manipulator(wmOperatorType *ot)
2972 ot->name= "Enable 3D Manipulator";
2973 ot->description = "Enable the transform manipulator for use";
2974 ot->idname= "VIEW3D_OT_enable_manipulator";
2977 ot->invoke= enable_manipulator_invoke;
2978 ot->poll= ED_operator_view3d_active;
2981 RNA_def_boolean(ot->srna, "translate", 0, "Translate", "Enable the translate manipulator");
2982 RNA_def_boolean(ot->srna, "rotate", 0, "Rotate", "Enable the rotate manipulator");
2983 RNA_def_boolean(ot->srna, "scale", 0, "Scale", "Enable the scale manipulator");
2986 /* ************************* below the line! *********************** */
2989 static float view_autodist_depth_margin(ARegion *ar, const int mval[2], int margin)
2991 ViewDepths depth_temp= {0};
2996 /* Get Z Depths, needed for perspective, nice for ortho */
2999 rect.xmax= mval[0] + 1;
3000 rect.ymax= mval[1] + 1;
3003 rect.xmax = mval[0] + margin;
3004 rect.ymax = mval[1] + margin;
3006 rect.xmin = mval[0] - margin;
3007 rect.ymin = mval[1] - margin;
3010 view3d_update_depths_rect(ar, &depth_temp, &rect);
3011 depth_close= view3d_depth_near(&depth_temp);
3012 if(depth_temp.depths) MEM_freeN(depth_temp.depths);
3016 /* XXX todo Zooms in on a border drawn by the user */
3017 int ED_view3d_autodist(Scene *scene, ARegion *ar, View3D *v3d, const int mval[2], float mouse_worldloc[3] ) //, float *autodist )
3019 bglMats mats; /* ZBuffer depth vars */
3020 float depth_close= FLT_MAX;
3021 double cent[2], p[3];
3023 /* Get Z Depths, needed for perspective, nice for ortho */
3024 bgl_get_mats(&mats);
3025 draw_depth(scene, ar, v3d, NULL);
3027 depth_close= view_autodist_depth_margin(ar, mval, 4);
3029 if (depth_close==FLT_MAX)
3032 cent[0] = (double)mval[0];
3033 cent[1] = (double)mval[1];
3035 if (!gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
3038 mouse_worldloc[0] = (float)p[0];
3039 mouse_worldloc[1] = (float)p[1];
3040 mouse_worldloc[2] = (float)p[2];
3044 int ED_view3d_autodist_init(Scene *scene, ARegion *ar, View3D *v3d, int mode) //, float *autodist )
3046 /* Get Z Depths, needed for perspective, nice for ortho */
3049 draw_depth(scene, ar, v3d, NULL);
3052 draw_depth_gpencil(scene, ar, v3d);
3059 // no 4x4 sampling, run view_autodist_init first
3060 int ED_view3d_autodist_simple(ARegion *ar, const int mval[2], float mouse_worldloc[3], int margin, float *force_depth) //, float *autodist )
3062 bglMats mats; /* ZBuffer depth vars, could cache? */
3064 double cent[2], p[3];
3066 /* Get Z Depths, needed for perspective, nice for ortho */
3068 depth= *force_depth;
3070 depth= view_autodist_depth_margin(ar, mval, margin);
3075 cent[0] = (double)mval[0];
3076 cent[1] = (double)mval[1];
3078 bgl_get_mats(&mats);
3079 if (!gluUnProject(cent[0], cent[1], depth, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
3082 mouse_worldloc[0] = (float)p[0];
3083 mouse_worldloc[1] = (float)p[1];
3084 mouse_worldloc[2] = (float)p[2];
3088 int ED_view3d_autodist_depth(struct ARegion *ar, const int mval[2], int margin, float *depth)
3090 *depth= view_autodist_depth_margin(ar, mval, margin);
3092 return (*depth==FLT_MAX) ? 0:1;
3095 static int depth_segment_cb(int x, int y, void *userData)
3097 struct { struct ARegion *ar; int margin; float depth; } *data = userData;
3104 depth= view_autodist_depth_margin(data->ar, mval, data->margin);
3106 if(depth != FLT_MAX) {
3115 int ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], const int mval_end[2], int margin, float *depth)
3117 struct { struct ARegion *ar; int margin; float depth; } data = {NULL};
3122 data.margin= margin;
3123 data.depth= FLT_MAX;