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