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