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