svn merge ^/trunk/blender -r41961:41998
[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 "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, 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         mul_m4_m4m4(vmat, ob->obmat, rv3d->viewmat);
744         mul_m4_m4m4(pmat, vmat, rv3d->winmat);
745 }
746
747 /* Uses window coordinates (x,y) and depth component z to find a point in
748    modelspace */
749 void view3d_unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
750 {
751         double ux, uy, uz;
752
753                 gluUnProject(x,y,z, mats->modelview, mats->projection,
754                          (GLint *)mats->viewport, &ux, &uy, &uz );
755         out[0] = ux;
756         out[1] = uy;
757         out[2] = uz;
758 }
759
760 /* use view3d_get_object_project_mat to get projecting mat */
761 void ED_view3d_project_float(ARegion *ar, const float vec[3], float adr[2], float mat[4][4])
762 {
763         float vec4[4];
764         
765         copy_v3_v3(vec4, vec);
766         vec4[3]= 1.0;
767         adr[0]= IS_CLIPPED;
768         
769         mul_m4_v4(mat, vec4);
770         
771         if( vec4[3]>FLT_EPSILON ) {
772                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
773                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
774         } else {
775                 adr[0] = adr[1] = 0.0f;
776         }
777 }
778
779 /* use view3d_get_object_project_mat to get projecting mat */
780 void ED_view3d_project_float_v3(ARegion *ar, float *vec, float *adr, float mat[4][4])
781 {
782         float vec4[4];
783         
784         copy_v3_v3(vec4, vec);
785         vec4[3]= 1.0;
786         adr[0]= IS_CLIPPED;
787         
788         mul_m4_v4(mat, vec4);
789         
790         if( vec4[3]>FLT_EPSILON ) {
791                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
792                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
793                 adr[2] = vec4[2]/vec4[3];
794         } else {
795                 adr[0] = adr[1] = adr[2] = 0.0f;
796         }
797 }
798
799 int ED_view3d_boundbox_clip(RegionView3D *rv3d, float obmat[][4], BoundBox *bb)
800 {
801         /* return 1: draw */
802         
803         float mat[4][4];
804         float vec[4], min, max;
805         int a, flag= -1, fl;
806         
807         if(bb==NULL) return 1;
808         if(bb->flag & OB_BB_DISABLED) return 1;
809         
810         mul_m4_m4m4(mat, obmat, rv3d->persmat);
811         
812         for(a=0; a<8; a++) {
813                 copy_v3_v3(vec, bb->vec[a]);
814                 vec[3]= 1.0;
815                 mul_m4_v4(mat, vec);
816                 max= vec[3];
817                 min= -vec[3];
818                 
819                 fl= 0;
820                 if(vec[0] < min) fl+= 1;
821                 if(vec[0] > max) fl+= 2;
822                 if(vec[1] < min) fl+= 4;
823                 if(vec[1] > max) fl+= 8;
824                 if(vec[2] < min) fl+= 16;
825                 if(vec[2] > max) fl+= 32;
826                 
827                 flag &= fl;
828                 if(flag==0) return 1;
829         }
830         
831         return 0;
832 }
833
834 void project_short(ARegion *ar, const float vec[3], short adr[2])       /* clips */
835 {
836         RegionView3D *rv3d= ar->regiondata;
837         float fx, fy, vec4[4];
838         
839         adr[0]= IS_CLIPPED;
840         
841         if(rv3d->rflag & RV3D_CLIPPING) {
842                 if(ED_view3d_test_clipping(rv3d, vec, 0))
843                         return;
844         }
845         
846         copy_v3_v3(vec4, vec);
847         vec4[3]= 1.0;
848         mul_m4_v4(rv3d->persmat, vec4);
849         
850         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
851                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
852                 
853                 if( fx>0 && fx<ar->winx) {
854                         
855                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
856                         
857                         if(fy > 0.0f && fy < (float)ar->winy) {
858                                 adr[0]= (short)floor(fx); 
859                                 adr[1]= (short)floor(fy);
860                         }
861                 }
862         }
863 }
864
865 void project_int(ARegion *ar, const float vec[3], int adr[2])
866 {
867         RegionView3D *rv3d= ar->regiondata;
868         float fx, fy, vec4[4];
869         
870         copy_v3_v3(vec4, vec);
871         vec4[3]= 1.0;
872         adr[0]= (int)2140000000.0f;
873         
874         mul_m4_v4(rv3d->persmat, vec4);
875         
876         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
877                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
878                 
879                 if( fx>-2140000000.0f && fx<2140000000.0f) {
880                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
881                         
882                         if(fy>-2140000000.0f && fy<2140000000.0f) {
883                                 adr[0]= (int)floor(fx); 
884                                 adr[1]= (int)floor(fy);
885                         }
886                 }
887         }
888 }
889
890 void project_int_noclip(ARegion *ar, const float vec[3], int adr[2])
891 {
892         RegionView3D *rv3d= ar->regiondata;
893         float fx, fy, vec4[4];
894         
895         copy_v3_v3(vec4, vec);
896         vec4[3]= 1.0;
897         
898         mul_m4_v4(rv3d->persmat, vec4);
899         
900         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
901                 fx = (ar->winx/2)*(1 + vec4[0]/vec4[3]);
902                 fy = (ar->winy/2)*(1 + vec4[1]/vec4[3]);
903                 
904                 adr[0] = (int)floor(fx); 
905                 adr[1] = (int)floor(fy);
906         }
907         else {
908                 adr[0] = ar->winx / 2;
909                 adr[1] = ar->winy / 2;
910         }
911 }
912
913 void project_short_noclip(ARegion *ar, const float vec[3], short adr[2])
914 {
915         RegionView3D *rv3d= ar->regiondata;
916         float fx, fy, vec4[4];
917         
918         copy_v3_v3(vec4, vec);
919         vec4[3]= 1.0;
920         adr[0]= IS_CLIPPED;
921         
922         mul_m4_v4(rv3d->persmat, vec4);
923         
924         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
925                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
926                 
927                 if( fx>-32700 && fx<32700) {
928                         
929                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
930                         
931                         if(fy > -32700.0f && fy < 32700.0f) {
932                                 adr[0]= (short)floor(fx); 
933                                 adr[1]= (short)floor(fy);
934                         }
935                 }
936         }
937 }
938
939 void project_float(ARegion *ar, const float vec[3], float adr[2])
940 {
941         RegionView3D *rv3d= ar->regiondata;
942         float vec4[4];
943         
944         copy_v3_v3(vec4, vec);
945         vec4[3]= 1.0;
946         adr[0]= IS_CLIPPED;
947         
948         mul_m4_v4(rv3d->persmat, vec4);
949         
950         if(vec4[3] > (float)BL_NEAR_CLIP) {
951                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
952                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
953         }
954 }
955
956 void project_float_noclip(ARegion *ar, const float vec[3], float adr[2])
957 {
958         RegionView3D *rv3d= ar->regiondata;
959         float vec4[4];
960         
961         copy_v3_v3(vec4, vec);
962         vec4[3]= 1.0;
963         
964         mul_m4_v4(rv3d->persmat, vec4);
965         
966         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
967                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
968                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
969         }
970         else {
971                 adr[0] = ar->winx / 2.0f;
972                 adr[1] = ar->winy / 2.0f;
973         }
974 }
975
976 /* copies logic of get_view3d_viewplane(), keep in sync */
977 int ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
978 {
979         CameraParams params;
980
981         camera_params_init(&params);
982         camera_params_from_view3d(&params, v3d, rv3d);
983
984         if(clipsta) *clipsta= params.clipsta;
985         if(clipend) *clipend= params.clipend;
986
987         return params.is_ortho;
988 }
989
990 /* also exposed in previewrender.c */
991 int ED_view3d_viewplane_get(View3D *v3d, RegionView3D *rv3d, int winx, int winy, rctf *viewplane, float *clipsta, float *clipend)
992 {
993         CameraParams params;
994
995         camera_params_init(&params);
996         camera_params_from_view3d(&params, v3d, rv3d);
997         camera_params_compute_viewplane(&params, winx, winy, 1.0f, 1.0f);
998
999         if(viewplane) *viewplane= params.viewplane;
1000         if(clipsta) *clipsta= params.clipsta;
1001         if(clipend) *clipend= params.clipend;
1002         
1003         return params.is_ortho;
1004 }
1005
1006 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
1007 {
1008         RegionView3D *rv3d= ar->regiondata;
1009         rctf viewplane;
1010         float clipsta, clipend, x1, y1, x2, y2;
1011         int orth;
1012         
1013         orth= ED_view3d_viewplane_get(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend);
1014         rv3d->is_persp= !orth;
1015
1016         //      printf("%d %d %f %f %f %f %f %f\n", winx, winy, viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax, clipsta, clipend);
1017         x1= viewplane.xmin;
1018         y1= viewplane.ymin;
1019         x2= viewplane.xmax;
1020         y2= viewplane.ymax;
1021         
1022         if(rect) {              /* picking */
1023                 rect->xmin/= (float)ar->winx;
1024                 rect->xmin= x1+rect->xmin*(x2-x1);
1025                 rect->ymin/= (float)ar->winy;
1026                 rect->ymin= y1+rect->ymin*(y2-y1);
1027                 rect->xmax/= (float)ar->winx;
1028                 rect->xmax= x1+rect->xmax*(x2-x1);
1029                 rect->ymax/= (float)ar->winy;
1030                 rect->ymax= y1+rect->ymax*(y2-y1);
1031                 
1032                 if(orth) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
1033                 else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
1034                 
1035         }
1036         else {
1037                 if(orth) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
1038                 else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
1039         }
1040
1041         /* update matrix in 3d view region */
1042         glGetFloatv(GL_PROJECTION_MATRIX, (float*)rv3d->winmat);
1043 }
1044
1045 static void obmat_to_viewmat(View3D *v3d, RegionView3D *rv3d, Object *ob, short smooth)
1046 {
1047         float bmat[4][4];
1048         float tmat[3][3];
1049         
1050         rv3d->view= RV3D_VIEW_USER; /* dont show the grid */
1051         
1052         copy_m4_m4(bmat, ob->obmat);
1053         normalize_m4(bmat);
1054         invert_m4_m4(rv3d->viewmat, bmat);
1055         
1056         /* view quat calculation, needed for add object */
1057         copy_m3_m4(tmat, rv3d->viewmat);
1058         if (smooth) {
1059                 float new_quat[4];
1060                 if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1061                         /* were from a camera view */
1062                         
1063                         float orig_ofs[3];
1064                         float orig_dist= rv3d->dist;
1065                         float orig_lens= v3d->lens;
1066                         copy_v3_v3(orig_ofs, rv3d->ofs);
1067                         
1068                         /* Switch from camera view */
1069                         mat3_to_quat( new_quat,tmat);
1070                         
1071                         rv3d->persp=RV3D_PERSP;
1072                         rv3d->dist= 0.0;
1073                         
1074                         ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1075                         smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1076                         
1077                         rv3d->persp=RV3D_CAMOB; /* just to be polite, not needed */
1078                         
1079                 } else {
1080                         mat3_to_quat( new_quat,tmat);
1081                         smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1082                 }
1083         } else {
1084                 mat3_to_quat( rv3d->viewquat,tmat);
1085         }
1086 }
1087
1088 #define QUATSET(a, b, c, d, e)  a[0]=b; a[1]=c; a[2]=d; a[3]=e; 
1089
1090 int ED_view3d_lock(RegionView3D *rv3d)
1091 {
1092         switch(rv3d->view) {
1093         case RV3D_VIEW_BOTTOM :
1094                 QUATSET(rv3d->viewquat,0.0, -1.0, 0.0, 0.0);
1095                 break;
1096
1097         case RV3D_VIEW_BACK:
1098                 QUATSET(rv3d->viewquat,0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0));
1099                 break;
1100
1101         case RV3D_VIEW_LEFT:
1102                 QUATSET(rv3d->viewquat,0.5, -0.5, 0.5, 0.5);
1103                 break;
1104
1105         case RV3D_VIEW_TOP:
1106                 QUATSET(rv3d->viewquat,1.0, 0.0, 0.0, 0.0);
1107                 break;
1108
1109         case RV3D_VIEW_FRONT:
1110                 QUATSET(rv3d->viewquat,(float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0);
1111                 break;
1112
1113         case RV3D_VIEW_RIGHT:
1114                 QUATSET(rv3d->viewquat, 0.5, -0.5, -0.5, -0.5);
1115                 break;
1116         default:
1117                 return FALSE;
1118         }
1119
1120         return TRUE;
1121 }
1122
1123 /* dont set windows active in here, is used by renderwin too */
1124 void setviewmatrixview3d(Scene *scene, View3D *v3d, RegionView3D *rv3d)
1125 {
1126         if(rv3d->persp==RV3D_CAMOB) {       /* obs/camera */
1127                 if(v3d->camera) {
1128                         where_is_object(scene, v3d->camera);    
1129                         obmat_to_viewmat(v3d, rv3d, v3d->camera, 0);
1130                 }
1131                 else {
1132                         quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1133                         rv3d->viewmat[3][2]-= rv3d->dist;
1134                 }
1135         }
1136         else {
1137                 /* should be moved to better initialize later on XXX */
1138                 if(rv3d->viewlock)
1139                         ED_view3d_lock(rv3d);
1140                 
1141                 quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1142                 if(rv3d->persp==RV3D_PERSP) rv3d->viewmat[3][2]-= rv3d->dist;
1143                 if(v3d->ob_centre) {
1144                         Object *ob= v3d->ob_centre;
1145                         float vec[3];
1146                         
1147                         copy_v3_v3(vec, ob->obmat[3]);
1148                         if(ob->type==OB_ARMATURE && v3d->ob_centre_bone[0]) {
1149                                 bPoseChannel *pchan= get_pose_channel(ob->pose, v3d->ob_centre_bone);
1150                                 if(pchan) {
1151                                         copy_v3_v3(vec, pchan->pose_mat[3]);
1152                                         mul_m4_v3(ob->obmat, vec);
1153                                 }
1154                         }
1155                         translate_m4( rv3d->viewmat,-vec[0], -vec[1], -vec[2]);
1156                 }
1157                 else if (v3d->ob_centre_cursor) {
1158                         float vec[3];
1159                         copy_v3_v3(vec, give_cursor(scene, v3d));
1160                         translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]);
1161                 }
1162                 else translate_m4( rv3d->viewmat,rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
1163         }
1164 }
1165
1166 /* IGLuint-> GLuint*/
1167 /* Warning: be sure to account for a negative return value
1168 *   This is an error, "Too many objects in select buffer"
1169 *   and no action should be taken (can crash blender) if this happens
1170 */
1171 short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
1172 {
1173         Scene *scene= vc->scene;
1174         View3D *v3d= vc->v3d;
1175         ARegion *ar= vc->ar;
1176         rctf rect;
1177         short code, hits;
1178         char dt, dtx;
1179         
1180         G.f |= G_PICKSEL;
1181         
1182         /* case not a border select */
1183         if(input->xmin==input->xmax) {
1184                 rect.xmin= input->xmin-12;      // seems to be default value for bones only now
1185                 rect.xmax= input->xmin+12;
1186                 rect.ymin= input->ymin-12;
1187                 rect.ymax= input->ymin+12;
1188         }
1189         else {
1190                 rect.xmin= input->xmin;
1191                 rect.xmax= input->xmax;
1192                 rect.ymin= input->ymin;
1193                 rect.ymax= input->ymax;
1194         }
1195         
1196         setwinmatrixview3d(ar, v3d, &rect);
1197         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1198         
1199         if(v3d->drawtype > OB_WIRE) {
1200                 v3d->zbuf= TRUE;
1201                 glEnable(GL_DEPTH_TEST);
1202         }
1203         
1204         if(vc->rv3d->rflag & RV3D_CLIPPING)
1205                 view3d_set_clipping(vc->rv3d);
1206         
1207         glSelectBuffer( bufsize, (GLuint *)buffer);
1208         glRenderMode(GL_SELECT);
1209         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1210         glPushName(-1);
1211         code= 1;
1212         
1213         if(vc->obedit && vc->obedit->type==OB_MBALL) {
1214                 draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1215         }
1216         else if((vc->obedit && vc->obedit->type==OB_ARMATURE)) {
1217                 /* if not drawing sketch, draw bones */
1218                 if(!BDR_drawSketchNames(vc)) {
1219                         draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1220                 }
1221         }
1222         else {
1223                 Base *base;
1224                 
1225                 v3d->xray= TRUE;        // otherwise it postpones drawing
1226                 for(base= scene->base.first; base; base= base->next) {
1227                         if(base->lay & v3d->lay) {
1228                                 
1229                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1230                                         base->selcol= 0;
1231                                 else {
1232                                         base->selcol= code;
1233                                         glLoadName(code);
1234                                         draw_object(scene, ar, v3d, base, DRAW_PICKING|DRAW_CONSTCOLOR);
1235                                         
1236                                         /* we draw group-duplicators for selection too */
1237                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1238                                                 ListBase *lb;
1239                                                 DupliObject *dob;
1240                                                 Base tbase;
1241                                                 
1242                                                 tbase.flag= OB_FROMDUPLI;
1243                                                 lb= object_duplilist(scene, base->object);
1244                                                 
1245                                                 for(dob= lb->first; dob; dob= dob->next) {
1246                                                         tbase.object= dob->ob;
1247                                                         copy_m4_m4(dob->ob->obmat, dob->mat);
1248                                                         
1249                                                         /* extra service: draw the duplicator in drawtype of parent */
1250                                                         /* MIN2 for the drawtype to allow bounding box objects in groups for lods */
1251                                                         dt= tbase.object->dt;   tbase.object->dt= MIN2(tbase.object->dt, base->object->dt);
1252                                                         dtx= tbase.object->dtx; tbase.object->dtx= base->object->dtx;
1253
1254                                                         draw_object(scene, ar, v3d, &tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1255                                                         
1256                                                         tbase.object->dt= dt;
1257                                                         tbase.object->dtx= dtx;
1258
1259                                                         copy_m4_m4(dob->ob->obmat, dob->omat);
1260                                                 }
1261                                                 free_object_duplilist(lb);
1262                                         }
1263                                         code++;
1264                                 }                               
1265                         }
1266                 }
1267                 v3d->xray= FALSE;       // restore
1268         }
1269         
1270         glPopName();    /* see above (pushname) */
1271         hits= glRenderMode(GL_RENDER);
1272         
1273         G.f &= ~G_PICKSEL;
1274         setwinmatrixview3d(ar, v3d, NULL);
1275         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1276         
1277         if(v3d->drawtype > OB_WIRE) {
1278                 v3d->zbuf= 0;
1279                 glDisable(GL_DEPTH_TEST);
1280         }
1281 // XXX  persp(PERSP_WIN);
1282         
1283         if(vc->rv3d->rflag & RV3D_CLIPPING)
1284                 view3d_clr_clipping();
1285         
1286         if(hits<0) printf("Too many objects in select buffer\n");       // XXX make error message
1287         
1288         return hits;
1289 }
1290
1291 /* ********************** local view operator ******************** */
1292
1293 static unsigned int free_localbit(Main *bmain)
1294 {
1295         unsigned int lay;
1296         ScrArea *sa;
1297         bScreen *sc;
1298         
1299         lay= 0;
1300         
1301         /* sometimes we loose a localview: when an area is closed */
1302         /* check all areas: which localviews are in use? */
1303         for(sc= bmain->screen.first; sc; sc= sc->id.next) {
1304                 for(sa= sc->areabase.first; sa; sa= sa->next) {
1305                         SpaceLink *sl= sa->spacedata.first;
1306                         for(; sl; sl= sl->next) {
1307                                 if(sl->spacetype==SPACE_VIEW3D) {
1308                                         View3D *v3d= (View3D*) sl;
1309                                         lay |= v3d->lay;
1310                                 }
1311                         }
1312                 }
1313         }
1314         
1315         if( (lay & 0x01000000)==0) return 0x01000000;
1316         if( (lay & 0x02000000)==0) return 0x02000000;
1317         if( (lay & 0x04000000)==0) return 0x04000000;
1318         if( (lay & 0x08000000)==0) return 0x08000000;
1319         if( (lay & 0x10000000)==0) return 0x10000000;
1320         if( (lay & 0x20000000)==0) return 0x20000000;
1321         if( (lay & 0x40000000)==0) return 0x40000000;
1322         if( (lay & 0x80000000)==0) return 0x80000000;
1323         
1324         return 0;
1325 }
1326
1327 int ED_view3d_scene_layer_set(int lay, const int *values, int *active)
1328 {
1329         int i, tot= 0;
1330         
1331         /* ensure we always have some layer selected */
1332         for(i=0; i<20; i++)
1333                 if(values[i])
1334                         tot++;
1335         
1336         if(tot==0)
1337                 return lay;
1338         
1339         for(i=0; i<20; i++) {
1340                 
1341                 if (active) {
1342                         /* if this value has just been switched on, make that layer active */
1343                         if (values[i] && (lay & (1<<i))==0) {
1344                                 *active = (1<<i);
1345                         }
1346                 }
1347                         
1348                 if (values[i]) lay |= (1<<i);
1349                 else lay &= ~(1<<i);
1350         }
1351         
1352         /* ensure always an active layer */
1353         if (active && (lay & *active)==0) {
1354                 for(i=0; i<20; i++) {
1355                         if(lay & (1<<i)) {
1356                                 *active= 1<<i;
1357                                 break;
1358                         }
1359                 }
1360         }
1361         
1362         return lay;
1363 }
1364
1365 static void initlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1366 {
1367         View3D *v3d= sa->spacedata.first;
1368         Base *base;
1369         float size = 0.0, min[3], max[3], box[3];
1370         unsigned int locallay;
1371         int ok=0;
1372
1373         if(v3d->localvd) return;
1374
1375         INIT_MINMAX(min, max);
1376
1377         locallay= free_localbit(bmain);
1378
1379         if(locallay==0) {
1380                 printf("Sorry, no more than 8 localviews\n");   // XXX error 
1381                 ok= 0;
1382         }
1383         else {
1384                 if(scene->obedit) {
1385                         minmax_object(scene->obedit, min, max);
1386                         
1387                         ok= 1;
1388                 
1389                         BASACT->lay |= locallay;
1390                         scene->obedit->lay= BASACT->lay;
1391                 }
1392                 else {
1393                         for(base= FIRSTBASE; base; base= base->next) {
1394                                 if(TESTBASE(v3d, base))  {
1395                                         minmax_object(base->object, min, max);
1396                                         base->lay |= locallay;
1397                                         base->object->lay= base->lay;
1398                                         ok= 1;
1399                                 }
1400                         }
1401                 }
1402                 
1403                 box[0]= (max[0]-min[0]);
1404                 box[1]= (max[1]-min[1]);
1405                 box[2]= (max[2]-min[2]);
1406                 size= MAX3(box[0], box[1], box[2]);
1407                 if(size <= 0.01f) size= 0.01f;
1408         }
1409         
1410         if(ok) {
1411                 ARegion *ar;
1412                 
1413                 v3d->localvd= MEM_mallocN(sizeof(View3D), "localview");
1414                 
1415                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1416
1417                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
1418                         if(ar->regiontype == RGN_TYPE_WINDOW) {
1419                                 RegionView3D *rv3d= ar->regiondata;
1420
1421                                 rv3d->localvd= MEM_mallocN(sizeof(RegionView3D), "localview region");
1422                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1423                                 
1424                                 rv3d->ofs[0]= -(min[0]+max[0])/2.0f;
1425                                 rv3d->ofs[1]= -(min[1]+max[1])/2.0f;
1426                                 rv3d->ofs[2]= -(min[2]+max[2])/2.0f;
1427
1428                                 rv3d->dist= size;
1429                                 /* perspective should be a bit farther away to look nice */
1430                                 if(rv3d->persp==RV3D_ORTHO)
1431                                         rv3d->dist*= 0.7f;
1432
1433                                 // correction for window aspect ratio
1434                                 if(ar->winy>2 && ar->winx>2) {
1435                                         float asp= (float)ar->winx/(float)ar->winy;
1436                                         if(asp < 1.0f) asp= 1.0f/asp;
1437                                         rv3d->dist*= asp;
1438                                 }
1439                                 
1440                                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP;
1441                                 
1442                                 v3d->cursor[0]= -rv3d->ofs[0];
1443                                 v3d->cursor[1]= -rv3d->ofs[1];
1444                                 v3d->cursor[2]= -rv3d->ofs[2];
1445                         }
1446                 }
1447                 
1448                 v3d->lay= locallay;
1449         }
1450         else {
1451                 /* clear flags */ 
1452                 for(base= FIRSTBASE; base; base= base->next) {
1453                         if( base->lay & locallay ) {
1454                                 base->lay-= locallay;
1455                                 if(base->lay==0) base->lay= v3d->layact;
1456                                 if(base->object != scene->obedit) base->flag |= SELECT;
1457                                 base->object->lay= base->lay;
1458                         }
1459                 }               
1460         }
1461
1462 }
1463
1464 static void restore_localviewdata(ScrArea *sa, int free)
1465 {
1466         ARegion *ar;
1467         View3D *v3d= sa->spacedata.first;
1468         
1469         if(v3d->localvd==NULL) return;
1470         
1471         v3d->near= v3d->localvd->near;
1472         v3d->far= v3d->localvd->far;
1473         v3d->lay= v3d->localvd->lay;
1474         v3d->layact= v3d->localvd->layact;
1475         v3d->drawtype= v3d->localvd->drawtype;
1476         v3d->camera= v3d->localvd->camera;
1477         
1478         if(free) {
1479                 MEM_freeN(v3d->localvd);
1480                 v3d->localvd= NULL;
1481         }
1482         
1483         for(ar= sa->regionbase.first; ar; ar= ar->next) {
1484                 if(ar->regiontype == RGN_TYPE_WINDOW) {
1485                         RegionView3D *rv3d= ar->regiondata;
1486                         
1487                         if(rv3d->localvd) {
1488                                 rv3d->dist= rv3d->localvd->dist;
1489                                 copy_v3_v3(rv3d->ofs, rv3d->localvd->ofs);
1490                                 copy_qt_qt(rv3d->viewquat, rv3d->localvd->viewquat);
1491                                 rv3d->view= rv3d->localvd->view;
1492                                 rv3d->persp= rv3d->localvd->persp;
1493                                 rv3d->camzoom= rv3d->localvd->camzoom;
1494
1495                                 if(free) {
1496                                         MEM_freeN(rv3d->localvd);
1497                                         rv3d->localvd= NULL;
1498                                 }
1499                         }
1500                 }
1501         }
1502 }
1503
1504 static void endlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1505 {
1506         View3D *v3d= sa->spacedata.first;
1507         struct Base *base;
1508         unsigned int locallay;
1509         
1510         if(v3d->localvd) {
1511                 
1512                 locallay= v3d->lay & 0xFF000000;
1513                 
1514                 restore_localviewdata(sa, 1); // 1 = free
1515
1516                 /* for when in other window the layers have changed */
1517                 if(v3d->scenelock) v3d->lay= scene->lay;
1518                 
1519                 for(base= FIRSTBASE; base; base= base->next) {
1520                         if( base->lay & locallay ) {
1521                                 base->lay-= locallay;
1522                                 if(base->lay==0) base->lay= v3d->layact;
1523                                 if(base->object != scene->obedit) {
1524                                         base->flag |= SELECT;
1525                                         base->object->flag |= SELECT;
1526                                 }
1527                                 base->object->lay= base->lay;
1528                         }
1529                 }
1530                 
1531                 DAG_on_visible_update(bmain, FALSE);
1532         } 
1533 }
1534
1535 static int localview_exec(bContext *C, wmOperator *UNUSED(unused))
1536 {
1537         View3D *v3d= CTX_wm_view3d(C);
1538         
1539         if(v3d->localvd)
1540                 endlocalview(CTX_data_main(C), CTX_data_scene(C), CTX_wm_area(C));
1541         else
1542                 initlocalview(CTX_data_main(C), CTX_data_scene(C), CTX_wm_area(C));
1543         
1544         ED_area_tag_redraw(CTX_wm_area(C));
1545         
1546         return OPERATOR_FINISHED;
1547 }
1548
1549 void VIEW3D_OT_localview(wmOperatorType *ot)
1550 {
1551         
1552         /* identifiers */
1553         ot->name= "Local View";
1554         ot->description= "Toggle display of selected object(s) separately and centered in view";
1555         ot->idname= "VIEW3D_OT_localview";
1556         
1557         /* api callbacks */
1558         ot->exec= localview_exec;
1559         ot->flag= OPTYPE_UNDO; /* localview changes object layer bitflags */
1560         
1561         ot->poll= ED_operator_view3d_active;
1562 }
1563
1564 #ifdef WITH_GAMEENGINE
1565
1566 static ListBase queue_back;
1567 static void SaveState(bContext *C, wmWindow *win)
1568 {
1569         Object *obact = CTX_data_active_object(C);
1570         
1571         glPushAttrib(GL_ALL_ATTRIB_BITS);
1572
1573         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1574                 GPU_paint_set_mipmap(1);
1575         
1576         queue_back= win->queue;
1577         
1578         win->queue.first= win->queue.last= NULL;
1579         
1580         //XXX waitcursor(1);
1581 }
1582
1583 static void RestoreState(bContext *C, wmWindow *win)
1584 {
1585         Object *obact = CTX_data_active_object(C);
1586         
1587         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1588                 GPU_paint_set_mipmap(0);
1589
1590         //XXX curarea->win_swap = 0;
1591         //XXX curarea->head_swap=0;
1592         //XXX allqueue(REDRAWVIEW3D, 1);
1593         //XXX allqueue(REDRAWBUTSALL, 0);
1594         //XXX reset_slowparents();
1595         //XXX waitcursor(0);
1596         //XXX G.qual= 0;
1597         
1598         if(win) /* check because closing win can set to NULL */
1599                 win->queue= queue_back;
1600         
1601         GPU_state_init();
1602         GPU_set_tpage(NULL, 0, 0);
1603
1604         glPopAttrib();
1605 }
1606
1607 /* was space_set_commmandline_options in 2.4x */
1608 static void game_set_commmandline_options(GameData *gm)
1609 {
1610         SYS_SystemHandle syshandle;
1611         int test;
1612
1613         if ( (syshandle = SYS_GetSystem()) ) {
1614                 /* User defined settings */
1615                 test= (U.gameflags & USER_DISABLE_MIPMAP);
1616                 GPU_set_mipmap(!test);
1617                 SYS_WriteCommandLineInt(syshandle, "nomipmap", test);
1618
1619                 /* File specific settings: */
1620                 /* Only test the first one. These two are switched
1621                  * simultaneously. */
1622                 test= (gm->flag & GAME_SHOW_FRAMERATE);
1623                 SYS_WriteCommandLineInt(syshandle, "show_framerate", test);
1624                 SYS_WriteCommandLineInt(syshandle, "show_profile", test);
1625
1626                 test = (gm->flag & GAME_SHOW_DEBUG_PROPS);
1627                 SYS_WriteCommandLineInt(syshandle, "show_properties", test);
1628
1629                 test= (gm->flag & GAME_SHOW_PHYSICS);
1630                 SYS_WriteCommandLineInt(syshandle, "show_physics", test);
1631
1632                 test= (gm->flag & GAME_ENABLE_ALL_FRAMES);
1633                 SYS_WriteCommandLineInt(syshandle, "fixedtime", test);
1634
1635                 test= (gm->flag & GAME_ENABLE_ANIMATION_RECORD);
1636                 SYS_WriteCommandLineInt(syshandle, "animation_record", test);
1637
1638                 test= (gm->flag & GAME_IGNORE_DEPRECATION_WARNINGS);
1639                 SYS_WriteCommandLineInt(syshandle, "ignore_deprecation_warnings", test);
1640
1641                 test= (gm->matmode == GAME_MAT_MULTITEX);
1642                 SYS_WriteCommandLineInt(syshandle, "blender_material", test);
1643                 test= (gm->matmode == GAME_MAT_GLSL);
1644                 SYS_WriteCommandLineInt(syshandle, "blender_glsl_material", test);
1645                 test= (gm->flag & GAME_DISPLAY_LISTS);
1646                 SYS_WriteCommandLineInt(syshandle, "displaylists", test);
1647
1648
1649         }
1650 }
1651
1652 #endif // WITH_GAMEENGINE
1653
1654 static int game_engine_poll(bContext *C)
1655 {
1656         /* we need a context and area to launch BGE
1657         it's a temporary solution to avoid crash at load time
1658         if we try to auto run the BGE. Ideally we want the
1659         context to be set as soon as we load the file. */
1660
1661         if(CTX_wm_window(C)==NULL) return 0;
1662         if(CTX_wm_screen(C)==NULL) return 0;
1663         if(CTX_wm_area(C)==NULL) return 0;
1664
1665         if(CTX_data_mode_enum(C)!=CTX_MODE_OBJECT)
1666                 return 0;
1667
1668         return 1;
1669 }
1670
1671 int ED_view3d_context_activate(bContext *C)
1672 {
1673         bScreen *sc= CTX_wm_screen(C);
1674         ScrArea *sa= CTX_wm_area(C);
1675         ARegion *ar;
1676
1677         /* sa can be NULL when called from python */
1678         if(sa==NULL || sa->spacetype != SPACE_VIEW3D)
1679                 for(sa=sc->areabase.first; sa; sa= sa->next)
1680                         if(sa->spacetype==SPACE_VIEW3D)
1681                                 break;
1682
1683         if(!sa)
1684                 return 0;
1685         
1686         for(ar=sa->regionbase.first; ar; ar=ar->next)
1687                 if(ar->regiontype == RGN_TYPE_WINDOW)
1688                         break;
1689         
1690         if(!ar)
1691                 return 0;
1692         
1693         // bad context switch ..
1694         CTX_wm_area_set(C, sa);
1695         CTX_wm_region_set(C, ar);
1696
1697         return 1;
1698 }
1699
1700 static int game_engine_exec(bContext *C, wmOperator *op)
1701 {
1702 #ifdef WITH_GAMEENGINE
1703         Scene *startscene = CTX_data_scene(C);
1704         ScrArea /* *sa, */ /* UNUSED */ *prevsa= CTX_wm_area(C);
1705         ARegion *ar, *prevar= CTX_wm_region(C);
1706         wmWindow *prevwin= CTX_wm_window(C);
1707         RegionView3D *rv3d;
1708         rcti cam_frame;
1709
1710         (void)op; /* unused */
1711         
1712         // bad context switch ..
1713         if(!ED_view3d_context_activate(C))
1714                 return OPERATOR_CANCELLED;
1715         
1716         rv3d= CTX_wm_region_view3d(C);
1717         /* sa= CTX_wm_area(C); */ /* UNUSED */
1718         ar= CTX_wm_region(C);
1719
1720         view3d_operator_needs_opengl(C);
1721         
1722         game_set_commmandline_options(&startscene->gm);
1723
1724         if(rv3d->persp==RV3D_CAMOB && startscene->gm.framing.type == SCE_GAMEFRAMING_BARS && startscene->gm.stereoflag != STEREO_DOME) { /* Letterbox */
1725                 rctf cam_framef;
1726                 ED_view3d_calc_camera_border(startscene, ar, CTX_wm_view3d(C), rv3d, &cam_framef, FALSE);
1727                 cam_frame.xmin = cam_framef.xmin + ar->winrct.xmin;
1728                 cam_frame.xmax = cam_framef.xmax + ar->winrct.xmin;
1729                 cam_frame.ymin = cam_framef.ymin + ar->winrct.ymin;
1730                 cam_frame.ymax = cam_framef.ymax + ar->winrct.ymin;
1731                 BLI_isect_rcti(&ar->winrct, &cam_frame, &cam_frame);
1732         }
1733         else {
1734                 cam_frame.xmin = ar->winrct.xmin;
1735                 cam_frame.xmax = ar->winrct.xmax;
1736                 cam_frame.ymin = ar->winrct.ymin;
1737                 cam_frame.ymax = ar->winrct.ymax;
1738         }
1739
1740
1741         SaveState(C, prevwin);
1742
1743         StartKetsjiShell(C, ar, &cam_frame, 1);
1744
1745         /* window wasnt closed while the BGE was running */
1746         if(BLI_findindex(&CTX_wm_manager(C)->windows, prevwin) == -1) {
1747                 prevwin= NULL;
1748                 CTX_wm_window_set(C, NULL);
1749         }
1750         
1751         if(prevwin) {
1752                 /* restore context, in case it changed in the meantime, for
1753                    example by working in another window or closing it */
1754                 CTX_wm_region_set(C, prevar);
1755                 CTX_wm_window_set(C, prevwin);
1756                 CTX_wm_area_set(C, prevsa);
1757         }
1758
1759         RestoreState(C, prevwin);
1760
1761         //XXX restore_all_scene_cfra(scene_cfra_store);
1762         set_scene_bg(CTX_data_main(C), startscene);
1763         //XXX scene_update_for_newframe(bmain, scene, scene->lay);
1764         
1765         ED_area_tag_redraw(CTX_wm_area(C));
1766
1767         return OPERATOR_FINISHED;
1768 #else
1769         (void)C; /* unused */
1770         BKE_report(op->reports, RPT_ERROR, "Game engine is disabled in this build");
1771         return OPERATOR_CANCELLED;
1772 #endif
1773 }
1774
1775 void VIEW3D_OT_game_start(wmOperatorType *ot)
1776 {
1777         
1778         /* identifiers */
1779         ot->name= "Start Game Engine";
1780         ot->description= "Start game engine";
1781         ot->idname= "VIEW3D_OT_game_start";
1782         
1783         /* api callbacks */
1784         ot->exec= game_engine_exec;
1785         
1786         ot->poll= game_engine_poll;
1787 }
1788
1789 /* ************************************** */
1790
1791 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
1792 {
1793         float alignaxis[3] = {0.0, 0.0, 0.0};
1794         float norm[3], axis[3], angle, new_quat[4];
1795         
1796         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
1797         else alignaxis[-axisidx-1]= -1.0;
1798
1799         normalize_v3_v3(norm, vec);
1800
1801         angle= (float)acos(dot_v3v3(alignaxis, norm));
1802         cross_v3_v3v3(axis, alignaxis, norm);
1803         axis_angle_to_quat( new_quat,axis, -angle);
1804         
1805         rv3d->view= RV3D_VIEW_USER;
1806         
1807         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1808                 /* switch out of camera view */
1809                 float orig_ofs[3];
1810                 float orig_dist= rv3d->dist;
1811                 float orig_lens= v3d->lens;
1812                 
1813                 copy_v3_v3(orig_ofs, rv3d->ofs);
1814                 rv3d->persp= RV3D_PERSP;
1815                 rv3d->dist= 0.0;
1816                 ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1817                 smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1818         } else {
1819                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP; /* switch out of camera mode */
1820                 smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1821         }
1822 }
1823
1824 float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3])
1825 {
1826         return  (rv3d->persmat[3][3] + (
1827                                 rv3d->persmat[0][3]*co[0] +
1828                                 rv3d->persmat[1][3]*co[1] +
1829                                 rv3d->persmat[2][3]*co[2])
1830                         ) * rv3d->pixsize;
1831 }