action editor was setting wrong rna type on Ctrl+RMB
[blender.git] / source / blender / editors / space_view3d / view3d_view.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/editors/space_view3d/view3d_view.c
30  *  \ingroup spview3d
31  */
32
33
34 #include "DNA_camera_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_lamp_types.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_math.h"
42 #include "BLI_rect.h"
43 #include "BLI_listbase.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_anim.h"
47 #include "BKE_action.h"
48 #include "BKE_context.h"
49 #include "BKE_depsgraph.h"
50 #include "BKE_object.h"
51 #include "BKE_global.h"
52 #include "BKE_main.h"
53 #include "BKE_report.h"
54 #include "BKE_scene.h"
55
56 #include "BIF_gl.h"
57 #include "BIF_glutil.h"
58
59 #include "GPU_draw.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "ED_screen.h"
65 #include "ED_armature.h"
66
67 #ifdef WITH_GAMEENGINE
68 #include "BL_System.h"
69 #endif
70
71 #include "view3d_intern.h"      // own include
72
73 /* use this call when executing an operator,
74    event system doesn't set for each event the
75    opengl drawing context */
76 void view3d_operator_needs_opengl(const bContext *C)
77 {
78         wmWindow *win = CTX_wm_window(C);
79         ARegion *ar= CTX_wm_region(C);
80         
81         view3d_region_operator_needs_opengl(win, ar);
82 }
83
84 void view3d_region_operator_needs_opengl(wmWindow *win, ARegion *ar)
85 {
86         /* for debugging purpose, context should always be OK */
87         if ((ar == NULL) || (ar->regiontype!=RGN_TYPE_WINDOW))
88                 printf("view3d_region_operator_needs_opengl error, wrong region\n");
89         else {
90                 RegionView3D *rv3d= ar->regiondata;
91                 
92                 wmSubWindowSet(win, ar->swinid);
93                 glMatrixMode(GL_PROJECTION);
94                 glLoadMatrixf(rv3d->winmat);
95                 glMatrixMode(GL_MODELVIEW);
96                 glLoadMatrixf(rv3d->viewmat);
97         }
98 }
99
100 float *give_cursor(Scene *scene, View3D *v3d)
101 {
102         if(v3d && v3d->localvd) return v3d->cursor;
103         else return scene->cursor;
104 }
105
106
107 /* Gets the lens and clipping values from a camera of lamp type object */
108 void get_object_clip_range(Object *ob, float *lens, float *clipsta, float *clipend)
109 {
110         if(ob->type==OB_LAMP ) {
111                 Lamp *la = ob->data;
112                 if (lens) {
113                         float x1, fac;
114                         fac= cosf((float)M_PI*la->spotsize/360.0f);
115                         x1= saacos(fac);
116                         *lens= 16.0f*fac/sinf(x1);
117                 }
118                 if (clipsta)    *clipsta= la->clipsta;
119                 if (clipend)    *clipend= la->clipend;
120         }
121         else if(ob->type==OB_CAMERA) {
122                 Camera *cam= ob->data;
123                 if (lens)               *lens= cam->lens;
124                 if (clipsta)    *clipsta= cam->clipsta;
125                 if (clipend)    *clipend= cam->clipend;
126         }
127         else {
128                 if (lens)               *lens= 35.0f;
129         }
130 }
131
132 /* ****************** smooth view operator ****************** */
133 /* This operator is one of the 'timer refresh' ones like animation playback */
134
135 struct SmoothViewStore {
136         float orig_dist, new_dist;
137         float orig_lens, new_lens;
138         float orig_quat[4], new_quat[4];
139         float orig_ofs[3], new_ofs[3];
140         
141         int to_camera, orig_view;
142         
143         double time_allowed;
144 };
145
146 /* will start timer if appropriate */
147 /* the arguments are the desired situation */
148 void smooth_view(bContext *C, View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera, float *ofs, float *quat, float *dist, float *lens)
149 {
150         wmWindowManager *wm= CTX_wm_manager(C);
151         wmWindow *win= CTX_wm_window(C);
152         ScrArea *sa= CTX_wm_area(C);
153
154         RegionView3D *rv3d= ar->regiondata;
155         struct SmoothViewStore sms= {0};
156         short ok= FALSE;
157         
158         /* initialize sms */
159         copy_v3_v3(sms.new_ofs, rv3d->ofs);
160         copy_qt_qt(sms.new_quat, rv3d->viewquat);
161         sms.new_dist= rv3d->dist;
162         sms.new_lens= v3d->lens;
163         sms.to_camera= 0;
164         
165         /* store the options we want to end with */
166         if(ofs) copy_v3_v3(sms.new_ofs, ofs);
167         if(quat) copy_qt_qt(sms.new_quat, quat);
168         if(dist) sms.new_dist= *dist;
169         if(lens) sms.new_lens= *lens;
170
171         if (camera) {
172                 ED_view3d_from_object(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens);
173                 sms.to_camera= 1; /* restore view3d values in end */
174         }
175         
176         if (C && U.smooth_viewtx) {
177                 int changed = 0; /* zero means no difference */
178                 
179                 if (oldcamera != camera)
180                         changed = 1;
181                 else if (sms.new_dist != rv3d->dist)
182                         changed = 1;
183                 else if (sms.new_lens != v3d->lens)
184                         changed = 1;
185                 else if (!equals_v3v3(sms.new_ofs, rv3d->ofs))
186                         changed = 1;
187                 else if (!equals_v4v4(sms.new_quat, rv3d->viewquat))
188                         changed = 1;
189                 
190                 /* The new view is different from the old one
191                         * so animate the view */
192                 if (changed) {
193
194                         /* original values */
195                         if (oldcamera) {
196                                 sms.orig_dist= rv3d->dist; // below function does weird stuff with it...
197                                 ED_view3d_from_object(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens);
198                         }
199                         else {
200                                 copy_v3_v3(sms.orig_ofs, rv3d->ofs);
201                                 copy_qt_qt(sms.orig_quat, rv3d->viewquat);
202                                 sms.orig_dist= rv3d->dist;
203                                 sms.orig_lens= v3d->lens;
204                         }
205                         /* grid draw as floor */
206                         if((rv3d->viewlock & RV3D_LOCKED)==0) {
207                                 /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
208                                 sms.orig_view= rv3d->sms ? rv3d->sms->orig_view : rv3d->view;
209                                 rv3d->view= 0;
210                         }
211
212                         sms.time_allowed= (double)U.smooth_viewtx / 1000.0;
213                         
214                         /* if this is view rotation only
215                                 * we can decrease the time allowed by
216                                 * the angle between quats 
217                                 * this means small rotations wont lag */
218                         if (quat && !ofs && !dist) {
219                                 float vec1[3]={0,0,1}, vec2[3]= {0,0,1};
220                                 float q1[4], q2[4];
221
222                                 invert_qt_qt(q1, sms.new_quat);
223                                 invert_qt_qt(q2, sms.orig_quat);
224
225                                 mul_qt_v3(q1, vec1);
226                                 mul_qt_v3(q2, vec2);
227
228                                 /* scale the time allowed by the rotation */
229                                 sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */
230                         }
231
232                         /* ensure it shows correct */
233                         if(sms.to_camera) rv3d->persp= RV3D_PERSP;
234
235                         rv3d->rflag |= RV3D_NAVIGATING;
236                         
237                         /* keep track of running timer! */
238                         if(rv3d->sms==NULL)
239                                 rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d");
240                         *rv3d->sms= sms;
241                         if(rv3d->smooth_timer)
242                                 WM_event_remove_timer(wm, win, rv3d->smooth_timer);
243                         /* TIMER1 is hardcoded in keymap */
244                         rv3d->smooth_timer= WM_event_add_timer(wm, win, TIMER1, 1.0/100.0);     /* max 30 frs/sec */
245                         
246                         ok= TRUE;
247                 }
248         }
249         
250         /* if we get here nothing happens */
251         if(ok == FALSE) {
252                 if(sms.to_camera==0) {
253                         copy_v3_v3(rv3d->ofs, sms.new_ofs);
254                         copy_qt_qt(rv3d->viewquat, sms.new_quat);
255                         rv3d->dist = sms.new_dist;
256                         v3d->lens = sms.new_lens;
257                 }
258
259                 if(rv3d->viewlock & RV3D_BOXVIEW)
260                         view3d_boxview_copy(sa, ar);
261
262                 ED_region_tag_redraw(ar);
263         }
264 }
265
266 /* only meant for timer usage */
267 static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
268 {
269         View3D *v3d = CTX_wm_view3d(C);
270         RegionView3D *rv3d= CTX_wm_region_view3d(C);
271         struct SmoothViewStore *sms= rv3d->sms;
272         float step, step_inv;
273         
274         /* escape if not our timer */
275         if(rv3d->smooth_timer==NULL || rv3d->smooth_timer!=event->customdata)
276                 return OPERATOR_PASS_THROUGH;
277         
278         if(sms->time_allowed != 0.0)
279                 step = (float)((rv3d->smooth_timer->duration)/sms->time_allowed);
280         else
281                 step = 1.0f;
282         
283         /* end timer */
284         if(step >= 1.0f) {
285                 
286                 /* if we went to camera, store the original */
287                 if(sms->to_camera) {
288                         rv3d->persp= RV3D_CAMOB;
289                         copy_v3_v3(rv3d->ofs, sms->orig_ofs);
290                         copy_qt_qt(rv3d->viewquat, sms->orig_quat);
291                         rv3d->dist = sms->orig_dist;
292                         v3d->lens = sms->orig_lens;
293                 }
294                 else {
295                         copy_v3_v3(rv3d->ofs, sms->new_ofs);
296                         copy_qt_qt(rv3d->viewquat, sms->new_quat);
297                         rv3d->dist = sms->new_dist;
298                         v3d->lens = sms->new_lens;
299                 }
300                 
301                 if((rv3d->viewlock & RV3D_LOCKED)==0) {
302                         rv3d->view= sms->orig_view;
303                 }
304
305                 MEM_freeN(rv3d->sms);
306                 rv3d->sms= NULL;
307                 
308                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
309                 rv3d->smooth_timer= NULL;
310                 rv3d->rflag &= ~RV3D_NAVIGATING;
311         }
312         else {
313                 int i;
314                 
315                 /* ease in/out */
316                 if (step < 0.5f)        step = (float)pow(step*2.0f, 2.0)/2.0f;
317                 else                            step = (float)1.0f-(powf(2.0f*(1.0f-step),2.0f)/2.0f);
318
319                 step_inv = 1.0f-step;
320
321                 for (i=0; i<3; i++)
322                         rv3d->ofs[i] = sms->new_ofs[i] * step + sms->orig_ofs[i]*step_inv;
323
324                 interp_qt_qtqt(rv3d->viewquat, sms->orig_quat, sms->new_quat, step);
325                 
326                 rv3d->dist = sms->new_dist * step + sms->orig_dist*step_inv;
327                 v3d->lens = sms->new_lens * step + sms->orig_lens*step_inv;
328         }
329         
330         if(rv3d->viewlock & RV3D_BOXVIEW)
331                 view3d_boxview_copy(CTX_wm_area(C), CTX_wm_region(C));
332         
333         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
334         
335         return OPERATOR_FINISHED;
336 }
337
338 void VIEW3D_OT_smoothview(wmOperatorType *ot)
339 {
340         
341         /* identifiers */
342         ot->name= "Smooth View";
343         ot->idname= "VIEW3D_OT_smoothview";
344         ot->description="The time to animate the change of view (in milliseconds)";
345         
346         /* api callbacks */
347         ot->invoke= view3d_smoothview_invoke;
348         
349         ot->poll= ED_operator_view3d_active;
350 }
351
352 /* ****************** change view operators ****************** */
353
354 static int view3d_setcameratoview_exec(bContext *C, wmOperator *UNUSED(op))
355 {
356         View3D *v3d = CTX_wm_view3d(C);
357         RegionView3D *rv3d= CTX_wm_region_view3d(C);
358
359         copy_qt_qt(rv3d->lviewquat, rv3d->viewquat);
360         rv3d->lview= rv3d->view;
361         if(rv3d->persp != RV3D_CAMOB) {
362                 rv3d->lpersp= rv3d->persp;
363         }
364
365         ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
366         DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
367         rv3d->persp = RV3D_CAMOB;
368         
369         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, v3d->camera);
370         
371         return OPERATOR_FINISHED;
372
373 }
374
375 static int view3d_setcameratoview_poll(bContext *C)
376 {
377         View3D *v3d = CTX_wm_view3d(C);
378         RegionView3D *rv3d= CTX_wm_region_view3d(C);
379
380         if (v3d==NULL || v3d->camera==NULL)     return 0;
381         if (rv3d && rv3d->viewlock != 0)                return 0;
382         return 1;
383 }
384
385 void VIEW3D_OT_setcameratoview(wmOperatorType *ot)
386 {
387         
388         /* identifiers */
389         ot->name= "Align Camera To View";
390         ot->description= "Set camera view to active view";
391         ot->idname= "VIEW3D_OT_camera_to_view";
392         
393         /* api callbacks */
394         ot->exec= view3d_setcameratoview_exec;  
395         ot->poll= view3d_setcameratoview_poll;
396         
397         /* flags */
398         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
399 }
400
401
402 static int view3d_setobjectascamera_exec(bContext *C, wmOperator *UNUSED(op))
403 {
404         View3D *v3d = CTX_wm_view3d(C);
405         ARegion *ar= ED_view3d_context_region_unlock(C);
406         RegionView3D *rv3d= ar->regiondata; /* no NULL check is needed, poll checks */
407         Scene *scene= CTX_data_scene(C);
408         Object *ob = CTX_data_active_object(C);
409
410         if(ob) {
411                 Object *camera_old= (rv3d->persp == RV3D_CAMOB) ? V3D_CAMERA_SCENE(scene, v3d) : NULL;
412                 rv3d->persp= RV3D_CAMOB;
413                 v3d->camera= ob;
414                 if(v3d->scenelock)
415                         scene->camera= ob;
416
417                 if(camera_old != ob) /* unlikely but looks like a glitch when set to the same */
418                         smooth_view(C, v3d, ar, camera_old, v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens);
419
420                 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS|NC_OBJECT|ND_DRAW, CTX_data_scene(C));
421         }
422         
423         return OPERATOR_FINISHED;
424 }
425
426 int ED_operator_rv3d_unlock_poll(bContext *C)
427 {
428         return ED_view3d_context_region_unlock(C) != NULL;
429 }
430
431 void VIEW3D_OT_object_as_camera(wmOperatorType *ot)
432 {
433         
434         /* identifiers */
435         ot->name= "Set Active Object as Camera";
436         ot->description= "Set the active object as the active camera for this view or scene";
437         ot->idname= "VIEW3D_OT_object_as_camera";
438         
439         /* api callbacks */
440         ot->exec= view3d_setobjectascamera_exec;        
441         ot->poll= ED_operator_rv3d_unlock_poll;
442         
443         /* flags */
444         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
445 }
446
447 /* ********************************** */
448
449 void view3d_calculate_clipping(BoundBox *bb, float planes[4][4], bglMats *mats, rcti *rect)
450 {
451         double xs, ys, p[3];
452         short val;
453
454         /* near zero floating point values can give issues with gluUnProject
455                 in side view on some implementations */
456         if(fabs(mats->modelview[0]) < 1e-6) mats->modelview[0]= 0.0;
457         if(fabs(mats->modelview[5]) < 1e-6) mats->modelview[5]= 0.0;
458
459         /* Set up viewport so that gluUnProject will give correct values */
460         mats->viewport[0] = 0;
461         mats->viewport[1] = 0;
462
463         /* four clipping planes and bounding volume */
464         /* first do the bounding volume */
465         for(val=0; val<4; val++) {
466                 xs= (val==0||val==3)?rect->xmin:rect->xmax;
467                 ys= (val==0||val==1)?rect->ymin:rect->ymax;
468
469                 gluUnProject(xs, ys, 0.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
470                 VECCOPY(bb->vec[val], p);
471
472                 gluUnProject(xs, ys, 1.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
473                 VECCOPY(bb->vec[4+val], p);
474         }
475
476         /* then plane equations */
477         for(val=0; val<4; val++) {
478
479                 normal_tri_v3(planes[val], bb->vec[val], bb->vec[val==3?0:val+1], bb->vec[val+4]);
480
481                 planes[val][3]= - planes[val][0]*bb->vec[val][0]
482                         - planes[val][1]*bb->vec[val][1]
483                         - planes[val][2]*bb->vec[val][2];
484         }
485 }
486
487 /* create intersection coordinates in view Z direction at mouse coordinates */
488 void viewline(ARegion *ar, View3D *v3d, float mval[2], float ray_start[3], float ray_end[3])
489 {
490         RegionView3D *rv3d= ar->regiondata;
491         float vec[4];
492         int a;
493         
494         if(!get_view3d_ortho(v3d, rv3d)) {
495                 vec[0]= 2.0f * mval[0] / ar->winx - 1;
496                 vec[1]= 2.0f * mval[1] / ar->winy - 1;
497                 vec[2]= -1.0f;
498                 vec[3]= 1.0f;
499                 
500                 mul_m4_v4(rv3d->persinv, vec);
501                 mul_v3_fl(vec, 1.0f / vec[3]);
502                 
503                 copy_v3_v3(ray_start, rv3d->viewinv[3]);
504                 sub_v3_v3(vec, ray_start);
505                 normalize_v3(vec);
506                 
507                 VECADDFAC(ray_start, rv3d->viewinv[3], vec, v3d->near);
508                 VECADDFAC(ray_end, rv3d->viewinv[3], vec, v3d->far);
509         }
510         else {
511                 vec[0] = 2.0f * mval[0] / ar->winx - 1;
512                 vec[1] = 2.0f * mval[1] / ar->winy - 1;
513                 vec[2] = 0.0f;
514                 vec[3] = 1.0f;
515                 
516                 mul_m4_v4(rv3d->persinv, vec);
517                 
518                 VECADDFAC(ray_start, vec, rv3d->viewinv[2],  1000.0f);
519                 VECADDFAC(ray_end, vec, rv3d->viewinv[2], -1000.0f);
520         }
521
522         /* clipping */
523         if(rv3d->rflag & RV3D_CLIPPING)
524                 for(a=0; a<4; a++)
525                         clip_line_plane(ray_start, ray_end, rv3d->clip[a]);
526 }
527
528 /* create intersection ray in view Z direction at mouse coordinates */
529 void viewray(ARegion *ar, View3D *v3d, float mval[2], float ray_start[3], float ray_normal[3])
530 {
531         float ray_end[3];
532         
533         viewline(ar, v3d, mval, ray_start, ray_end);
534         sub_v3_v3v3(ray_normal, ray_end, ray_start);
535         normalize_v3(ray_normal);
536 }
537
538 void viewvector(RegionView3D *rv3d, float coord[3], float vec[3])
539 {
540         if (rv3d->persp != RV3D_ORTHO)
541         {
542                 float p1[4], p2[4];
543
544                 copy_v3_v3(p1, coord);
545                 p1[3] = 1.0f;
546                 copy_v3_v3(p2, p1);
547                 p2[3] = 1.0f;
548                 mul_m4_v4(rv3d->viewmat, p2);
549
550                 mul_v3_fl(p2, 2.0f);
551
552                 mul_m4_v4(rv3d->viewinv, p2);
553
554                 sub_v3_v3v3(vec, p1, p2);
555         }
556         else {
557                 copy_v3_v3(vec, rv3d->viewinv[2]);
558         }
559         normalize_v3(vec);
560 }
561
562 int initgrabz(RegionView3D *rv3d, float x, float y, float z)
563 {
564         int flip= FALSE;
565         if(rv3d==NULL) return flip;
566         rv3d->zfac= rv3d->persmat[0][3]*x+ rv3d->persmat[1][3]*y+ rv3d->persmat[2][3]*z+ rv3d->persmat[3][3];
567         if (rv3d->zfac < 0.0f)
568                 flip= TRUE;
569         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that 
570                 * (accounting for near zero values)
571                 * */
572         if (rv3d->zfac < 1.e-6f && rv3d->zfac > -1.e-6f) rv3d->zfac = 1.0f;
573         
574         /* Negative zfac means x, y, z was behind the camera (in perspective).
575                 * This gives flipped directions, so revert back to ok default case.
576         */
577         // NOTE: I've changed this to flip zfac to be positive again for now so that GPencil draws ok
578         //      -- Aligorith, 2009Aug31
579         //if (rv3d->zfac < 0.0f) rv3d->zfac = 1.0f;
580         if (rv3d->zfac < 0.0f) rv3d->zfac= -rv3d->zfac;
581         
582         return flip;
583 }
584
585 /* always call initgrabz */
586 void window_to_3d(ARegion *ar, float out[3], const int mx, const int my)
587 {
588         RegionView3D *rv3d= ar->regiondata;
589         
590         float dx= ((float)(mx-(ar->winx/2)))*rv3d->zfac/(ar->winx/2);
591         float dy= ((float)(my-(ar->winy/2)))*rv3d->zfac/(ar->winy/2);
592         
593         float fz= rv3d->persmat[0][3]*out[0]+ rv3d->persmat[1][3]*out[1]+ rv3d->persmat[2][3]*out[2]+ rv3d->persmat[3][3];
594         fz= fz/rv3d->zfac;
595         
596         out[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy+ rv3d->persinv[2][0]*fz)-rv3d->ofs[0];
597         out[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy+ rv3d->persinv[2][1]*fz)-rv3d->ofs[1];
598         out[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy+ rv3d->persinv[2][2]*fz)-rv3d->ofs[2];
599         
600 }
601
602 /* always call initgrabz */
603 /* only to detect delta motion */
604 void window_to_3d_delta(ARegion *ar, float out[3], const int mx, const int my)
605 {
606         RegionView3D *rv3d= ar->regiondata;
607         float dx, dy;
608         
609         dx= 2.0f*mx*rv3d->zfac/ar->winx;
610         dy= 2.0f*my*rv3d->zfac/ar->winy;
611         
612         out[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy);
613         out[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy);
614         out[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy);
615 }
616
617 /* doesn't rely on initgrabz */
618 /* for perspective view, get the vector direction to
619  * the mouse cursor as a normalized vector */
620 void window_to_3d_vector(ARegion *ar, float out[3], const int mx, const int my)
621 {
622         RegionView3D *rv3d= ar->regiondata;
623         float dx, dy;
624         float viewvec[3];
625
626         dx= (2.0f * mx / ar->winx) - 1.0f;
627         dy= (2.0f * my / ar->winy) - 1.0f;
628
629         /* normalize here so vecs are proportional to eachother */
630         normalize_v3_v3(viewvec, rv3d->viewinv[2]);
631
632         out[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy) - viewvec[0];
633         out[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy) - viewvec[1];
634         out[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy) - viewvec[2];
635
636         normalize_v3(out);
637 }
638
639 float read_cached_depth(ViewContext *vc, int x, int y)
640 {
641         ViewDepths *vd = vc->rv3d->depths;
642                 
643         x -= vc->ar->winrct.xmin;
644         y -= vc->ar->winrct.ymin;
645
646         if(vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
647                 return vd->depths[y * vd->w + x];
648         else
649                 return 1;
650 }
651
652 void request_depth_update(RegionView3D *rv3d)
653 {
654         if(rv3d->depths)
655                 rv3d->depths->damaged= 1;
656 }
657
658 void view3d_get_object_project_mat(RegionView3D *rv3d, Object *ob, float pmat[4][4])
659 {
660         float vmat[4][4];
661         
662         mul_m4_m4m4(vmat, ob->obmat, rv3d->viewmat);
663         mul_m4_m4m4(pmat, vmat, rv3d->winmat);
664 }
665
666 /* Uses window coordinates (x,y) and depth component z to find a point in
667    modelspace */
668 void view3d_unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
669 {
670         double ux, uy, uz;
671
672                 gluUnProject(x,y,z, mats->modelview, mats->projection,
673                          (GLint *)mats->viewport, &ux, &uy, &uz );
674         out[0] = ux;
675         out[1] = uy;
676         out[2] = uz;
677 }
678
679 /* use above call to get projecting mat */
680 void view3d_project_float(ARegion *ar, const float vec[3], float adr[2], float mat[4][4])
681 {
682         float vec4[4];
683         
684         adr[0]= IS_CLIPPED;
685         copy_v3_v3(vec4, vec);
686         vec4[3]= 1.0;
687         
688         mul_m4_v4(mat, vec4);
689         
690         if( vec4[3]>FLT_EPSILON ) {
691                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
692                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
693         } else {
694                 adr[0] = adr[1] = 0.0f;
695         }
696 }
697
698 int boundbox_clip(RegionView3D *rv3d, float obmat[][4], BoundBox *bb)
699 {
700         /* return 1: draw */
701         
702         float mat[4][4];
703         float vec[4], min, max;
704         int a, flag= -1, fl;
705         
706         if(bb==NULL) return 1;
707         if(bb->flag & OB_BB_DISABLED) return 1;
708         
709         mul_m4_m4m4(mat, obmat, rv3d->persmat);
710         
711         for(a=0; a<8; a++) {
712                 copy_v3_v3(vec, bb->vec[a]);
713                 vec[3]= 1.0;
714                 mul_m4_v4(mat, vec);
715                 max= vec[3];
716                 min= -vec[3];
717                 
718                 fl= 0;
719                 if(vec[0] < min) fl+= 1;
720                 if(vec[0] > max) fl+= 2;
721                 if(vec[1] < min) fl+= 4;
722                 if(vec[1] > max) fl+= 8;
723                 if(vec[2] < min) fl+= 16;
724                 if(vec[2] > max) fl+= 32;
725                 
726                 flag &= fl;
727                 if(flag==0) return 1;
728         }
729         
730         return 0;
731 }
732
733 void project_short(ARegion *ar, const float vec[3], short adr[2])       /* clips */
734 {
735         RegionView3D *rv3d= ar->regiondata;
736         float fx, fy, vec4[4];
737         
738         adr[0]= IS_CLIPPED;
739         
740         if(rv3d->rflag & RV3D_CLIPPING) {
741                 if(view3d_test_clipping(rv3d, vec, 0))
742                         return;
743         }
744         
745         copy_v3_v3(vec4, vec);
746         vec4[3]= 1.0;
747         mul_m4_v4(rv3d->persmat, vec4);
748         
749         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
750                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
751                 
752                 if( fx>0 && fx<ar->winx) {
753                         
754                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
755                         
756                         if(fy > 0.0f && fy < (float)ar->winy) {
757                                 adr[0]= (short)floor(fx); 
758                                 adr[1]= (short)floor(fy);
759                         }
760                 }
761         }
762 }
763
764 void project_int(ARegion *ar, const float vec[3], int adr[2])
765 {
766         RegionView3D *rv3d= ar->regiondata;
767         float fx, fy, vec4[4];
768         
769         adr[0]= (int)2140000000.0f;
770         copy_v3_v3(vec4, vec);
771         vec4[3]= 1.0;
772         
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>-2140000000.0f && fx<2140000000.0f) {
779                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
780                         
781                         if(fy>-2140000000.0f && fy<2140000000.0f) {
782                                 adr[0]= (int)floor(fx); 
783                                 adr[1]= (int)floor(fy);
784                         }
785                 }
786         }
787 }
788
789 void project_int_noclip(ARegion *ar, const float vec[3], int adr[2])
790 {
791         RegionView3D *rv3d= ar->regiondata;
792         float fx, fy, vec4[4];
793         
794         copy_v3_v3(vec4, vec);
795         vec4[3]= 1.0;
796         
797         mul_m4_v4(rv3d->persmat, vec4);
798         
799         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
800                 fx = (ar->winx/2)*(1 + vec4[0]/vec4[3]);
801                 fy = (ar->winy/2)*(1 + vec4[1]/vec4[3]);
802                 
803                 adr[0] = (int)floor(fx); 
804                 adr[1] = (int)floor(fy);
805         }
806         else
807         {
808                 adr[0] = ar->winx / 2;
809                 adr[1] = ar->winy / 2;
810         }
811 }
812
813 void project_short_noclip(ARegion *ar, const float vec[3], short adr[2])
814 {
815         RegionView3D *rv3d= ar->regiondata;
816         float fx, fy, vec4[4];
817         
818         adr[0]= IS_CLIPPED;
819         copy_v3_v3(vec4, vec);
820         vec4[3]= 1.0;
821         
822         mul_m4_v4(rv3d->persmat, vec4);
823         
824         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
825                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
826                 
827                 if( fx>-32700 && fx<32700) {
828                         
829                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
830                         
831                         if(fy > -32700.0f && fy < 32700.0f) {
832                                 adr[0]= (short)floor(fx); 
833                                 adr[1]= (short)floor(fy);
834                         }
835                 }
836         }
837 }
838
839 void project_float(ARegion *ar, const float vec[3], float adr[2])
840 {
841         RegionView3D *rv3d= ar->regiondata;
842         float 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) {
851                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
852                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
853         }
854 }
855
856 void project_float_noclip(ARegion *ar, const float vec[3], float adr[2])
857 {
858         RegionView3D *rv3d= ar->regiondata;
859         float vec4[4];
860         
861         copy_v3_v3(vec4, vec);
862         vec4[3]= 1.0;
863         
864         mul_m4_v4(rv3d->persmat, vec4);
865         
866         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
867                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
868                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
869         }
870         else
871         {
872                 adr[0] = ar->winx / 2.0f;
873                 adr[1] = ar->winy / 2.0f;
874         }
875 }
876
877 int get_view3d_ortho(View3D *v3d, RegionView3D *rv3d)
878 {
879         Camera *cam;
880
881         if(rv3d->persp==RV3D_CAMOB) {
882                 if(v3d->camera && v3d->camera->type==OB_CAMERA) {
883                         cam= v3d->camera->data;
884
885                         if(cam && cam->type==CAM_ORTHO)
886                                 return 1;
887                         else
888                                 return 0;
889                 }
890                 else
891                         return 0;
892         }
893
894         if(rv3d->persp==RV3D_ORTHO)
895                 return 1;
896
897         return 0;
898 }
899
900 /* copies logic of get_view3d_viewplane(), keep in sync */
901 int get_view3d_cliprange(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
902 {
903         int orth= 0;
904
905         *clipsta= v3d->near;
906         *clipend= v3d->far;
907
908         if(rv3d->persp==RV3D_CAMOB) {
909                 if(v3d->camera) {
910                         if(v3d->camera->type==OB_LAMP ) {
911                                 Lamp *la= v3d->camera->data;
912                                 *clipsta= la->clipsta;
913                                 *clipend= la->clipend;
914                         }
915                         else if(v3d->camera->type==OB_CAMERA) {
916                                 Camera *cam= v3d->camera->data;
917                                 *clipsta= cam->clipsta;
918                                 *clipend= cam->clipend;
919
920                                 if(cam->type==CAM_ORTHO)
921                                         orth= 1;
922                         }
923                 }
924         }
925
926         if(rv3d->persp==RV3D_ORTHO) {
927                 *clipend *= 0.5f;       // otherwise too extreme low zbuffer quality
928                 *clipsta= - *clipend;
929                 orth= 1;
930         }
931
932         return orth;
933 }
934
935 /* also exposed in previewrender.c */
936 int get_view3d_viewplane(View3D *v3d, RegionView3D *rv3d, int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend, float *pixsize)
937 {
938         Camera *cam=NULL;
939         float lens, fac, x1, y1, x2, y2;
940         float winx= (float)winxi, winy= (float)winyi;
941         int orth= 0;
942         
943         lens= v3d->lens;        
944         
945         *clipsta= v3d->near;
946         *clipend= v3d->far;
947         
948         if(rv3d->persp==RV3D_CAMOB) {
949                 if(v3d->camera) {
950                         if(v3d->camera->type==OB_LAMP ) {
951                                 Lamp *la;
952                                 
953                                 la= v3d->camera->data;
954                                 fac= cosf(((float)M_PI)*la->spotsize/360.0f);
955                                 
956                                 x1= saacos(fac);
957                                 lens= 16.0f*fac/sinf(x1);
958                                 
959                                 *clipsta= la->clipsta;
960                                 *clipend= la->clipend;
961                         }
962                         else if(v3d->camera->type==OB_CAMERA) {
963                                 cam= v3d->camera->data;
964                                 lens= cam->lens;
965                                 *clipsta= cam->clipsta;
966                                 *clipend= cam->clipend;
967                         }
968                 }
969         }
970         
971         if(rv3d->persp==RV3D_ORTHO) {
972                 if(winx>winy) x1= -rv3d->dist;
973                 else x1= -winx*rv3d->dist/winy;
974                 x2= -x1;
975                 
976                 if(winx>winy) y1= -winy*rv3d->dist/winx;
977                 else y1= -rv3d->dist;
978                 y2= -y1;
979                 
980                 *clipend *= 0.5f;       // otherwise too extreme low zbuffer quality
981                 *clipsta= - *clipend;
982                 orth= 1;
983         }
984         else {
985                 /* fac for zoom, also used for camdx */
986                 if(rv3d->persp==RV3D_CAMOB) {
987                         fac= (1.41421f + ( (float)rv3d->camzoom )/50.0f);
988                         fac*= fac;
989                 }
990                 else fac= 2.0;
991                 
992                 /* viewplane size depends... */
993                 if(cam && cam->type==CAM_ORTHO) {
994                         /* ortho_scale == 1 means exact 1 to 1 mapping */
995                         float dfac= 2.0f*cam->ortho_scale/fac;
996                         
997                         if(winx>winy) x1= -dfac;
998                         else x1= -winx*dfac/winy;
999                         x2= -x1;
1000                         
1001                         if(winx>winy) y1= -winy*dfac/winx;
1002                         else y1= -dfac;
1003                         y2= -y1;
1004                         orth= 1;
1005                 }
1006                 else {
1007                         float dfac;
1008                         
1009                         if(winx>winy) dfac= 64.0f/(fac*winx*lens);
1010                         else dfac= 64.0f/(fac*winy*lens);
1011                         
1012                         x1= - *clipsta * winx*dfac;
1013                         x2= -x1;
1014                         y1= - *clipsta * winy*dfac;
1015                         y2= -y1;
1016                         orth= 0;
1017                 }
1018                 /* cam view offset */
1019                 if(cam) {
1020                         float dx= 0.5f*fac*rv3d->camdx*(x2-x1);
1021                         float dy= 0.5f*fac*rv3d->camdy*(y2-y1);
1022
1023                         /* shift offset */              
1024                         if(cam->type==CAM_ORTHO) {
1025                                 dx += cam->shiftx * cam->ortho_scale;
1026                                 dy += cam->shifty * cam->ortho_scale;
1027                         }
1028                         else {
1029                                 dx += cam->shiftx * (cam->clipsta / cam->lens) * 32.0f;
1030                                 dy += cam->shifty * (cam->clipsta / cam->lens) * 32.0f;
1031                         }
1032
1033                         x1+= dx;
1034                         x2+= dx;
1035                         y1+= dy;
1036                         y2+= dy;
1037                 }
1038         }
1039         
1040         if(pixsize) {
1041                 float viewfac;
1042                 
1043                 if(orth) {
1044                         viewfac= (winx >= winy)? winx: winy;
1045                         *pixsize= 1.0f/viewfac;
1046                 }
1047                 else {
1048                         viewfac= (((winx >= winy)? winx: winy)*lens)/32.0f;
1049                         *pixsize= *clipsta/viewfac;
1050                 }
1051         }
1052         
1053         viewplane->xmin= x1;
1054         viewplane->ymin= y1;
1055         viewplane->xmax= x2;
1056         viewplane->ymax= y2;
1057         
1058         return orth;
1059 }
1060
1061 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
1062 {
1063         RegionView3D *rv3d= ar->regiondata;
1064         rctf viewplane;
1065         float clipsta, clipend, x1, y1, x2, y2;
1066         int orth;
1067         
1068         orth= get_view3d_viewplane(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
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= 0; /* 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= 0;
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 }