svn merge ^/trunk/blender -r43751:43819, need to look into changes made to editmesh_l...
[blender-staging.git] / source / blender / editors / space_view3d / view3d_view.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_view3d/view3d_view.c
28  *  \ingroup spview3d
29  */
30
31
32 #include "DNA_camera_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_lamp_types.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLI_math.h"
40 #include "BLI_rect.h"
41 #include "BLI_listbase.h"
42 #include "BLI_utildefines.h"
43
44 #include "BKE_anim.h"
45 #include "BKE_action.h"
46 #include "BKE_camera.h"
47 #include "BKE_context.h"
48 #include "BKE_depsgraph.h"
49 #include "BKE_object.h"
50 #include "BKE_global.h"
51 #include "BKE_main.h"
52 #include "BKE_report.h"
53 #include "BKE_scene.h"
54 #include "BKE_screen.h"
55
56 #include "BIF_gl.h"
57 #include "BIF_glutil.h"
58
59 #include "GPU_draw.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "ED_screen.h"
65 #include "ED_armature.h"
66
67 #ifdef WITH_GAMEENGINE
68 #include "BL_System.h"
69 #endif
70
71 #include "RNA_access.h"
72 #include "RNA_define.h"
73
74 #include "view3d_intern.h"      // own include
75
76 /* use this call when executing an operator,
77    event system doesn't set for each event the
78    opengl drawing context */
79 void view3d_operator_needs_opengl(const bContext *C)
80 {
81         wmWindow *win = CTX_wm_window(C);
82         ARegion *ar= CTX_wm_region(C);
83         
84         view3d_region_operator_needs_opengl(win, ar);
85 }
86
87 void view3d_region_operator_needs_opengl(wmWindow *win, ARegion *ar)
88 {
89         /* for debugging purpose, context should always be OK */
90         if ((ar == NULL) || (ar->regiontype!=RGN_TYPE_WINDOW))
91                 printf("view3d_region_operator_needs_opengl error, wrong region\n");
92         else {
93                 RegionView3D *rv3d= ar->regiondata;
94                 
95                 wmSubWindowSet(win, ar->swinid);
96                 glMatrixMode(GL_PROJECTION);
97                 glLoadMatrixf(rv3d->winmat);
98                 glMatrixMode(GL_MODELVIEW);
99                 glLoadMatrixf(rv3d->viewmat);
100         }
101 }
102
103 float *give_cursor(Scene *scene, View3D *v3d)
104 {
105         if(v3d && v3d->localvd) return v3d->cursor;
106         else return scene->cursor;
107 }
108
109
110 /* ****************** smooth view operator ****************** */
111 /* This operator is one of the 'timer refresh' ones like animation playback */
112
113 struct SmoothViewStore {
114         float orig_dist, new_dist;
115         float orig_lens, new_lens;
116         float orig_quat[4], new_quat[4];
117         float orig_ofs[3], new_ofs[3];
118         
119         int to_camera, orig_view;
120         
121         double time_allowed;
122 };
123
124 /* will start timer if appropriate */
125 /* the arguments are the desired situation */
126 void smooth_view(bContext *C, View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera,
127                  float *ofs, float *quat, float *dist, float *lens)
128 {
129         wmWindowManager *wm= CTX_wm_manager(C);
130         wmWindow *win= CTX_wm_window(C);
131         ScrArea *sa= CTX_wm_area(C);
132
133         RegionView3D *rv3d= ar->regiondata;
134         struct SmoothViewStore sms= {0};
135         short ok= FALSE;
136         
137         /* initialize sms */
138         copy_v3_v3(sms.new_ofs, rv3d->ofs);
139         copy_qt_qt(sms.new_quat, rv3d->viewquat);
140         sms.new_dist= rv3d->dist;
141         sms.new_lens= v3d->lens;
142         sms.to_camera= 0;
143
144         /* note on camera locking, this is a little confusing but works ok.
145          * we may be changing the view 'as if' there is no active camera, but infact
146          * there is an active camera which is locked to the view.
147          *
148          * In the case where smooth view is moving _to_ a camera we dont want that
149          * camera to be moved or changed, so only when the camera is not being set should
150          * we allow camera option locking to initialize the view settings from the camera.
151          */
152         if(camera == NULL && oldcamera == NULL) {
153                 ED_view3d_camera_lock_init(v3d, rv3d);
154         }
155
156         /* store the options we want to end with */
157         if(ofs) copy_v3_v3(sms.new_ofs, ofs);
158         if(quat) copy_qt_qt(sms.new_quat, quat);
159         if(dist) sms.new_dist= *dist;
160         if(lens) sms.new_lens= *lens;
161
162         if (camera) {
163                 ED_view3d_from_object(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens);
164                 sms.to_camera= 1; /* restore view3d values in end */
165         }
166         
167         if (C && U.smooth_viewtx) {
168                 int changed = 0; /* zero means no difference */
169                 
170                 if (oldcamera != camera)
171                         changed = 1;
172                 else if (sms.new_dist != rv3d->dist)
173                         changed = 1;
174                 else if (sms.new_lens != v3d->lens)
175                         changed = 1;
176                 else if (!equals_v3v3(sms.new_ofs, rv3d->ofs))
177                         changed = 1;
178                 else if (!equals_v4v4(sms.new_quat, rv3d->viewquat))
179                         changed = 1;
180                 
181                 /* The new view is different from the old one
182                         * so animate the view */
183                 if (changed) {
184
185                         /* original values */
186                         if (oldcamera) {
187                                 sms.orig_dist= rv3d->dist; // below function does weird stuff with it...
188                                 ED_view3d_from_object(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens);
189                         }
190                         else {
191                                 copy_v3_v3(sms.orig_ofs, rv3d->ofs);
192                                 copy_qt_qt(sms.orig_quat, rv3d->viewquat);
193                                 sms.orig_dist= rv3d->dist;
194                                 sms.orig_lens= v3d->lens;
195                         }
196                         /* grid draw as floor */
197                         if((rv3d->viewlock & RV3D_LOCKED)==0) {
198                                 /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
199                                 sms.orig_view= rv3d->sms ? rv3d->sms->orig_view : rv3d->view;
200                                 rv3d->view= RV3D_VIEW_USER;
201                         }
202
203                         sms.time_allowed= (double)U.smooth_viewtx / 1000.0;
204                         
205                         /* if this is view rotation only
206                                 * we can decrease the time allowed by
207                                 * the angle between quats 
208                                 * this means small rotations wont lag */
209                         if (quat && !ofs && !dist) {
210                                 float vec1[3]={0,0,1}, vec2[3]= {0,0,1};
211                                 float q1[4], q2[4];
212
213                                 invert_qt_qt(q1, sms.new_quat);
214                                 invert_qt_qt(q2, sms.orig_quat);
215
216                                 mul_qt_v3(q1, vec1);
217                                 mul_qt_v3(q2, vec2);
218
219                                 /* scale the time allowed by the rotation */
220                                 sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */
221                         }
222
223                         /* ensure it shows correct */
224                         if(sms.to_camera) rv3d->persp= RV3D_PERSP;
225
226                         rv3d->rflag |= RV3D_NAVIGATING;
227                         
228                         /* keep track of running timer! */
229                         if(rv3d->sms==NULL)
230                                 rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d");
231                         *rv3d->sms= sms;
232                         if(rv3d->smooth_timer)
233                                 WM_event_remove_timer(wm, win, rv3d->smooth_timer);
234                         /* TIMER1 is hardcoded in keymap */
235                         rv3d->smooth_timer= WM_event_add_timer(wm, win, TIMER1, 1.0/100.0);     /* max 30 frs/sec */
236                         
237                         ok= TRUE;
238                 }
239         }
240         
241         /* if we get here nothing happens */
242         if(ok == FALSE) {
243                 if(sms.to_camera==0) {
244                         copy_v3_v3(rv3d->ofs, sms.new_ofs);
245                         copy_qt_qt(rv3d->viewquat, sms.new_quat);
246                         rv3d->dist = sms.new_dist;
247                         v3d->lens = sms.new_lens;
248                 }
249
250                 if(rv3d->viewlock & RV3D_BOXVIEW)
251                         view3d_boxview_copy(sa, ar);
252
253                 ED_region_tag_redraw(ar);
254         }
255 }
256
257 /* only meant for timer usage */
258 static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
259 {
260         View3D *v3d = CTX_wm_view3d(C);
261         RegionView3D *rv3d= CTX_wm_region_view3d(C);
262         struct SmoothViewStore *sms= rv3d->sms;
263         float step, step_inv;
264         
265         /* escape if not our timer */
266         if(rv3d->smooth_timer==NULL || rv3d->smooth_timer!=event->customdata)
267                 return OPERATOR_PASS_THROUGH;
268         
269         if(sms->time_allowed != 0.0)
270                 step = (float)((rv3d->smooth_timer->duration)/sms->time_allowed);
271         else
272                 step = 1.0f;
273         
274         /* end timer */
275         if(step >= 1.0f) {
276                 
277                 /* if we went to camera, store the original */
278                 if(sms->to_camera) {
279                         rv3d->persp= RV3D_CAMOB;
280                         copy_v3_v3(rv3d->ofs, sms->orig_ofs);
281                         copy_qt_qt(rv3d->viewquat, sms->orig_quat);
282                         rv3d->dist = sms->orig_dist;
283                         v3d->lens = sms->orig_lens;
284                 }
285                 else {
286                         copy_v3_v3(rv3d->ofs, sms->new_ofs);
287                         copy_qt_qt(rv3d->viewquat, sms->new_quat);
288                         rv3d->dist = sms->new_dist;
289                         v3d->lens = sms->new_lens;
290
291                         ED_view3d_camera_lock_sync(v3d, rv3d);
292                 }
293                 
294                 if((rv3d->viewlock & RV3D_LOCKED)==0) {
295                         rv3d->view= sms->orig_view;
296                 }
297
298                 MEM_freeN(rv3d->sms);
299                 rv3d->sms= NULL;
300                 
301                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
302                 rv3d->smooth_timer= NULL;
303                 rv3d->rflag &= ~RV3D_NAVIGATING;
304         }
305         else {
306                 int i;
307                 
308                 /* ease in/out */
309                 if (step < 0.5f)        step = (float)pow(step*2.0f, 2.0)/2.0f;
310                 else                            step = (float)1.0f-(powf(2.0f*(1.0f-step),2.0f)/2.0f);
311
312                 step_inv = 1.0f-step;
313
314                 for (i=0; i<3; i++)
315                         rv3d->ofs[i] = sms->new_ofs[i] * step + sms->orig_ofs[i]*step_inv;
316
317                 interp_qt_qtqt(rv3d->viewquat, sms->orig_quat, sms->new_quat, step);
318                 
319                 rv3d->dist = sms->new_dist * step + sms->orig_dist*step_inv;
320                 v3d->lens = sms->new_lens * step + sms->orig_lens*step_inv;
321
322                 ED_view3d_camera_lock_sync(v3d, rv3d);
323         }
324         
325         if(rv3d->viewlock & RV3D_BOXVIEW)
326                 view3d_boxview_copy(CTX_wm_area(C), CTX_wm_region(C));
327         
328         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
329         
330         return OPERATOR_FINISHED;
331 }
332
333 void VIEW3D_OT_smoothview(wmOperatorType *ot)
334 {
335         
336         /* identifiers */
337         ot->name= "Smooth View";
338         ot->idname= "VIEW3D_OT_smoothview";
339         ot->description="The time to animate the change of view (in milliseconds)";
340         
341         /* api callbacks */
342         ot->invoke= view3d_smoothview_invoke;
343         
344         ot->poll= ED_operator_view3d_active;
345 }
346
347 /* ****************** change view operators ****************** */
348
349 static int view3d_camera_to_view_exec(bContext *C, wmOperator *UNUSED(op))
350 {
351         View3D *v3d = CTX_wm_view3d(C);
352         RegionView3D *rv3d= CTX_wm_region_view3d(C);
353         ObjectTfmProtectedChannels obtfm;
354
355         copy_qt_qt(rv3d->lviewquat, rv3d->viewquat);
356         rv3d->lview= rv3d->view;
357         if(rv3d->persp != RV3D_CAMOB) {
358                 rv3d->lpersp= rv3d->persp;
359         }
360
361         object_tfm_protected_backup(v3d->camera, &obtfm);
362
363         ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
364
365         object_tfm_protected_restore(v3d->camera, &obtfm, v3d->camera->protectflag);
366
367         DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
368         rv3d->persp = RV3D_CAMOB;
369         
370         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, v3d->camera);
371         
372         return OPERATOR_FINISHED;
373
374 }
375
376 static int view3d_camera_to_view_poll(bContext *C)
377 {
378         View3D *v3d= CTX_wm_view3d(C);
379         if(v3d && v3d->camera && v3d->camera->id.lib==NULL) {
380                 RegionView3D *rv3d= CTX_wm_region_view3d(C);
381                 if(rv3d && !rv3d->viewlock) {
382                         return 1;
383                 }
384         }
385
386         return 0;
387 }
388
389 void VIEW3D_OT_camera_to_view(wmOperatorType *ot)
390 {
391         /* identifiers */
392         ot->name= "Align Camera To View";
393         ot->description= "Set camera view to active view";
394         ot->idname= "VIEW3D_OT_camera_to_view";
395         
396         /* api callbacks */
397         ot->exec= view3d_camera_to_view_exec;
398         ot->poll= view3d_camera_to_view_poll;
399         
400         /* flags */
401         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
402 }
403
404 /* unlike VIEW3D_OT_view_selected this is for framing a render and not
405  * meant to take into account vertex/bone selection for eg. */
406 static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
407 {
408         Scene *scene= CTX_data_scene(C);
409         View3D *v3d = CTX_wm_view3d(C);
410         Object *camera_ob= v3d->camera;
411
412         float r_co[3]; /* the new location to apply */
413
414         /* this function does all the important stuff */
415         if (camera_view_frame_fit_to_scene(scene, v3d, camera_ob, r_co)) {
416
417                 ObjectTfmProtectedChannels obtfm;
418                 float obmat_new[4][4];
419
420                 copy_m4_m4(obmat_new, camera_ob->obmat);
421                 copy_v3_v3(obmat_new[3], r_co);
422
423                 /* only touch location */
424                 object_tfm_protected_backup(camera_ob, &obtfm);
425                 object_apply_mat4(camera_ob, obmat_new, TRUE, TRUE);
426                 object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
427
428                 /* notifiers */
429                 DAG_id_tag_update(&camera_ob->id, OB_RECALC_OB);
430                 WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, camera_ob);
431                 return OPERATOR_FINISHED;
432         }
433         else {
434                 return OPERATOR_CANCELLED;
435         }
436 }
437
438 static int view3d_camera_to_view_selected_poll(bContext *C)
439 {
440         View3D *v3d= CTX_wm_view3d(C);
441         if(v3d && v3d->camera && v3d->camera->id.lib==NULL) {
442                 RegionView3D *rv3d= CTX_wm_region_view3d(C);
443                 if(rv3d) {
444                         if (rv3d->is_persp == FALSE) {
445                                 CTX_wm_operator_poll_msg_set(C, "Only valid for a perspective camera view");
446                         }
447                         else if (!rv3d->viewlock) {
448                                 return 1;
449                         }
450                 }
451         }
452
453         return 0;
454 }
455
456 void VIEW3D_OT_camera_to_view_selected(wmOperatorType *ot)
457 {
458         /* identifiers */
459         ot->name= "Camera Fit Frame to Selected";
460         ot->description= "Move the camera so selected objects are framed";
461         ot->idname= "VIEW3D_OT_camera_to_view_selected";
462
463         /* api callbacks */
464         ot->exec= view3d_camera_to_view_selected_exec;
465         ot->poll= view3d_camera_to_view_selected_poll;
466
467         /* flags */
468         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
469 }
470
471
472 static int view3d_setobjectascamera_exec(bContext *C, wmOperator *UNUSED(op))
473 {       
474         View3D *v3d;
475         ARegion *ar;
476         RegionView3D *rv3d;
477
478         Scene *scene= CTX_data_scene(C);
479         Object *ob = CTX_data_active_object(C);
480
481         /* no NULL check is needed, poll checks */
482         ED_view3d_context_user_region(C, &v3d, &ar);
483         rv3d = ar->regiondata;
484
485         if(ob) {
486                 Object *camera_old= (rv3d->persp == RV3D_CAMOB) ? V3D_CAMERA_SCENE(scene, v3d) : NULL;
487                 rv3d->persp= RV3D_CAMOB;
488                 v3d->camera= ob;
489                 if(v3d->scenelock)
490                         scene->camera= ob;
491
492                 if(camera_old != ob) /* unlikely but looks like a glitch when set to the same */
493                         smooth_view(C, v3d, ar, camera_old, v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens);
494
495                 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS|NC_OBJECT|ND_DRAW, CTX_data_scene(C));
496         }
497         
498         return OPERATOR_FINISHED;
499 }
500
501 int ED_operator_rv3d_user_region_poll(bContext *C)
502 {
503         View3D *v3d_dummy;
504         ARegion *ar_dummy;
505
506         return ED_view3d_context_user_region(C, &v3d_dummy, &ar_dummy);
507 }
508
509 void VIEW3D_OT_object_as_camera(wmOperatorType *ot)
510 {
511         
512         /* identifiers */
513         ot->name= "Set Active Object as Camera";
514         ot->description= "Set the active object as the active camera for this view or scene";
515         ot->idname= "VIEW3D_OT_object_as_camera";
516         
517         /* api callbacks */
518         ot->exec= view3d_setobjectascamera_exec;
519         ot->poll= ED_operator_rv3d_user_region_poll;
520         
521         /* flags */
522         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
523 }
524
525 /* ********************************** */
526
527 void ED_view3d_calc_clipping(BoundBox *bb, float planes[4][4], bglMats *mats, const rcti *rect)
528 {
529         float modelview[4][4];
530         double xs, ys, p[3];
531         int val, flip_sign, a;
532
533         /* near zero floating point values can give issues with gluUnProject
534                 in side view on some implementations */
535         if(fabs(mats->modelview[0]) < 1e-6) mats->modelview[0]= 0.0;
536         if(fabs(mats->modelview[5]) < 1e-6) mats->modelview[5]= 0.0;
537
538         /* Set up viewport so that gluUnProject will give correct values */
539         mats->viewport[0] = 0;
540         mats->viewport[1] = 0;
541
542         /* four clipping planes and bounding volume */
543         /* first do the bounding volume */
544         for(val=0; val<4; val++) {
545                 xs= (val==0||val==3)?rect->xmin:rect->xmax;
546                 ys= (val==0||val==1)?rect->ymin:rect->ymax;
547
548                 gluUnProject(xs, ys, 0.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
549                 VECCOPY(bb->vec[val], p);
550
551                 gluUnProject(xs, ys, 1.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
552                 VECCOPY(bb->vec[4+val], p);
553         }
554
555         /* verify if we have negative scale. doing the transform before cross
556            product flips the sign of the vector compared to doing cross product
557            before transform then, so we correct for that. */
558         for(a=0; a<16; a++)
559                 ((float*)modelview)[a] = mats->modelview[a];
560         flip_sign = is_negative_m4(modelview);
561
562         /* then plane equations */
563         for(val=0; val<4; val++) {
564
565                 normal_tri_v3(planes[val], bb->vec[val], bb->vec[val==3?0:val+1], bb->vec[val+4]);
566
567                 if(flip_sign)
568                         negate_v3(planes[val]);
569
570                 planes[val][3]= - planes[val][0]*bb->vec[val][0]
571                         - planes[val][1]*bb->vec[val][1]
572                         - planes[val][2]*bb->vec[val][2];
573         }
574 }
575
576 /* create intersection coordinates in view Z direction at mouse coordinates */
577 void ED_view3d_win_to_segment_clip(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_end[3])
578 {
579         RegionView3D *rv3d= ar->regiondata;
580         
581         if(rv3d->is_persp) {
582                 float vec[3];
583                 ED_view3d_win_to_vector(ar, mval, vec);
584
585                 copy_v3_v3(ray_start, rv3d->viewinv[3]);
586                 madd_v3_v3v3fl(ray_start, rv3d->viewinv[3], vec, v3d->near);
587                 madd_v3_v3v3fl(ray_end, rv3d->viewinv[3], vec, v3d->far);
588         }
589         else {
590                 float vec[4];
591                 vec[0] = 2.0f * mval[0] / ar->winx - 1;
592                 vec[1] = 2.0f * mval[1] / ar->winy - 1;
593                 vec[2] = 0.0f;
594                 vec[3] = 1.0f;
595                 
596                 mul_m4_v4(rv3d->persinv, vec);
597                 
598                 madd_v3_v3v3fl(ray_start, vec, rv3d->viewinv[2],  1000.0f);
599                 madd_v3_v3v3fl(ray_end, vec, rv3d->viewinv[2], -1000.0f);
600         }
601
602         /* clipping */
603         if(rv3d->rflag & RV3D_CLIPPING) {
604                 int a;
605                 for(a=0; a<4; a++) {
606                         clip_line_plane(ray_start, ray_end, rv3d->clip[a]);
607                 }
608         }
609 }
610
611 /* create intersection ray in view Z direction at mouse coordinates */
612 void ED_view3d_win_to_ray(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_normal[3])
613 {
614         float ray_end[3];
615         
616         ED_view3d_win_to_segment_clip(ar, v3d, mval, ray_start, ray_end);
617         sub_v3_v3v3(ray_normal, ray_end, ray_start);
618         normalize_v3(ray_normal);
619 }
620
621 void ED_view3d_global_to_vector(RegionView3D *rv3d, const float coord[3], float vec[3])
622 {
623         if (rv3d->is_persp) {
624                 float p1[4], p2[4];
625
626                 copy_v3_v3(p1, coord);
627                 p1[3] = 1.0f;
628                 copy_v3_v3(p2, p1);
629                 p2[3] = 1.0f;
630                 mul_m4_v4(rv3d->viewmat, p2);
631
632                 mul_v3_fl(p2, 2.0f);
633
634                 mul_m4_v4(rv3d->viewinv, p2);
635
636                 sub_v3_v3v3(vec, p1, p2);
637         }
638         else {
639                 copy_v3_v3(vec, rv3d->viewinv[2]);
640         }
641         normalize_v3(vec);
642 }
643
644 int initgrabz(RegionView3D *rv3d, float x, float y, float z)
645 {
646         int flip= FALSE;
647         if(rv3d==NULL) return flip;
648         rv3d->zfac= rv3d->persmat[0][3]*x+ rv3d->persmat[1][3]*y+ rv3d->persmat[2][3]*z+ rv3d->persmat[3][3];
649         if (rv3d->zfac < 0.0f)
650                 flip= TRUE;
651         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that 
652                 * (accounting for near zero values)
653                 * */
654         if (rv3d->zfac < 1.e-6f && rv3d->zfac > -1.e-6f) rv3d->zfac = 1.0f;
655         
656         /* Negative zfac means x, y, z was behind the camera (in perspective).
657                 * This gives flipped directions, so revert back to ok default case.
658         */
659         // NOTE: I've changed this to flip zfac to be positive again for now so that GPencil draws ok
660         //      -- Aligorith, 2009Aug31
661         //if (rv3d->zfac < 0.0f) rv3d->zfac = 1.0f;
662         if (rv3d->zfac < 0.0f) rv3d->zfac= -rv3d->zfac;
663         
664         return flip;
665 }
666
667 void ED_view3d_win_to_3d(ARegion *ar, const float depth_pt[3], const float mval[2], float out[3])
668 {
669         RegionView3D *rv3d= ar->regiondata;
670         
671         float line_sta[3];
672         float line_end[3];
673
674         if(rv3d->is_persp) {
675                 float mousevec[3];
676                 copy_v3_v3(line_sta, rv3d->viewinv[3]);
677                 ED_view3d_win_to_vector(ar, mval, mousevec);
678                 add_v3_v3v3(line_end, line_sta, mousevec);
679
680                 if(isect_line_plane_v3(out, line_sta, line_end, depth_pt, rv3d->viewinv[2], TRUE) == 0) {
681                         /* highly unlikely to ever happen, mouse vec paralelle with view plane */
682                         zero_v3(out);
683                 }
684         }
685         else {
686                 const float dx= (2.0f * mval[0] / (float)ar->winx) - 1.0f;
687                 const float dy= (2.0f * mval[1] / (float)ar->winy) - 1.0f;
688                 line_sta[0]= (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
689                 line_sta[1]= (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
690                 line_sta[2]= (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
691
692                 add_v3_v3v3(line_end, line_sta, rv3d->viewinv[2]);
693                 closest_to_line_v3(out, depth_pt, line_sta, line_end);
694         }
695 }
696
697 /* always call initgrabz */
698 /* only to detect delta motion */
699 void ED_view3d_win_to_delta(ARegion *ar, const float mval[2], float out[3])
700 {
701         RegionView3D *rv3d= ar->regiondata;
702         float dx, dy;
703         
704         dx= 2.0f*mval[0]*rv3d->zfac/ar->winx;
705         dy= 2.0f*mval[1]*rv3d->zfac/ar->winy;
706         
707         out[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy);
708         out[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy);
709         out[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy);
710 }
711
712 /* doesn't rely on initgrabz */
713 /* for perspective view, get the vector direction to
714  * the mouse cursor as a normalized vector */
715 void ED_view3d_win_to_vector(ARegion *ar, const float mval[2], float out[3])
716 {
717         RegionView3D *rv3d= ar->regiondata;
718
719         if(rv3d->is_persp) {
720                 out[0]= 2.0f * (mval[0] / ar->winx) - 1.0f;
721                 out[1]= 2.0f * (mval[1] / ar->winy) - 1.0f;
722                 out[2]= -0.5f;
723                 mul_project_m4_v3(rv3d->persinv, out);
724                 sub_v3_v3(out, rv3d->viewinv[3]);
725         }
726         else {
727                 copy_v3_v3(out, rv3d->viewinv[2]);
728         }
729         normalize_v3(out);
730 }
731
732 float ED_view3d_depth_read_cached(ViewContext *vc, int x, int y)
733 {
734         ViewDepths *vd = vc->rv3d->depths;
735                 
736         x -= vc->ar->winrct.xmin;
737         y -= vc->ar->winrct.ymin;
738
739         if(vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
740                 return vd->depths[y * vd->w + x];
741         else
742                 return 1;
743 }
744
745 void ED_view3d_depth_tag_update(RegionView3D *rv3d)
746 {
747         if(rv3d->depths)
748                 rv3d->depths->damaged= 1;
749 }
750
751 void ED_view3d_ob_project_mat_get(RegionView3D *rv3d, Object *ob, float pmat[4][4])
752 {
753         float vmat[4][4];
754         
755         mult_m4_m4m4(vmat, rv3d->viewmat, ob->obmat);
756         mult_m4_m4m4(pmat, rv3d->winmat, vmat);
757 }
758
759 /* Uses window coordinates (x,y) and depth component z to find a point in
760    modelspace */
761 void view3d_unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
762 {
763         double ux, uy, uz;
764
765                 gluUnProject(x,y,z, mats->modelview, mats->projection,
766                          (GLint *)mats->viewport, &ux, &uy, &uz );
767         out[0] = ux;
768         out[1] = uy;
769         out[2] = uz;
770 }
771
772 /* use view3d_get_object_project_mat to get projecting mat */
773 void ED_view3d_project_float(const ARegion *ar, const float vec[3], float adr[2], float mat[4][4])
774 {
775         float vec4[4];
776         
777         adr[0]= IS_CLIPPED;
778         copy_v3_v3(vec4, vec);
779         vec4[3]= 1.0;
780         
781         mul_m4_v4(mat, vec4);
782         
783         if( vec4[3]>FLT_EPSILON ) {
784                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
785                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
786         } else {
787                 adr[0] = adr[1] = 0.0f;
788         }
789 }
790
791 /* use view3d_get_object_project_mat to get projecting mat */
792 void ED_view3d_project_float_v3(ARegion *ar, float *vec, float *adr, float mat[4][4])
793 {
794         float vec4[4];
795         
796         copy_v3_v3(vec4, vec);
797         vec4[3]= 1.0;
798         adr[0]= IS_CLIPPED;
799         
800         mul_m4_v4(mat, vec4);
801         
802         if( vec4[3]>FLT_EPSILON ) {
803                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
804                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
805                 adr[2] = vec4[2]/vec4[3];
806         } else {
807                 adr[0] = adr[1] = adr[2] = 0.0f;
808         }
809 }
810
811 int ED_view3d_boundbox_clip(RegionView3D *rv3d, float obmat[][4], BoundBox *bb)
812 {
813         /* return 1: draw */
814         
815         float mat[4][4];
816         float vec[4], min, max;
817         int a, flag= -1, fl;
818         
819         if(bb==NULL) return 1;
820         if(bb->flag & OB_BB_DISABLED) return 1;
821         
822         mult_m4_m4m4(mat, rv3d->persmat, obmat);
823         
824         for(a=0; a<8; a++) {
825                 copy_v3_v3(vec, bb->vec[a]);
826                 vec[3]= 1.0;
827                 mul_m4_v4(mat, vec);
828                 max= vec[3];
829                 min= -vec[3];
830                 
831                 fl= 0;
832                 if(vec[0] < min) fl+= 1;
833                 if(vec[0] > max) fl+= 2;
834                 if(vec[1] < min) fl+= 4;
835                 if(vec[1] > max) fl+= 8;
836                 if(vec[2] < min) fl+= 16;
837                 if(vec[2] > max) fl+= 32;
838                 
839                 flag &= fl;
840                 if(flag==0) return 1;
841         }
842         
843         return 0;
844 }
845
846 void project_short(ARegion *ar, const float vec[3], short adr[2])       /* clips */
847 {
848         RegionView3D *rv3d= ar->regiondata;
849         float fx, fy, vec4[4];
850         
851         adr[0]= IS_CLIPPED;
852         
853         if(rv3d->rflag & RV3D_CLIPPING) {
854                 if(ED_view3d_test_clipping(rv3d, vec, 0))
855                         return;
856         }
857         
858         copy_v3_v3(vec4, vec);
859         vec4[3]= 1.0;
860         mul_m4_v4(rv3d->persmat, vec4);
861         
862         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
863                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
864                 
865                 if( fx>0 && fx<ar->winx) {
866                         
867                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
868                         
869                         if(fy > 0.0f && fy < (float)ar->winy) {
870                                 adr[0]= (short)floor(fx); 
871                                 adr[1]= (short)floor(fy);
872                         }
873                 }
874         }
875 }
876
877 void project_int(ARegion *ar, const float vec[3], int adr[2])
878 {
879         RegionView3D *rv3d= ar->regiondata;
880         float fx, fy, vec4[4];
881         
882         copy_v3_v3(vec4, vec);
883         vec4[3]= 1.0;
884         adr[0]= (int)2140000000.0f;
885         
886         mul_m4_v4(rv3d->persmat, vec4);
887         
888         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
889                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
890                 
891                 if( fx>-2140000000.0f && fx<2140000000.0f) {
892                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
893                         
894                         if(fy>-2140000000.0f && fy<2140000000.0f) {
895                                 adr[0]= (int)floor(fx); 
896                                 adr[1]= (int)floor(fy);
897                         }
898                 }
899         }
900 }
901
902 void project_int_noclip(ARegion *ar, const float vec[3], int adr[2])
903 {
904         RegionView3D *rv3d= ar->regiondata;
905         float fx, fy, vec4[4];
906         
907         copy_v3_v3(vec4, vec);
908         vec4[3]= 1.0;
909         
910         mul_m4_v4(rv3d->persmat, vec4);
911         
912         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
913                 fx = (ar->winx/2)*(1 + vec4[0]/vec4[3]);
914                 fy = (ar->winy/2)*(1 + vec4[1]/vec4[3]);
915                 
916                 adr[0] = (int)floor(fx); 
917                 adr[1] = (int)floor(fy);
918         }
919         else {
920                 adr[0] = ar->winx / 2;
921                 adr[1] = ar->winy / 2;
922         }
923 }
924
925 void project_short_noclip(ARegion *ar, const float vec[3], short adr[2])
926 {
927         RegionView3D *rv3d= ar->regiondata;
928         float fx, fy, vec4[4];
929         
930         copy_v3_v3(vec4, vec);
931         vec4[3]= 1.0;
932         adr[0]= IS_CLIPPED;
933         
934         mul_m4_v4(rv3d->persmat, vec4);
935         
936         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
937                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
938                 
939                 if( fx>-32700 && fx<32700) {
940                         
941                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
942                         
943                         if(fy > -32700.0f && fy < 32700.0f) {
944                                 adr[0]= (short)floor(fx); 
945                                 adr[1]= (short)floor(fy);
946                         }
947                 }
948         }
949 }
950
951 void apply_project_float(float persmat[4][4], int winx, int winy, const float vec[3], float adr[2])
952 {
953         float vec4[4];
954
955         copy_v3_v3(vec4, vec);
956         vec4[3]= 1.0;
957         adr[0]= IS_CLIPPED;
958
959         mul_m4_v4(persmat, vec4);
960
961         if(vec4[3] > (float)BL_NEAR_CLIP) {
962                 adr[0] = (float)(winx/2.0f)+(winx/2.0f)*vec4[0]/vec4[3];
963                 adr[1] = (float)(winy/2.0f)+(winy/2.0f)*vec4[1]/vec4[3];
964         }
965 }
966
967 void project_float(ARegion *ar, const float vec[3], float adr[2])
968 {
969         RegionView3D *rv3d= ar->regiondata;
970
971         apply_project_float(rv3d->persmat, ar->winx, ar->winy, vec, adr);
972 }
973
974 void project_float_noclip(ARegion *ar, const float vec[3], float adr[2])
975 {
976         RegionView3D *rv3d= ar->regiondata;
977         float vec4[4];
978         
979         copy_v3_v3(vec4, vec);
980         vec4[3]= 1.0;
981         
982         mul_m4_v4(rv3d->persmat, vec4);
983         
984         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
985                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
986                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
987         }
988         else {
989                 adr[0] = ar->winx / 2.0f;
990                 adr[1] = ar->winy / 2.0f;
991         }
992 }
993
994 /* copies logic of get_view3d_viewplane(), keep in sync */
995 int ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
996 {
997         CameraParams params;
998
999         camera_params_init(&params);
1000         camera_params_from_view3d(&params, v3d, rv3d);
1001
1002         if(clipsta) *clipsta= params.clipsta;
1003         if(clipend) *clipend= params.clipend;
1004
1005         return params.is_ortho;
1006 }
1007
1008 /* also exposed in previewrender.c */
1009 int ED_view3d_viewplane_get(View3D *v3d, RegionView3D *rv3d, int winx, int winy,
1010                             rctf *viewplane, float *clipsta, float *clipend)
1011 {
1012         CameraParams params;
1013
1014         camera_params_init(&params);
1015         camera_params_from_view3d(&params, v3d, rv3d);
1016         camera_params_compute_viewplane(&params, winx, winy, 1.0f, 1.0f);
1017
1018         if(viewplane) *viewplane= params.viewplane;
1019         if(clipsta) *clipsta= params.clipsta;
1020         if(clipend) *clipend= params.clipend;
1021         
1022         return params.is_ortho;
1023 }
1024
1025 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
1026 {
1027         RegionView3D *rv3d= ar->regiondata;
1028         rctf viewplane;
1029         float clipsta, clipend, x1, y1, x2, y2;
1030         int orth;
1031         
1032         orth= ED_view3d_viewplane_get(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend);
1033         rv3d->is_persp= !orth;
1034
1035 #if 0
1036         printf("%s: %d %d %f %f %f %f %f %f\n", __func__, winx, winy,
1037                viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax,
1038                clipsta, clipend);
1039 #endif
1040
1041         x1= viewplane.xmin;
1042         y1= viewplane.ymin;
1043         x2= viewplane.xmax;
1044         y2= viewplane.ymax;
1045         
1046         if(rect) {              /* picking */
1047                 rect->xmin/= (float)ar->winx;
1048                 rect->xmin= x1+rect->xmin*(x2-x1);
1049                 rect->ymin/= (float)ar->winy;
1050                 rect->ymin= y1+rect->ymin*(y2-y1);
1051                 rect->xmax/= (float)ar->winx;
1052                 rect->xmax= x1+rect->xmax*(x2-x1);
1053                 rect->ymax/= (float)ar->winy;
1054                 rect->ymax= y1+rect->ymax*(y2-y1);
1055                 
1056                 if(orth) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
1057                 else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
1058                 
1059         }
1060         else {
1061                 if(orth) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
1062                 else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
1063         }
1064
1065         /* update matrix in 3d view region */
1066         glGetFloatv(GL_PROJECTION_MATRIX, (float*)rv3d->winmat);
1067 }
1068
1069 static void obmat_to_viewmat(View3D *v3d, RegionView3D *rv3d, Object *ob, short smooth)
1070 {
1071         float bmat[4][4];
1072         float tmat[3][3];
1073         
1074         rv3d->view= RV3D_VIEW_USER; /* dont show the grid */
1075         
1076         copy_m4_m4(bmat, ob->obmat);
1077         normalize_m4(bmat);
1078         invert_m4_m4(rv3d->viewmat, bmat);
1079         
1080         /* view quat calculation, needed for add object */
1081         copy_m3_m4(tmat, rv3d->viewmat);
1082         if (smooth) {
1083                 float new_quat[4];
1084                 if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1085                         /* were from a camera view */
1086                         
1087                         float orig_ofs[3];
1088                         float orig_dist= rv3d->dist;
1089                         float orig_lens= v3d->lens;
1090                         copy_v3_v3(orig_ofs, rv3d->ofs);
1091                         
1092                         /* Switch from camera view */
1093                         mat3_to_quat( new_quat,tmat);
1094                         
1095                         rv3d->persp=RV3D_PERSP;
1096                         rv3d->dist= 0.0;
1097                         
1098                         ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1099                         smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1100                         
1101                         rv3d->persp=RV3D_CAMOB; /* just to be polite, not needed */
1102                         
1103                 } else {
1104                         mat3_to_quat( new_quat,tmat);
1105                         smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1106                 }
1107         } else {
1108                 mat3_to_quat( rv3d->viewquat,tmat);
1109         }
1110 }
1111
1112 #define QUATSET(a, b, c, d, e)  a[0]=b; a[1]=c; a[2]=d; a[3]=e; 
1113
1114 int ED_view3d_lock(RegionView3D *rv3d)
1115 {
1116         switch(rv3d->view) {
1117         case RV3D_VIEW_BOTTOM :
1118                 QUATSET(rv3d->viewquat,0.0, -1.0, 0.0, 0.0);
1119                 break;
1120
1121         case RV3D_VIEW_BACK:
1122                 QUATSET(rv3d->viewquat,0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0));
1123                 break;
1124
1125         case RV3D_VIEW_LEFT:
1126                 QUATSET(rv3d->viewquat,0.5, -0.5, 0.5, 0.5);
1127                 break;
1128
1129         case RV3D_VIEW_TOP:
1130                 QUATSET(rv3d->viewquat,1.0, 0.0, 0.0, 0.0);
1131                 break;
1132
1133         case RV3D_VIEW_FRONT:
1134                 QUATSET(rv3d->viewquat,(float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0);
1135                 break;
1136
1137         case RV3D_VIEW_RIGHT:
1138                 QUATSET(rv3d->viewquat, 0.5, -0.5, -0.5, -0.5);
1139                 break;
1140         default:
1141                 return FALSE;
1142         }
1143
1144         return TRUE;
1145 }
1146
1147 /* dont set windows active in here, is used by renderwin too */
1148 void setviewmatrixview3d(Scene *scene, View3D *v3d, RegionView3D *rv3d)
1149 {
1150         if(rv3d->persp==RV3D_CAMOB) {       /* obs/camera */
1151                 if(v3d->camera) {
1152                         where_is_object(scene, v3d->camera);    
1153                         obmat_to_viewmat(v3d, rv3d, v3d->camera, 0);
1154                 }
1155                 else {
1156                         quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1157                         rv3d->viewmat[3][2]-= rv3d->dist;
1158                 }
1159         }
1160         else {
1161                 /* should be moved to better initialize later on XXX */
1162                 if(rv3d->viewlock)
1163                         ED_view3d_lock(rv3d);
1164                 
1165                 quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1166                 if(rv3d->persp==RV3D_PERSP) rv3d->viewmat[3][2]-= rv3d->dist;
1167                 if(v3d->ob_centre) {
1168                         Object *ob= v3d->ob_centre;
1169                         float vec[3];
1170                         
1171                         copy_v3_v3(vec, ob->obmat[3]);
1172                         if(ob->type==OB_ARMATURE && v3d->ob_centre_bone[0]) {
1173                                 bPoseChannel *pchan= get_pose_channel(ob->pose, v3d->ob_centre_bone);
1174                                 if(pchan) {
1175                                         copy_v3_v3(vec, pchan->pose_mat[3]);
1176                                         mul_m4_v3(ob->obmat, vec);
1177                                 }
1178                         }
1179                         translate_m4( rv3d->viewmat,-vec[0], -vec[1], -vec[2]);
1180                 }
1181                 else if (v3d->ob_centre_cursor) {
1182                         float vec[3];
1183                         copy_v3_v3(vec, give_cursor(scene, v3d));
1184                         translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]);
1185                 }
1186                 else translate_m4( rv3d->viewmat,rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
1187         }
1188 }
1189
1190 /* IGLuint-> GLuint*/
1191 /* Warning: be sure to account for a negative return value
1192 *   This is an error, "Too many objects in select buffer"
1193 *   and no action should be taken (can crash blender) if this happens
1194 */
1195 short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
1196 {
1197         Scene *scene= vc->scene;
1198         View3D *v3d= vc->v3d;
1199         ARegion *ar= vc->ar;
1200         rctf rect;
1201         short code, hits;
1202         char dt, dtx;
1203         
1204         G.f |= G_PICKSEL;
1205         
1206         /* case not a border select */
1207         if(input->xmin==input->xmax) {
1208                 rect.xmin= input->xmin-12;      // seems to be default value for bones only now
1209                 rect.xmax= input->xmin+12;
1210                 rect.ymin= input->ymin-12;
1211                 rect.ymax= input->ymin+12;
1212         }
1213         else {
1214                 rect.xmin= input->xmin;
1215                 rect.xmax= input->xmax;
1216                 rect.ymin= input->ymin;
1217                 rect.ymax= input->ymax;
1218         }
1219         
1220         setwinmatrixview3d(ar, v3d, &rect);
1221         mult_m4_m4m4(vc->rv3d->persmat, vc->rv3d->winmat, vc->rv3d->viewmat);
1222         
1223         if(v3d->drawtype > OB_WIRE) {
1224                 v3d->zbuf= TRUE;
1225                 glEnable(GL_DEPTH_TEST);
1226         }
1227         
1228         if(vc->rv3d->rflag & RV3D_CLIPPING)
1229                 view3d_set_clipping(vc->rv3d);
1230         
1231         glSelectBuffer( bufsize, (GLuint *)buffer);
1232         glRenderMode(GL_SELECT);
1233         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1234         glPushName(-1);
1235         code= 1;
1236         
1237         if(vc->obedit && vc->obedit->type==OB_MBALL) {
1238                 draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1239         }
1240         else if((vc->obedit && vc->obedit->type==OB_ARMATURE)) {
1241                 /* if not drawing sketch, draw bones */
1242                 if(!BDR_drawSketchNames(vc)) {
1243                         draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1244                 }
1245         }
1246         else {
1247                 Base *base;
1248                 
1249                 v3d->xray= TRUE;        // otherwise it postpones drawing
1250                 for(base= scene->base.first; base; base= base->next) {
1251                         if(base->lay & v3d->lay) {
1252                                 
1253                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1254                                         base->selcol= 0;
1255                                 else {
1256                                         base->selcol= code;
1257                                         glLoadName(code);
1258                                         draw_object(scene, ar, v3d, base, DRAW_PICKING|DRAW_CONSTCOLOR);
1259                                         
1260                                         /* we draw group-duplicators for selection too */
1261                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1262                                                 ListBase *lb;
1263                                                 DupliObject *dob;
1264                                                 Base tbase;
1265                                                 
1266                                                 tbase.flag= OB_FROMDUPLI;
1267                                                 lb= object_duplilist(scene, base->object);
1268                                                 
1269                                                 for(dob= lb->first; dob; dob= dob->next) {
1270                                                         tbase.object= dob->ob;
1271                                                         copy_m4_m4(dob->ob->obmat, dob->mat);
1272                                                         
1273                                                         /* extra service: draw the duplicator in drawtype of parent */
1274                                                         /* MIN2 for the drawtype to allow bounding box objects in groups for lods */
1275                                                         dt= tbase.object->dt;   tbase.object->dt= MIN2(tbase.object->dt, base->object->dt);
1276                                                         dtx= tbase.object->dtx; tbase.object->dtx= base->object->dtx;
1277
1278                                                         draw_object(scene, ar, v3d, &tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1279                                                         
1280                                                         tbase.object->dt= dt;
1281                                                         tbase.object->dtx= dtx;
1282
1283                                                         copy_m4_m4(dob->ob->obmat, dob->omat);
1284                                                 }
1285                                                 free_object_duplilist(lb);
1286                                         }
1287                                         code++;
1288                                 }                               
1289                         }
1290                 }
1291                 v3d->xray= FALSE;       // restore
1292         }
1293         
1294         glPopName();    /* see above (pushname) */
1295         hits= glRenderMode(GL_RENDER);
1296         
1297         G.f &= ~G_PICKSEL;
1298         setwinmatrixview3d(ar, v3d, NULL);
1299         mult_m4_m4m4(vc->rv3d->persmat, vc->rv3d->winmat, vc->rv3d->viewmat);
1300         
1301         if(v3d->drawtype > OB_WIRE) {
1302                 v3d->zbuf= 0;
1303                 glDisable(GL_DEPTH_TEST);
1304         }
1305 // XXX  persp(PERSP_WIN);
1306         
1307         if(vc->rv3d->rflag & RV3D_CLIPPING)
1308                 view3d_clr_clipping();
1309         
1310         if(hits<0) printf("Too many objects in select buffer\n");       // XXX make error message
1311         
1312         return hits;
1313 }
1314
1315 /* ********************** local view operator ******************** */
1316
1317 static unsigned int free_localbit(Main *bmain)
1318 {
1319         unsigned int lay;
1320         ScrArea *sa;
1321         bScreen *sc;
1322         
1323         lay= 0;
1324         
1325         /* sometimes we loose a localview: when an area is closed */
1326         /* check all areas: which localviews are in use? */
1327         for(sc= bmain->screen.first; sc; sc= sc->id.next) {
1328                 for(sa= sc->areabase.first; sa; sa= sa->next) {
1329                         SpaceLink *sl= sa->spacedata.first;
1330                         for(; sl; sl= sl->next) {
1331                                 if(sl->spacetype==SPACE_VIEW3D) {
1332                                         View3D *v3d= (View3D*) sl;
1333                                         lay |= v3d->lay;
1334                                 }
1335                         }
1336                 }
1337         }
1338         
1339         if( (lay & 0x01000000)==0) return 0x01000000;
1340         if( (lay & 0x02000000)==0) return 0x02000000;
1341         if( (lay & 0x04000000)==0) return 0x04000000;
1342         if( (lay & 0x08000000)==0) return 0x08000000;
1343         if( (lay & 0x10000000)==0) return 0x10000000;
1344         if( (lay & 0x20000000)==0) return 0x20000000;
1345         if( (lay & 0x40000000)==0) return 0x40000000;
1346         if( (lay & 0x80000000)==0) return 0x80000000;
1347         
1348         return 0;
1349 }
1350
1351 int ED_view3d_scene_layer_set(int lay, const int *values, int *active)
1352 {
1353         int i, tot= 0;
1354         
1355         /* ensure we always have some layer selected */
1356         for(i=0; i<20; i++)
1357                 if(values[i])
1358                         tot++;
1359         
1360         if(tot==0)
1361                 return lay;
1362         
1363         for(i=0; i<20; i++) {
1364                 
1365                 if (active) {
1366                         /* if this value has just been switched on, make that layer active */
1367                         if (values[i] && (lay & (1<<i))==0) {
1368                                 *active = (1<<i);
1369                         }
1370                 }
1371                         
1372                 if (values[i]) lay |= (1<<i);
1373                 else lay &= ~(1<<i);
1374         }
1375         
1376         /* ensure always an active layer */
1377         if (active && (lay & *active)==0) {
1378                 for(i=0; i<20; i++) {
1379                         if(lay & (1<<i)) {
1380                                 *active= 1<<i;
1381                                 break;
1382                         }
1383                 }
1384         }
1385         
1386         return lay;
1387 }
1388
1389 static void initlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1390 {
1391         View3D *v3d= sa->spacedata.first;
1392         Base *base;
1393         float size = 0.0, min[3], max[3], box[3];
1394         unsigned int locallay;
1395         int ok=0;
1396
1397         if(v3d->localvd) return;
1398
1399         INIT_MINMAX(min, max);
1400
1401         locallay= free_localbit(bmain);
1402
1403         if(locallay==0) {
1404                 printf("Sorry, no more than 8 localviews\n");   // XXX error 
1405                 ok= 0;
1406         }
1407         else {
1408                 if(scene->obedit) {
1409                         minmax_object(scene->obedit, min, max);
1410                         
1411                         ok= 1;
1412                 
1413                         BASACT->lay |= locallay;
1414                         scene->obedit->lay= BASACT->lay;
1415                 }
1416                 else {
1417                         for(base= FIRSTBASE; base; base= base->next) {
1418                                 if(TESTBASE(v3d, base))  {
1419                                         minmax_object(base->object, min, max);
1420                                         base->lay |= locallay;
1421                                         base->object->lay= base->lay;
1422                                         ok= 1;
1423                                 }
1424                         }
1425                 }
1426                 
1427                 box[0]= (max[0]-min[0]);
1428                 box[1]= (max[1]-min[1]);
1429                 box[2]= (max[2]-min[2]);
1430                 size= MAX3(box[0], box[1], box[2]);
1431                 if(size <= 0.01f) size= 0.01f;
1432         }
1433         
1434         if(ok) {
1435                 ARegion *ar;
1436                 
1437                 v3d->localvd= MEM_mallocN(sizeof(View3D), "localview");
1438                 
1439                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1440
1441                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
1442                         if(ar->regiontype == RGN_TYPE_WINDOW) {
1443                                 RegionView3D *rv3d= ar->regiondata;
1444
1445                                 rv3d->localvd= MEM_mallocN(sizeof(RegionView3D), "localview region");
1446                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1447                                 
1448                                 rv3d->ofs[0]= -(min[0]+max[0])/2.0f;
1449                                 rv3d->ofs[1]= -(min[1]+max[1])/2.0f;
1450                                 rv3d->ofs[2]= -(min[2]+max[2])/2.0f;
1451
1452                                 rv3d->dist= size;
1453                                 /* perspective should be a bit farther away to look nice */
1454                                 if(rv3d->persp==RV3D_ORTHO)
1455                                         rv3d->dist*= 0.7f;
1456
1457                                 // correction for window aspect ratio
1458                                 if(ar->winy>2 && ar->winx>2) {
1459                                         float asp= (float)ar->winx/(float)ar->winy;
1460                                         if(asp < 1.0f) asp= 1.0f/asp;
1461                                         rv3d->dist*= asp;
1462                                 }
1463                                 
1464                                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP;
1465                                 
1466                                 v3d->cursor[0]= -rv3d->ofs[0];
1467                                 v3d->cursor[1]= -rv3d->ofs[1];
1468                                 v3d->cursor[2]= -rv3d->ofs[2];
1469                         }
1470                 }
1471                 
1472                 v3d->lay= locallay;
1473         }
1474         else {
1475                 /* clear flags */ 
1476                 for(base= FIRSTBASE; base; base= base->next) {
1477                         if( base->lay & locallay ) {
1478                                 base->lay-= locallay;
1479                                 if(base->lay==0) base->lay= v3d->layact;
1480                                 if(base->object != scene->obedit) base->flag |= SELECT;
1481                                 base->object->lay= base->lay;
1482                         }
1483                 }               
1484         }
1485
1486 }
1487
1488 static void restore_localviewdata(ScrArea *sa, int free)
1489 {
1490         ARegion *ar;
1491         View3D *v3d= sa->spacedata.first;
1492         
1493         if(v3d->localvd==NULL) return;
1494         
1495         v3d->near= v3d->localvd->near;
1496         v3d->far= v3d->localvd->far;
1497         v3d->lay= v3d->localvd->lay;
1498         v3d->layact= v3d->localvd->layact;
1499         v3d->drawtype= v3d->localvd->drawtype;
1500         v3d->camera= v3d->localvd->camera;
1501         
1502         if(free) {
1503                 MEM_freeN(v3d->localvd);
1504                 v3d->localvd= NULL;
1505         }
1506         
1507         for(ar= sa->regionbase.first; ar; ar= ar->next) {
1508                 if(ar->regiontype == RGN_TYPE_WINDOW) {
1509                         RegionView3D *rv3d= ar->regiondata;
1510                         
1511                         if(rv3d->localvd) {
1512                                 rv3d->dist= rv3d->localvd->dist;
1513                                 copy_v3_v3(rv3d->ofs, rv3d->localvd->ofs);
1514                                 copy_qt_qt(rv3d->viewquat, rv3d->localvd->viewquat);
1515                                 rv3d->view= rv3d->localvd->view;
1516                                 rv3d->persp= rv3d->localvd->persp;
1517                                 rv3d->camzoom= rv3d->localvd->camzoom;
1518
1519                                 if(free) {
1520                                         MEM_freeN(rv3d->localvd);
1521                                         rv3d->localvd= NULL;
1522                                 }
1523                         }
1524                 }
1525         }
1526 }
1527
1528 static void endlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1529 {
1530         View3D *v3d= sa->spacedata.first;
1531         struct Base *base;
1532         unsigned int locallay;
1533         
1534         if(v3d->localvd) {
1535                 
1536                 locallay= v3d->lay & 0xFF000000;
1537                 
1538                 restore_localviewdata(sa, 1); // 1 = free
1539
1540                 /* for when in other window the layers have changed */
1541                 if(v3d->scenelock) v3d->lay= scene->lay;
1542                 
1543                 for(base= FIRSTBASE; base; base= base->next) {
1544                         if( base->lay & locallay ) {
1545                                 base->lay-= locallay;
1546                                 if(base->lay==0) base->lay= v3d->layact;
1547                                 if(base->object != scene->obedit) {
1548                                         base->flag |= SELECT;
1549                                         base->object->flag |= SELECT;
1550                                 }
1551                                 base->object->lay= base->lay;
1552                         }
1553                 }
1554                 
1555                 DAG_on_visible_update(bmain, FALSE);
1556         } 
1557 }
1558
1559 static int localview_exec(bContext *C, wmOperator *UNUSED(unused))
1560 {
1561         View3D *v3d= CTX_wm_view3d(C);
1562         
1563         if(v3d->localvd)
1564                 endlocalview(CTX_data_main(C), CTX_data_scene(C), CTX_wm_area(C));
1565         else
1566                 initlocalview(CTX_data_main(C), CTX_data_scene(C), CTX_wm_area(C));
1567         
1568         ED_area_tag_redraw(CTX_wm_area(C));
1569         
1570         return OPERATOR_FINISHED;
1571 }
1572
1573 void VIEW3D_OT_localview(wmOperatorType *ot)
1574 {
1575         
1576         /* identifiers */
1577         ot->name= "Local View";
1578         ot->description= "Toggle display of selected object(s) separately and centered in view";
1579         ot->idname= "VIEW3D_OT_localview";
1580         
1581         /* api callbacks */
1582         ot->exec= localview_exec;
1583         ot->flag= OPTYPE_UNDO; /* localview changes object layer bitflags */
1584         
1585         ot->poll= ED_operator_view3d_active;
1586 }
1587
1588 #ifdef WITH_GAMEENGINE
1589
1590 static ListBase queue_back;
1591 static void SaveState(bContext *C, wmWindow *win)
1592 {
1593         Object *obact = CTX_data_active_object(C);
1594         
1595         glPushAttrib(GL_ALL_ATTRIB_BITS);
1596
1597         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1598                 GPU_paint_set_mipmap(1);
1599         
1600         queue_back= win->queue;
1601         
1602         win->queue.first= win->queue.last= NULL;
1603         
1604         //XXX waitcursor(1);
1605 }
1606
1607 static void RestoreState(bContext *C, wmWindow *win)
1608 {
1609         Object *obact = CTX_data_active_object(C);
1610         
1611         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1612                 GPU_paint_set_mipmap(0);
1613
1614         //XXX curarea->win_swap = 0;
1615         //XXX curarea->head_swap=0;
1616         //XXX allqueue(REDRAWVIEW3D, 1);
1617         //XXX allqueue(REDRAWBUTSALL, 0);
1618         //XXX reset_slowparents();
1619         //XXX waitcursor(0);
1620         //XXX G.qual= 0;
1621         
1622         if(win) /* check because closing win can set to NULL */
1623                 win->queue= queue_back;
1624         
1625         GPU_state_init();
1626         GPU_set_tpage(NULL, 0, 0);
1627
1628         glPopAttrib();
1629 }
1630
1631 /* was space_set_commmandline_options in 2.4x */
1632 static void game_set_commmandline_options(GameData *gm)
1633 {
1634         SYS_SystemHandle syshandle;
1635         int test;
1636
1637         if ( (syshandle = SYS_GetSystem()) ) {
1638                 /* User defined settings */
1639                 test= (U.gameflags & USER_DISABLE_MIPMAP);
1640                 GPU_set_mipmap(!test);
1641                 SYS_WriteCommandLineInt(syshandle, "nomipmap", test);
1642
1643                 /* File specific settings: */
1644                 /* Only test the first one. These two are switched
1645                  * simultaneously. */
1646                 test= (gm->flag & GAME_SHOW_FRAMERATE);
1647                 SYS_WriteCommandLineInt(syshandle, "show_framerate", test);
1648                 SYS_WriteCommandLineInt(syshandle, "show_profile", test);
1649
1650                 test = (gm->flag & GAME_SHOW_DEBUG_PROPS);
1651                 SYS_WriteCommandLineInt(syshandle, "show_properties", test);
1652
1653                 test= (gm->flag & GAME_SHOW_PHYSICS);
1654                 SYS_WriteCommandLineInt(syshandle, "show_physics", test);
1655
1656                 test= (gm->flag & GAME_ENABLE_ALL_FRAMES);
1657                 SYS_WriteCommandLineInt(syshandle, "fixedtime", test);
1658
1659                 test= (gm->flag & GAME_ENABLE_ANIMATION_RECORD);
1660                 SYS_WriteCommandLineInt(syshandle, "animation_record", test);
1661
1662                 test= (gm->flag & GAME_IGNORE_DEPRECATION_WARNINGS);
1663                 SYS_WriteCommandLineInt(syshandle, "ignore_deprecation_warnings", test);
1664
1665                 test= (gm->matmode == GAME_MAT_MULTITEX);
1666                 SYS_WriteCommandLineInt(syshandle, "blender_material", test);
1667                 test= (gm->matmode == GAME_MAT_GLSL);
1668                 SYS_WriteCommandLineInt(syshandle, "blender_glsl_material", test);
1669                 test= (gm->flag & GAME_DISPLAY_LISTS);
1670                 SYS_WriteCommandLineInt(syshandle, "displaylists", test);
1671
1672
1673         }
1674 }
1675
1676 #endif // WITH_GAMEENGINE
1677
1678 static int game_engine_poll(bContext *C)
1679 {
1680         /* we need a context and area to launch BGE
1681         it's a temporary solution to avoid crash at load time
1682         if we try to auto run the BGE. Ideally we want the
1683         context to be set as soon as we load the file. */
1684
1685         if(CTX_wm_window(C)==NULL) return 0;
1686         if(CTX_wm_screen(C)==NULL) return 0;
1687         if(CTX_wm_area(C)==NULL) return 0;
1688
1689         if(CTX_data_mode_enum(C)!=CTX_MODE_OBJECT)
1690                 return 0;
1691
1692         return 1;
1693 }
1694
1695 int ED_view3d_context_activate(bContext *C)
1696 {
1697         bScreen *sc= CTX_wm_screen(C);
1698         ScrArea *sa= CTX_wm_area(C);
1699         ARegion *ar;
1700
1701         /* sa can be NULL when called from python */
1702         if(sa==NULL || sa->spacetype != SPACE_VIEW3D)
1703                 for(sa=sc->areabase.first; sa; sa= sa->next)
1704                         if(sa->spacetype==SPACE_VIEW3D)
1705                                 break;
1706
1707         if(!sa)
1708                 return 0;
1709         
1710         for(ar=sa->regionbase.first; ar; ar=ar->next)
1711                 if(ar->regiontype == RGN_TYPE_WINDOW)
1712                         break;
1713         
1714         if(!ar)
1715                 return 0;
1716         
1717         // bad context switch ..
1718         CTX_wm_area_set(C, sa);
1719         CTX_wm_region_set(C, ar);
1720
1721         return 1;
1722 }
1723
1724 static int game_engine_exec(bContext *C, wmOperator *op)
1725 {
1726 #ifdef WITH_GAMEENGINE
1727         Scene *startscene = CTX_data_scene(C);
1728         ScrArea /* *sa, */ /* UNUSED */ *prevsa= CTX_wm_area(C);
1729         ARegion *ar, *prevar= CTX_wm_region(C);
1730         wmWindow *prevwin= CTX_wm_window(C);
1731         RegionView3D *rv3d;
1732         rcti cam_frame;
1733
1734         (void)op; /* unused */
1735         
1736         // bad context switch ..
1737         if(!ED_view3d_context_activate(C))
1738                 return OPERATOR_CANCELLED;
1739         
1740         /* redraw to hide any menus/popups, we don't go back to
1741            the window manager until after this operator exits */
1742         WM_redraw_windows(C);
1743
1744         rv3d= CTX_wm_region_view3d(C);
1745         /* sa= CTX_wm_area(C); */ /* UNUSED */
1746         ar= CTX_wm_region(C);
1747
1748         view3d_operator_needs_opengl(C);
1749         
1750         game_set_commmandline_options(&startscene->gm);
1751
1752         if((rv3d->persp == RV3D_CAMOB) &&
1753            (startscene->gm.framing.type == SCE_GAMEFRAMING_BARS) &&
1754            (startscene->gm.stereoflag != STEREO_DOME))
1755         {
1756                 /* Letterbox */
1757                 rctf cam_framef;
1758                 ED_view3d_calc_camera_border(startscene, ar, CTX_wm_view3d(C), rv3d, &cam_framef, FALSE);
1759                 cam_frame.xmin = cam_framef.xmin + ar->winrct.xmin;
1760                 cam_frame.xmax = cam_framef.xmax + ar->winrct.xmin;
1761                 cam_frame.ymin = cam_framef.ymin + ar->winrct.ymin;
1762                 cam_frame.ymax = cam_framef.ymax + ar->winrct.ymin;
1763                 BLI_isect_rcti(&ar->winrct, &cam_frame, &cam_frame);
1764         }
1765         else {
1766                 cam_frame.xmin = ar->winrct.xmin;
1767                 cam_frame.xmax = ar->winrct.xmax;
1768                 cam_frame.ymin = ar->winrct.ymin;
1769                 cam_frame.ymax = ar->winrct.ymax;
1770         }
1771
1772
1773         SaveState(C, prevwin);
1774
1775         StartKetsjiShell(C, ar, &cam_frame, 1);
1776
1777         /* window wasnt closed while the BGE was running */
1778         if(BLI_findindex(&CTX_wm_manager(C)->windows, prevwin) == -1) {
1779                 prevwin= NULL;
1780                 CTX_wm_window_set(C, NULL);
1781         }
1782         
1783         ED_area_tag_redraw(CTX_wm_area(C));
1784
1785         if(prevwin) {
1786                 /* restore context, in case it changed in the meantime, for
1787                    example by working in another window or closing it */
1788                 CTX_wm_region_set(C, prevar);
1789                 CTX_wm_window_set(C, prevwin);
1790                 CTX_wm_area_set(C, prevsa);
1791         }
1792
1793         RestoreState(C, prevwin);
1794
1795         //XXX restore_all_scene_cfra(scene_cfra_store);
1796         set_scene_bg(CTX_data_main(C), startscene);
1797         //XXX scene_update_for_newframe(bmain, scene, scene->lay);
1798
1799         return OPERATOR_FINISHED;
1800 #else
1801         (void)C; /* unused */
1802         BKE_report(op->reports, RPT_ERROR, "Game engine is disabled in this build");
1803         return OPERATOR_CANCELLED;
1804 #endif
1805 }
1806
1807 void VIEW3D_OT_game_start(wmOperatorType *ot)
1808 {
1809         
1810         /* identifiers */
1811         ot->name= "Start Game Engine";
1812         ot->description= "Start game engine";
1813         ot->idname= "VIEW3D_OT_game_start";
1814         
1815         /* api callbacks */
1816         ot->exec= game_engine_exec;
1817         
1818         ot->poll= game_engine_poll;
1819 }
1820
1821 /* ************************************** */
1822
1823 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
1824 {
1825         float alignaxis[3] = {0.0, 0.0, 0.0};
1826         float norm[3], axis[3], angle, new_quat[4];
1827         
1828         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
1829         else alignaxis[-axisidx-1]= -1.0;
1830
1831         normalize_v3_v3(norm, vec);
1832
1833         angle= (float)acos(dot_v3v3(alignaxis, norm));
1834         cross_v3_v3v3(axis, alignaxis, norm);
1835         axis_angle_to_quat( new_quat,axis, -angle);
1836         
1837         rv3d->view= RV3D_VIEW_USER;
1838         
1839         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1840                 /* switch out of camera view */
1841                 float orig_ofs[3];
1842                 float orig_dist= rv3d->dist;
1843                 float orig_lens= v3d->lens;
1844                 
1845                 copy_v3_v3(orig_ofs, rv3d->ofs);
1846                 rv3d->persp= RV3D_PERSP;
1847                 rv3d->dist= 0.0;
1848                 ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1849                 smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1850         } else {
1851                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP; /* switch out of camera mode */
1852                 smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1853         }
1854 }
1855
1856 float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3])
1857 {
1858         return  (rv3d->persmat[3][3] + (
1859                                 rv3d->persmat[0][3]*co[0] +
1860                                 rv3d->persmat[1][3]*co[1] +
1861                                 rv3d->persmat[2][3]*co[2])
1862                         ) * rv3d->pixsize;
1863 }
1864
1865 /* view matrix properties utilities */
1866
1867 void ED_view3d_operator_properties_viewmat(wmOperatorType *ot)
1868 {
1869         PropertyRNA *prop;
1870
1871         prop = RNA_def_int(ot->srna, "region_width", 0, 0, INT_MAX, "Region Width", "", 0, INT_MAX);
1872         RNA_def_property_flag(prop, PROP_HIDDEN);
1873
1874         prop = RNA_def_int(ot->srna, "region_height", 0, 0, INT_MAX, "Region height", "", 0, INT_MAX);
1875         RNA_def_property_flag(prop, PROP_HIDDEN);
1876
1877         prop = RNA_def_float_matrix(ot->srna, "perspective_matrix", 4, 4, NULL, 0.0f, 0.0f, "", "Perspective Matrix", 0.0f, 0.0f);
1878         RNA_def_property_flag(prop, PROP_HIDDEN);
1879 }
1880
1881 void ED_view3d_operator_properties_viewmat_set(bContext *C, wmOperator *op)
1882 {
1883         ARegion *ar= CTX_wm_region(C);
1884         RegionView3D *rv3d= ED_view3d_context_rv3d(C);
1885
1886         if(!RNA_struct_property_is_set(op->ptr, "region_width"))
1887                 RNA_int_set(op->ptr, "region_width", ar->winx);
1888
1889         if(!RNA_struct_property_is_set(op->ptr, "region_height"))
1890                 RNA_int_set(op->ptr, "region_height", ar->winy);
1891
1892         if(!RNA_struct_property_is_set(op->ptr, "perspective_matrix"))
1893                 RNA_float_set_array(op->ptr, "perspective_matrix", (float *)rv3d->persmat);
1894 }
1895
1896 void ED_view3d_operator_properties_viewmat_get(wmOperator *op, int *winx, int *winy, float persmat[4][4])
1897 {
1898         *winx = RNA_int_get(op->ptr, "region_width");
1899         *winy = RNA_int_get(op->ptr, "region_height");
1900
1901         RNA_float_get_array(op->ptr, "perspective_matrix", (float *)persmat);
1902 }