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