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