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