committing working copy
[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 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_anim_types.h"
35 #include "DNA_camera_types.h"
36 #include "DNA_lamp_types.h"
37 #include "DNA_scene_types.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_math.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_editVert.h"
44 #include "BLI_rand.h"
45
46 #include "BKE_anim.h"
47 #include "BKE_action.h"
48 #include "BKE_context.h"
49 #include "BKE_object.h"
50 #include "BKE_global.h"
51 #include "BKE_main.h"
52 #include "BKE_report.h"
53 #include "BKE_scene.h"
54 #include "BKE_screen.h"
55 #include "BKE_utildefines.h"
56 #include "BKE_depsgraph.h" /* for fly mode updating */
57
58
59 #include "BIF_gl.h"
60 #include "BIF_glutil.h"
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64
65 #include "ED_keyframing.h"
66 #include "ED_screen.h"
67 #include "ED_armature.h"
68
69 #include "GPU_draw.h"
70
71
72 #include "PIL_time.h" /* smoothview */
73
74 #if GAMEBLENDER == 1
75 #include "SYS_System.h"
76 #endif
77
78 #include "view3d_intern.h"      // own include
79
80 /* use this call when executing an operator,
81    event system doesn't set for each event the
82    opengl drawing context */
83 void view3d_operator_needs_opengl(const bContext *C)
84 {
85         ARegion *ar= CTX_wm_region(C);
86
87         /* for debugging purpose, context should always be OK */
88         if(ar->regiontype!=RGN_TYPE_WINDOW)
89                 printf("view3d_operator_needs_opengl error, wrong region\n");
90         else {
91                 RegionView3D *rv3d= ar->regiondata;
92                 
93                 wmSubWindowSet(CTX_wm_window(C), 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 static void object_lens_clip_settings(Object *ob, float *lens, float *clipsta, float *clipend)
110 {       
111         if (!ob) return;
112         
113         if(ob->type==OB_LAMP ) {
114                 Lamp *la = ob->data;
115                 if (lens) {
116                         float x1, fac;
117                         fac= cos( M_PI*la->spotsize/360.0);
118                         x1= saacos(fac);
119                         *lens= 16.0*fac/sin(x1);
120                 }
121                 if (clipsta)    *clipsta= la->clipsta;
122                 if (clipend)    *clipend= la->clipend;
123         }
124         else if(ob->type==OB_CAMERA) {
125                 Camera *cam= ob->data;
126                 if (lens)               *lens= cam->lens;
127                 if (clipsta)    *clipsta= cam->clipsta;
128                 if (clipend)    *clipend= cam->clipend;
129         }
130         else {
131                 if (lens)               *lens= 35.0f;
132         }
133 }
134
135
136 /* Gets the view trasnformation from a camera
137 * currently dosnt take camzoom into account
138
139 * The dist is not modified for this function, if NULL its assimed zero
140 * */
141 void view3d_settings_from_ob(Object *ob, float *ofs, float *quat, float *dist, float *lens)
142 {
143         if (!ob) return;
144
145         /* Offset */
146         if (ofs)
147                 negate_v3_v3(ofs, ob->obmat[3]);
148
149         /* Quat */
150         if (quat) {
151                 float imat[4][4];
152                 invert_m4_m4(imat, ob->obmat);
153                 mat4_to_quat(quat, imat);
154         }
155
156         if (dist) {
157                 float vec[3] = {0.0f, 0.0f, -(*dist)};
158                 float tquat[4];
159
160                 mat4_to_quat(tquat, ob->obmat);
161
162                 mul_qt_v3(tquat, vec);
163
164                 sub_v3_v3(ofs, vec);
165         }
166
167         /* Lens */
168         if (lens)
169                 object_lens_clip_settings(ob, lens, NULL, NULL);
170 }
171
172
173 /* ****************** smooth view operator ****************** */
174 /* This operator is one of the 'timer refresh' ones like animation playback */
175
176 struct SmoothViewStore {
177         float orig_dist, new_dist;
178         float orig_lens, new_lens;
179         float orig_quat[4], new_quat[4];
180         float orig_ofs[3], new_ofs[3];
181         
182         int to_camera, orig_view;
183         
184         double time_allowed;
185 };
186
187 /* will start timer if appropriate */
188 /* the arguments are the desired situation */
189 void smooth_view(bContext *C, Object *oldcamera, Object *camera, float *ofs, float *quat, float *dist, float *lens)
190 {
191         View3D *v3d = CTX_wm_view3d(C);
192         RegionView3D *rv3d= CTX_wm_region_view3d(C);
193         struct SmoothViewStore sms;
194         
195         /* initialize sms */
196         memset(&sms,0,sizeof(struct SmoothViewStore));
197         VECCOPY(sms.new_ofs, rv3d->ofs);
198         QUATCOPY(sms.new_quat, rv3d->viewquat);
199         sms.new_dist= rv3d->dist;
200         sms.new_lens= v3d->lens;
201         sms.to_camera= 0;
202         
203         /* store the options we want to end with */
204         if(ofs) VECCOPY(sms.new_ofs, ofs);
205         if(quat) QUATCOPY(sms.new_quat, quat);
206         if(dist) sms.new_dist= *dist;
207         if(lens) sms.new_lens= *lens;
208         
209         if (camera) {
210                 view3d_settings_from_ob(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens);
211                 sms.to_camera= 1; /* restore view3d values in end */
212         }
213         
214         if (C && U.smooth_viewtx) {
215                 int changed = 0; /* zero means no difference */
216                 
217                 if (sms.new_dist != rv3d->dist)
218                         changed = 1;
219                 if (sms.new_lens != v3d->lens)
220                         changed = 1;
221                 
222                 if ((sms.new_ofs[0]!=rv3d->ofs[0]) ||
223                         (sms.new_ofs[1]!=rv3d->ofs[1]) ||
224                         (sms.new_ofs[2]!=rv3d->ofs[2]) )
225                         changed = 1;
226                 
227                 if ((sms.new_quat[0]!=rv3d->viewquat[0]) ||
228                         (sms.new_quat[1]!=rv3d->viewquat[1]) ||
229                         (sms.new_quat[2]!=rv3d->viewquat[2]) ||
230                         (sms.new_quat[3]!=rv3d->viewquat[3]) )
231                         changed = 1;
232                 
233                 /* The new view is different from the old one
234                         * so animate the view */
235                 if (changed) {
236                         
237                         sms.time_allowed= (double)U.smooth_viewtx / 1000.0;
238                         
239                         /* if this is view rotation only
240                                 * we can decrease the time allowed by
241                                 * the angle between quats 
242                                 * this means small rotations wont lag */
243                         if (quat && !ofs && !dist) {
244                                  float vec1[3], vec2[3];
245                                 
246                                  VECCOPY(vec1, sms.new_quat);
247                                  VECCOPY(vec2, sms.orig_quat);
248                                  normalize_v3(vec1);
249                                  normalize_v3(vec2);
250                                  /* scale the time allowed by the rotation */
251                                  sms.time_allowed *= angle_normalized_v3v3(vec1, vec2)/(M_PI/2); 
252                         }
253                         
254                         /* original values */
255                         if (oldcamera) {
256                                 sms.orig_dist= rv3d->dist; // below function does weird stuff with it...
257                                 view3d_settings_from_ob(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens);
258                         }
259                         else {
260                                 VECCOPY(sms.orig_ofs, rv3d->ofs);
261                                 QUATCOPY(sms.orig_quat, rv3d->viewquat);
262                                 sms.orig_dist= rv3d->dist;
263                                 sms.orig_lens= v3d->lens;
264                         }
265                         /* grid draw as floor */
266                         if((rv3d->viewlock & RV3D_LOCKED)==0) {
267                                 /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
268                                 sms.orig_view= rv3d->sms ? rv3d->sms->orig_view : rv3d->view;
269                                 rv3d->view= 0;
270                         }
271
272                         /* ensure it shows correct */
273                         if(sms.to_camera) rv3d->persp= RV3D_PERSP;
274
275                         rv3d->rflag |= RV3D_NAVIGATING;
276                         
277                         /* keep track of running timer! */
278                         if(rv3d->sms==NULL)
279                                 rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d");
280                         *rv3d->sms= sms;
281                         if(rv3d->smooth_timer)
282                                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
283                         /* TIMER1 is hardcoded in keymap */
284                         rv3d->smooth_timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER1, 1.0/100.0); /* max 30 frs/sec */
285                         
286                         return;
287                 }
288         }
289         
290         /* if we get here nothing happens */
291         if(sms.to_camera==0) {
292                 VECCOPY(rv3d->ofs, sms.new_ofs);
293                 QUATCOPY(rv3d->viewquat, sms.new_quat);
294                 rv3d->dist = sms.new_dist;
295                 v3d->lens = sms.new_lens;
296         }
297         ED_region_tag_redraw(CTX_wm_region(C));
298 }
299
300 /* only meant for timer usage */
301 static int view3d_smoothview_invoke(bContext *C, wmOperator *op, wmEvent *event)
302 {
303         View3D *v3d = CTX_wm_view3d(C);
304         RegionView3D *rv3d= CTX_wm_region_view3d(C);
305         struct SmoothViewStore *sms= rv3d->sms;
306         double step, step_inv;
307         
308         /* escape if not our timer */
309         if(rv3d->smooth_timer==NULL || rv3d->smooth_timer!=event->customdata)
310                 return OPERATOR_PASS_THROUGH;
311         
312         step =  (rv3d->smooth_timer->duration)/sms->time_allowed;
313         
314         /* end timer */
315         if(step >= 1.0f) {
316                 
317                 /* if we went to camera, store the original */
318                 if(sms->to_camera) {
319                         rv3d->persp= RV3D_CAMOB;
320                         VECCOPY(rv3d->ofs, sms->orig_ofs);
321                         QUATCOPY(rv3d->viewquat, sms->orig_quat);
322                         rv3d->dist = sms->orig_dist;
323                         v3d->lens = sms->orig_lens;
324                 }
325                 else {
326                         VECCOPY(rv3d->ofs, sms->new_ofs);
327                         QUATCOPY(rv3d->viewquat, sms->new_quat);
328                         rv3d->dist = sms->new_dist;
329                         v3d->lens = sms->new_lens;
330                 }
331                 
332                 if((rv3d->viewlock & RV3D_LOCKED)==0) {
333                         rv3d->view= sms->orig_view;
334                 }
335
336                 MEM_freeN(rv3d->sms);
337                 rv3d->sms= NULL;
338                 
339                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
340                 rv3d->smooth_timer= NULL;
341                 rv3d->rflag &= ~RV3D_NAVIGATING;
342         }
343         else {
344                 int i;
345                 
346                 /* ease in/out */
347                 if (step < 0.5) step = (float)pow(step*2, 2)/2;
348                 else                    step = (float)1-(pow(2*(1-step),2)/2);
349
350                 step_inv = 1.0-step;
351
352                 for (i=0; i<3; i++)
353                         rv3d->ofs[i] = sms->new_ofs[i]*step + sms->orig_ofs[i]*step_inv;
354
355                 interp_qt_qtqt(rv3d->viewquat, sms->orig_quat, sms->new_quat, step);
356                 
357                 rv3d->dist = sms->new_dist*step + sms->orig_dist*step_inv;
358                 v3d->lens = sms->new_lens*step + sms->orig_lens*step_inv;
359         }
360         
361         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
362         
363         return OPERATOR_FINISHED;
364 }
365
366 void VIEW3D_OT_smoothview(wmOperatorType *ot)
367 {
368         
369         /* identifiers */
370         ot->name= "Smooth View";
371         ot->idname= "VIEW3D_OT_smoothview";
372         ot->description="The time to animate the change of view (in milliseconds)";
373         
374         /* api callbacks */
375         ot->invoke= view3d_smoothview_invoke;
376         
377         ot->poll= ED_operator_view3d_active;
378 }
379
380 /* ****************** change view operators ****************** */
381
382 static void setcameratoview3d(View3D *v3d, RegionView3D *rv3d, Object *ob)
383 {
384         float dvec[3];
385         float mat3[3][3];
386
387         mul_v3_v3fl(dvec, rv3d->viewinv[2], rv3d->dist);
388         sub_v3_v3v3(ob->loc, dvec, rv3d->ofs);
389         rv3d->viewquat[0]= -rv3d->viewquat[0];
390
391         // quat_to_eul( ob->rot,rv3d->viewquat); // in 2.4x for xyz eulers only
392         quat_to_mat3(mat3, rv3d->viewquat);
393         object_mat3_to_rot(ob, mat3, 0);
394
395         rv3d->viewquat[0]= -rv3d->viewquat[0];
396         
397         ob->recalc= OB_RECALC_OB;
398 }
399
400
401 static int view3d_setcameratoview_exec(bContext *C, wmOperator *op)
402 {
403         View3D *v3d = CTX_wm_view3d(C);
404         RegionView3D *rv3d= CTX_wm_region_view3d(C);
405
406         setcameratoview3d(v3d, rv3d, v3d->camera);
407         rv3d->persp = RV3D_CAMOB;
408         
409         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, v3d->camera);
410         
411         return OPERATOR_FINISHED;
412
413 }
414
415 int view3d_setcameratoview_poll(bContext *C)
416 {
417         View3D *v3d = CTX_wm_view3d(C);
418         RegionView3D *rv3d= CTX_wm_region_view3d(C);
419
420         if (v3d==NULL || v3d->camera==NULL)     return 0;
421         if (rv3d && rv3d->viewlock != 0)                return 0;
422         return 1;
423 }
424
425 void VIEW3D_OT_setcameratoview(wmOperatorType *ot)
426 {
427         
428         /* identifiers */
429         ot->name= "Align Camera To View";
430         ot->description= "Set camera view to active view";
431         ot->idname= "VIEW3D_OT_camera_to_view";
432         
433         /* api callbacks */
434         ot->exec= view3d_setcameratoview_exec;  
435         ot->poll= view3d_setcameratoview_poll;
436         
437         /* flags */
438         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
439 }
440
441
442 static int view3d_setobjectascamera_exec(bContext *C, wmOperator *op)
443 {
444         View3D *v3d = CTX_wm_view3d(C);
445         RegionView3D *rv3d= CTX_wm_region_view3d(C);
446         Scene *scene= CTX_data_scene(C);
447         
448         if(BASACT) {
449                 rv3d->persp= RV3D_CAMOB;
450                 v3d->camera= OBACT;
451                 if(v3d->scenelock)
452                         scene->camera= OBACT;
453                 smooth_view(C, NULL, v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens);
454         }
455         
456         WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS|NC_OBJECT|ND_DRAW, CTX_data_scene(C));
457         
458         return OPERATOR_FINISHED;
459 }
460
461 void VIEW3D_OT_setobjectascamera(wmOperatorType *ot)
462 {
463         
464         /* identifiers */
465         ot->name= "Set Active Object as Camera";
466         ot->description= "Set the active object as the active camera for this view or scene";
467         ot->idname= "VIEW3D_OT_object_as_camera";
468         
469         /* api callbacks */
470         ot->exec= view3d_setobjectascamera_exec;        
471         ot->poll= ED_operator_view3d_active;
472         
473         /* flags */
474         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
475 }
476
477 /* ********************************** */
478
479 void view3d_calculate_clipping(BoundBox *bb, float planes[4][4], bglMats *mats, rcti *rect)
480 {
481         double xs, ys, p[3];
482         short val;
483
484         /* near zero floating point values can give issues with gluUnProject
485                 in side view on some implementations */
486         if(fabs(mats->modelview[0]) < 1e-6) mats->modelview[0]= 0.0;
487         if(fabs(mats->modelview[5]) < 1e-6) mats->modelview[5]= 0.0;
488
489         /* Set up viewport so that gluUnProject will give correct values */
490         mats->viewport[0] = 0;
491         mats->viewport[1] = 0;
492
493         /* four clipping planes and bounding volume */
494         /* first do the bounding volume */
495         for(val=0; val<4; val++) {
496                 xs= (val==0||val==3)?rect->xmin:rect->xmax;
497                 ys= (val==0||val==1)?rect->ymin:rect->ymax;
498
499                 gluUnProject(xs, ys, 0.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
500                 VECCOPY(bb->vec[val], p);
501
502                 gluUnProject(xs, ys, 1.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
503                 VECCOPY(bb->vec[4+val], p);
504         }
505
506         /* then plane equations */
507         for(val=0; val<4; val++) {
508
509                 normal_tri_v3(planes[val], bb->vec[val], bb->vec[val==3?0:val+1], bb->vec[val+4]);
510
511                 planes[val][3]= - planes[val][0]*bb->vec[val][0]
512                         - planes[val][1]*bb->vec[val][1]
513                         - planes[val][2]*bb->vec[val][2];
514         }
515 }
516
517 /* create intersection coordinates in view Z direction at mouse coordinates */
518 void viewline(ARegion *ar, View3D *v3d, float mval[2], float ray_start[3], float ray_end[3])
519 {
520         RegionView3D *rv3d= ar->regiondata;
521         float vec[4];
522         int a;
523         
524         if(!get_view3d_ortho(v3d, rv3d)) {
525                 vec[0]= 2.0f * mval[0] / ar->winx - 1;
526                 vec[1]= 2.0f * mval[1] / ar->winy - 1;
527                 vec[2]= -1.0f;
528                 vec[3]= 1.0f;
529                 
530                 mul_m4_v4(rv3d->persinv, vec);
531                 mul_v3_fl(vec, 1.0f / vec[3]);
532                 
533                 copy_v3_v3(ray_start, rv3d->viewinv[3]);
534                 sub_v3_v3(vec, ray_start);
535                 normalize_v3(vec);
536                 
537                 VECADDFAC(ray_start, rv3d->viewinv[3], vec, v3d->near);
538                 VECADDFAC(ray_end, rv3d->viewinv[3], vec, v3d->far);
539         }
540         else {
541                 vec[0] = 2.0f * mval[0] / ar->winx - 1;
542                 vec[1] = 2.0f * mval[1] / ar->winy - 1;
543                 vec[2] = 0.0f;
544                 vec[3] = 1.0f;
545                 
546                 mul_m4_v4(rv3d->persinv, vec);
547                 
548                 VECADDFAC(ray_start, vec, rv3d->viewinv[2],  1000.0f);
549                 VECADDFAC(ray_end, vec, rv3d->viewinv[2], -1000.0f);
550         }
551
552         /* clipping */
553         if(rv3d->rflag & RV3D_CLIPPING)
554                 for(a=0; a<4; a++)
555                         clip_line_plane(ray_start, ray_end, rv3d->clip[a]);
556 }
557
558 /* create intersection ray in view Z direction at mouse coordinates */
559 void viewray(ARegion *ar, View3D *v3d, float mval[2], float ray_start[3], float ray_normal[3])
560 {
561         float ray_end[3];
562         
563         viewline(ar, v3d, mval, ray_start, ray_end);
564         sub_v3_v3v3(ray_normal, ray_end, ray_start);
565         normalize_v3(ray_normal);
566 }
567
568 void viewvector(RegionView3D *rv3d, float coord[3], float vec[3])
569 {
570         if (rv3d->persp != RV3D_ORTHO)
571         {
572                 float p1[4], p2[4];
573
574                 VECCOPY(p1, coord);
575                 p1[3] = 1.0f;
576                 VECCOPY(p2, p1);
577                 p2[3] = 1.0f;
578                 mul_m4_v4(rv3d->viewmat, p2);
579
580                 mul_v3_fl(p2, 2.0f);
581
582                 mul_m4_v4(rv3d->viewinv, p2);
583
584                 sub_v3_v3v3(vec, p1, p2);
585         }
586         else {
587                 VECCOPY(vec, rv3d->viewinv[2]);
588         }
589         normalize_v3(vec);
590 }
591
592 void initgrabz(RegionView3D *rv3d, float x, float y, float z)
593 {
594         if(rv3d==NULL) return;
595         rv3d->zfac= rv3d->persmat[0][3]*x+ rv3d->persmat[1][3]*y+ rv3d->persmat[2][3]*z+ rv3d->persmat[3][3];
596         
597         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that 
598                 * (accounting for near zero values)
599                 * */
600         if (rv3d->zfac < 1.e-6f && rv3d->zfac > -1.e-6f) rv3d->zfac = 1.0f;
601         
602         /* Negative zfac means x, y, z was behind the camera (in perspective).
603                 * This gives flipped directions, so revert back to ok default case.
604         */
605         // NOTE: I've changed this to flip zfac to be positive again for now so that GPencil draws ok
606         //      -- Aligorith, 2009Aug31
607         //if (rv3d->zfac < 0.0f) rv3d->zfac = 1.0f;
608         if (rv3d->zfac < 0.0f) rv3d->zfac= -rv3d->zfac;
609 }
610
611 /* always call initgrabz */
612 void window_to_3d(ARegion *ar, float *vec, short mx, short my)
613 {
614         RegionView3D *rv3d= ar->regiondata;
615         
616         float dx= ((float)(mx-(ar->winx/2)))*rv3d->zfac/(ar->winx/2);
617         float dy= ((float)(my-(ar->winy/2)))*rv3d->zfac/(ar->winy/2);
618         
619         float fz= rv3d->persmat[0][3]*vec[0]+ rv3d->persmat[1][3]*vec[1]+ rv3d->persmat[2][3]*vec[2]+ rv3d->persmat[3][3];
620         fz= fz/rv3d->zfac;
621         
622         vec[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy+ rv3d->persinv[2][0]*fz)-rv3d->ofs[0];
623         vec[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy+ rv3d->persinv[2][1]*fz)-rv3d->ofs[1];
624         vec[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy+ rv3d->persinv[2][2]*fz)-rv3d->ofs[2];
625         
626 }
627
628 /* always call initgrabz */
629 /* only to detect delta motion */
630 void window_to_3d_delta(ARegion *ar, float *vec, short mx, short my)
631 {
632         RegionView3D *rv3d= ar->regiondata;
633         float dx, dy;
634         
635         dx= 2.0f*mx*rv3d->zfac/ar->winx;
636         dy= 2.0f*my*rv3d->zfac/ar->winy;
637         
638         vec[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy);
639         vec[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy);
640         vec[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy);
641 }
642
643 float read_cached_depth(ViewContext *vc, int x, int y)
644 {
645         ViewDepths *vd = vc->rv3d->depths;
646                 
647         x -= vc->ar->winrct.xmin;
648         y -= vc->ar->winrct.ymin;
649
650         if(vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
651                 return vd->depths[y * vd->w + x];
652         else
653                 return 1;
654 }
655
656 void request_depth_update(RegionView3D *rv3d)
657 {
658         if(rv3d->depths)
659                 rv3d->depths->damaged= 1;
660 }
661
662 void view3d_get_object_project_mat(RegionView3D *rv3d, Object *ob, float pmat[4][4])
663 {
664         float vmat[4][4];
665         
666         mul_m4_m4m4(vmat, ob->obmat, rv3d->viewmat);
667         mul_m4_m4m4(pmat, vmat, rv3d->winmat);
668 }
669
670 /* Uses window coordinates (x,y) and depth component z to find a point in
671    modelspace */
672 void view3d_unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
673 {
674         double ux, uy, uz;
675
676                 gluUnProject(x,y,z, mats->modelview, mats->projection,
677                          (GLint *)mats->viewport, &ux, &uy, &uz );
678         out[0] = ux;
679         out[1] = uy;
680         out[2] = uz;
681 }
682
683 /* use above call to get projecting mat */
684 void view3d_project_float(ARegion *ar, float *vec, float *adr, float mat[4][4])
685 {
686         float vec4[4];
687         
688         VECCOPY(vec4, vec);
689         adr[0]= IS_CLIPPED;
690         vec4[3]= 1.0;
691         
692         mul_m4_v4(mat, vec4);
693         
694         if( vec4[3]>FLT_EPSILON ) {
695                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
696                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
697         } else {
698                 adr[0] = adr[1] = 0.0f;
699         }
700 }
701
702 /* use above call to get projecting mat */
703 void view3d_project_float_v3(ARegion *ar, float *vec, float *adr, float mat[4][4])
704 {
705         float vec4[4];
706         
707         VECCOPY(vec4, vec);
708         adr[0]= IS_CLIPPED;
709         vec4[3]= 1.0;
710         
711         mul_m4_v4(mat, vec4);
712         
713         if( vec4[3]>FLT_EPSILON ) {
714                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
715                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
716                 adr[2] = vec4[2]/vec4[3];
717         } else {
718                 adr[0] = adr[1] = adr[2] = 0.0f;
719         }
720 }
721
722 int boundbox_clip(RegionView3D *rv3d, float obmat[][4], BoundBox *bb)
723 {
724         /* return 1: draw */
725         
726         float mat[4][4];
727         float vec[4], min, max;
728         int a, flag= -1, fl;
729         
730         if(bb==NULL) return 1;
731         if(bb->flag & OB_BB_DISABLED) return 1;
732         
733         mul_m4_m4m4(mat, obmat, rv3d->persmat);
734         
735         for(a=0; a<8; a++) {
736                 VECCOPY(vec, bb->vec[a]);
737                 vec[3]= 1.0;
738                 mul_m4_v4(mat, vec);
739                 max= vec[3];
740                 min= -vec[3];
741                 
742                 fl= 0;
743                 if(vec[0] < min) fl+= 1;
744                 if(vec[0] > max) fl+= 2;
745                 if(vec[1] < min) fl+= 4;
746                 if(vec[1] > max) fl+= 8;
747                 if(vec[2] < min) fl+= 16;
748                 if(vec[2] > max) fl+= 32;
749                 
750                 flag &= fl;
751                 if(flag==0) return 1;
752         }
753         
754         return 0;
755 }
756
757 void project_short(ARegion *ar, float *vec, short *adr) /* clips */
758 {
759         RegionView3D *rv3d= ar->regiondata;
760         float fx, fy, vec4[4];
761         
762         adr[0]= IS_CLIPPED;
763         
764         if(rv3d->rflag & RV3D_CLIPPING) {
765                 if(view3d_test_clipping(rv3d, vec, 0))
766                         return;
767         }
768         
769         VECCOPY(vec4, vec);
770         vec4[3]= 1.0;
771         mul_m4_v4(rv3d->persmat, vec4);
772         
773         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
774                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
775                 
776                 if( fx>0 && fx<ar->winx) {
777                         
778                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
779                         
780                         if(fy>0.0 && fy< (float)ar->winy) {
781                                 adr[0]= (short)floor(fx); 
782                                 adr[1]= (short)floor(fy);
783                         }
784                 }
785         }
786 }
787
788 void project_int(ARegion *ar, float *vec, int *adr)
789 {
790         RegionView3D *rv3d= ar->regiondata;
791         float fx, fy, vec4[4];
792         
793         adr[0]= (int)2140000000.0f;
794         VECCOPY(vec4, vec);
795         vec4[3]= 1.0;
796         
797         mul_m4_v4(rv3d->persmat, vec4);
798         
799         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
800                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
801                 
802                 if( fx>-2140000000.0f && fx<2140000000.0f) {
803                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
804                         
805                         if(fy>-2140000000.0f && fy<2140000000.0f) {
806                                 adr[0]= (int)floor(fx); 
807                                 adr[1]= (int)floor(fy);
808                         }
809                 }
810         }
811 }
812
813 void project_int_noclip(ARegion *ar, float *vec, int *adr)
814 {
815         RegionView3D *rv3d= ar->regiondata;
816         float fx, fy, vec4[4];
817         
818         VECCOPY(vec4, vec);
819         vec4[3]= 1.0;
820         
821         mul_m4_v4(rv3d->persmat, vec4);
822         
823         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
824                 fx = (ar->winx/2)*(1 + vec4[0]/vec4[3]);
825                 fy = (ar->winy/2)*(1 + vec4[1]/vec4[3]);
826                 
827                 adr[0] = (int)floor(fx); 
828                 adr[1] = (int)floor(fy);
829         }
830         else
831         {
832                 adr[0] = ar->winx / 2;
833                 adr[1] = ar->winy / 2;
834         }
835 }
836
837 void project_short_noclip(ARegion *ar, float *vec, short *adr)
838 {
839         RegionView3D *rv3d= ar->regiondata;
840         float fx, fy, vec4[4];
841         
842         adr[0]= IS_CLIPPED;
843         VECCOPY(vec4, vec);
844         vec4[3]= 1.0;
845         
846         mul_m4_v4(rv3d->persmat, vec4);
847         
848         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
849                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
850                 
851                 if( fx>-32700 && fx<32700) {
852                         
853                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
854                         
855                         if(fy>-32700.0 && fy<32700.0) {
856                                 adr[0]= (short)floor(fx); 
857                                 adr[1]= (short)floor(fy);
858                         }
859                 }
860         }
861 }
862
863 void project_float(ARegion *ar, float *vec, float *adr)
864 {
865         RegionView3D *rv3d= ar->regiondata;
866         float vec4[4];
867         
868         adr[0]= IS_CLIPPED;
869         VECCOPY(vec4, vec);
870         vec4[3]= 1.0;
871         
872         mul_m4_v4(rv3d->persmat, vec4);
873         
874         if( vec4[3]>BL_NEAR_CLIP ) {
875                 adr[0] = (float)(ar->winx/2.0)+(ar->winx/2.0)*vec4[0]/vec4[3];  
876                 adr[1] = (float)(ar->winy/2.0)+(ar->winy/2.0)*vec4[1]/vec4[3];
877         }
878 }
879
880 void project_float_noclip(ARegion *ar, float *vec, float *adr)
881 {
882         RegionView3D *rv3d= ar->regiondata;
883         float vec4[4];
884         
885         VECCOPY(vec4, vec);
886         vec4[3]= 1.0;
887         
888         mul_m4_v4(rv3d->persmat, vec4);
889         
890         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
891                 adr[0] = (float)(ar->winx/2.0)+(ar->winx/2.0)*vec4[0]/vec4[3];  
892                 adr[1] = (float)(ar->winy/2.0)+(ar->winy/2.0)*vec4[1]/vec4[3];
893         }
894         else
895         {
896                 adr[0] = ar->winx / 2.0f;
897                 adr[1] = ar->winy / 2.0f;
898         }
899 }
900
901 int get_view3d_ortho(View3D *v3d, RegionView3D *rv3d)
902 {
903   Camera *cam;
904   
905   if(rv3d->persp==RV3D_CAMOB) {
906           if(v3d->camera && v3d->camera->type==OB_CAMERA) {
907                   cam= v3d->camera->data;
908
909                   if(cam && cam->type==CAM_ORTHO)
910                           return 1;
911                   else
912                           return 0;
913           }
914           else
915                   return 0;
916   }
917   
918   if(rv3d->persp==RV3D_ORTHO)
919           return 1;
920
921   return 0;
922 }
923
924 /* copies logic of get_view3d_viewplane(), keep in sync */
925 int get_view3d_cliprange(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
926 {
927         int orth= 0;
928
929         *clipsta= v3d->near;
930         *clipend= v3d->far;
931
932         if(rv3d->persp==RV3D_CAMOB) {
933                 if(v3d->camera) {
934                         if(v3d->camera->type==OB_LAMP ) {
935                                 Lamp *la= v3d->camera->data;
936                                 *clipsta= la->clipsta;
937                                 *clipend= la->clipend;
938                         }
939                         else if(v3d->camera->type==OB_CAMERA) {
940                                 Camera *cam= v3d->camera->data;
941                                 *clipsta= cam->clipsta;
942                                 *clipend= cam->clipend;
943
944                                 if(cam->type==CAM_ORTHO)
945                                         orth= 1;
946                         }
947                 }
948         }
949
950         if(rv3d->persp==RV3D_ORTHO) {
951                 *clipend *= 0.5;        // otherwise too extreme low zbuffer quality
952                 *clipsta= - *clipend;
953                 orth= 1;
954         }
955
956         return orth;
957 }
958
959 /* also exposed in previewrender.c */
960 int get_view3d_viewplane(View3D *v3d, RegionView3D *rv3d, int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend, float *pixsize)
961 {
962         Camera *cam=NULL;
963         float lens, fac, x1, y1, x2, y2;
964         float winx= (float)winxi, winy= (float)winyi;
965         int orth= 0;
966         
967         lens= v3d->lens;        
968         
969         *clipsta= v3d->near;
970         *clipend= v3d->far;
971         
972         if(rv3d->persp==RV3D_CAMOB) {
973                 if(v3d->camera) {
974                         if(v3d->camera->type==OB_LAMP ) {
975                                 Lamp *la;
976                                 
977                                 la= v3d->camera->data;
978                                 fac= cos( M_PI*la->spotsize/360.0);
979                                 
980                                 x1= saacos(fac);
981                                 lens= 16.0*fac/sin(x1);
982                                 
983                                 *clipsta= la->clipsta;
984                                 *clipend= la->clipend;
985                         }
986                         else if(v3d->camera->type==OB_CAMERA) {
987                                 cam= v3d->camera->data;
988                                 lens= cam->lens;
989                                 *clipsta= cam->clipsta;
990                                 *clipend= cam->clipend;
991                         }
992                 }
993         }
994         
995         if(rv3d->persp==RV3D_ORTHO) {
996                 if(winx>winy) x1= -rv3d->dist;
997                 else x1= -winx*rv3d->dist/winy;
998                 x2= -x1;
999                 
1000                 if(winx>winy) y1= -winy*rv3d->dist/winx;
1001                 else y1= -rv3d->dist;
1002                 y2= -y1;
1003                 
1004                 *clipend *= 0.5;        // otherwise too extreme low zbuffer quality
1005                 *clipsta= - *clipend;
1006                 orth= 1;
1007         }
1008         else {
1009                 /* fac for zoom, also used for camdx */
1010                 if(rv3d->persp==RV3D_CAMOB) {
1011                         fac= (1.41421+( (float)rv3d->camzoom )/50.0);
1012                         fac*= fac;
1013                 }
1014                 else fac= 2.0;
1015                 
1016                 /* viewplane size depends... */
1017                 if(cam && cam->type==CAM_ORTHO) {
1018                         /* ortho_scale == 1 means exact 1 to 1 mapping */
1019                         float dfac= 2.0*cam->ortho_scale/fac;
1020                         
1021                         if(winx>winy) x1= -dfac;
1022                         else x1= -winx*dfac/winy;
1023                         x2= -x1;
1024                         
1025                         if(winx>winy) y1= -winy*dfac/winx;
1026                         else y1= -dfac;
1027                         y2= -y1;
1028                         orth= 1;
1029                 }
1030                 else {
1031                         float dfac;
1032                         
1033                         if(winx>winy) dfac= 64.0/(fac*winx*lens);
1034                         else dfac= 64.0/(fac*winy*lens);
1035                         
1036                         x1= - *clipsta * winx*dfac;
1037                         x2= -x1;
1038                         y1= - *clipsta * winy*dfac;
1039                         y2= -y1;
1040                         orth= 0;
1041                 }
1042                 /* cam view offset */
1043                 if(cam) {
1044                         float dx= 0.5*fac*rv3d->camdx*(x2-x1);
1045                         float dy= 0.5*fac*rv3d->camdy*(y2-y1);
1046                         x1+= dx;
1047                         x2+= dx;
1048                         y1+= dy;
1049                         y2+= dy;
1050                 }
1051         }
1052         
1053         if(pixsize) {
1054                 float viewfac;
1055                 
1056                 if(orth) {
1057                         viewfac= (winx >= winy)? winx: winy;
1058                         *pixsize= 1.0f/viewfac;
1059                 }
1060                 else {
1061                         viewfac= (((winx >= winy)? winx: winy)*lens)/32.0;
1062                         *pixsize= *clipsta/viewfac;
1063                 }
1064         }
1065         
1066         viewplane->xmin= x1;
1067         viewplane->ymin= y1;
1068         viewplane->xmax= x2;
1069         viewplane->ymax= y2;
1070         
1071         return orth;
1072 }
1073
1074 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
1075 {
1076         RegionView3D *rv3d= ar->regiondata;
1077         rctf viewplane;
1078         float clipsta, clipend, x1, y1, x2, y2;
1079         int orth;
1080         
1081         orth= get_view3d_viewplane(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
1082         //      printf("%d %d %f %f %f %f %f %f\n", winx, winy, viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax, clipsta, clipend);
1083         x1= viewplane.xmin;
1084         y1= viewplane.ymin;
1085         x2= viewplane.xmax;
1086         y2= viewplane.ymax;
1087         
1088         if(rect) {              /* picking */
1089                 rect->xmin/= (float)ar->winx;
1090                 rect->xmin= x1+rect->xmin*(x2-x1);
1091                 rect->ymin/= (float)ar->winy;
1092                 rect->ymin= y1+rect->ymin*(y2-y1);
1093                 rect->xmax/= (float)ar->winx;
1094                 rect->xmax= x1+rect->xmax*(x2-x1);
1095                 rect->ymax/= (float)ar->winy;
1096                 rect->ymax= y1+rect->ymax*(y2-y1);
1097                 
1098                 if(orth) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
1099                 else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
1100                 
1101         }
1102         else {
1103                 if(orth) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
1104                 else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
1105         }
1106
1107         /* update matrix in 3d view region */
1108         glGetFloatv(GL_PROJECTION_MATRIX, (float*)rv3d->winmat);
1109 }
1110
1111 static void obmat_to_viewmat(View3D *v3d, RegionView3D *rv3d, Object *ob, short smooth)
1112 {
1113         float bmat[4][4];
1114         float tmat[3][3];
1115         
1116         rv3d->view= 0; /* dont show the grid */
1117         
1118         copy_m4_m4(bmat, ob->obmat);
1119         normalize_m4(bmat);
1120         invert_m4_m4(rv3d->viewmat, bmat);
1121         
1122         /* view quat calculation, needed for add object */
1123         copy_m3_m4(tmat, rv3d->viewmat);
1124         if (smooth) {
1125                 float new_quat[4];
1126                 if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1127                         /* were from a camera view */
1128                         
1129                         float orig_ofs[3];
1130                         float orig_dist= rv3d->dist;
1131                         float orig_lens= v3d->lens;
1132                         VECCOPY(orig_ofs, rv3d->ofs);
1133                         
1134                         /* Switch from camera view */
1135                         mat3_to_quat( new_quat,tmat);
1136                         
1137                         rv3d->persp=RV3D_PERSP;
1138                         rv3d->dist= 0.0;
1139                         
1140                         view3d_settings_from_ob(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1141                         smooth_view(NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1142                         
1143                         rv3d->persp=RV3D_CAMOB; /* just to be polite, not needed */
1144                         
1145                 } else {
1146                         mat3_to_quat( new_quat,tmat);
1147                         smooth_view(NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1148                 }
1149         } else {
1150                 mat3_to_quat( rv3d->viewquat,tmat);
1151         }
1152 }
1153
1154 #define QUATSET(a, b, c, d, e)  a[0]=b; a[1]=c; a[2]=d; a[3]=e; 
1155
1156 static void view3d_viewlock(RegionView3D *rv3d)
1157 {
1158         switch(rv3d->view) {
1159         case RV3D_VIEW_BOTTOM :
1160                 QUATSET(rv3d->viewquat,0.0, -1.0, 0.0, 0.0);
1161                 break;
1162                 
1163         case RV3D_VIEW_BACK:
1164                 QUATSET(rv3d->viewquat,0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0));
1165                 break;
1166                 
1167         case RV3D_VIEW_LEFT:
1168                 QUATSET(rv3d->viewquat,0.5, -0.5, 0.5, 0.5);
1169                 break;
1170                 
1171         case RV3D_VIEW_TOP:
1172                 QUATSET(rv3d->viewquat,1.0, 0.0, 0.0, 0.0);
1173                 break;
1174                 
1175         case RV3D_VIEW_FRONT:
1176                 QUATSET(rv3d->viewquat,(float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0);
1177                 break;
1178                 
1179         case RV3D_VIEW_RIGHT:
1180                 QUATSET(rv3d->viewquat, 0.5, -0.5, -0.5, -0.5);
1181                 break;
1182         }
1183 }
1184
1185 /* give a 4x4 matrix from a perspective view, only needs viewquat, ofs and dist
1186  * basically the same as...
1187  *     rv3d->persp= RV3D_PERSP
1188  *     setviewmatrixview3d(scene, v3d, rv3d);
1189  *     setcameratoview3d(v3d, rv3d, v3d->camera);
1190  * ...but less of a hassle
1191  * */
1192 static void view3d_persp_mat4(RegionView3D *rv3d, float mat[][4])
1193 {
1194         float qt[4], dvec[3];
1195         copy_qt_qt(qt, rv3d->viewquat);
1196         qt[0]= -qt[0];
1197         quat_to_mat4(mat, qt);
1198         mat[3][2] -= rv3d->dist;
1199         translate_m4(mat, rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
1200         mul_v3_v3fl(dvec, mat[2], -rv3d->dist);
1201         sub_v3_v3v3(mat[3], dvec, rv3d->ofs);
1202 }
1203
1204 /* dont set windows active in in here, is used by renderwin too */
1205 void setviewmatrixview3d(Scene *scene, View3D *v3d, RegionView3D *rv3d)
1206 {
1207         if(rv3d->persp==RV3D_CAMOB) {       /* obs/camera */
1208                 if(v3d->camera) {
1209                         where_is_object(scene, v3d->camera);    
1210                         obmat_to_viewmat(v3d, rv3d, v3d->camera, 0);
1211                 }
1212                 else {
1213                         quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1214                         rv3d->viewmat[3][2]-= rv3d->dist;
1215                 }
1216         }
1217         else {
1218                 /* should be moved to better initialize later on XXX */
1219                 if(rv3d->viewlock)
1220                         view3d_viewlock(rv3d);
1221                 
1222                 quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1223                 if(rv3d->persp==RV3D_PERSP) rv3d->viewmat[3][2]-= rv3d->dist;
1224                 if(v3d->ob_centre) {
1225                         Object *ob= v3d->ob_centre;
1226                         float vec[3];
1227                         
1228                         VECCOPY(vec, ob->obmat[3]);
1229                         if(ob->type==OB_ARMATURE && v3d->ob_centre_bone[0]) {
1230                                 bPoseChannel *pchan= get_pose_channel(ob->pose, v3d->ob_centre_bone);
1231                                 if(pchan) {
1232                                         VECCOPY(vec, pchan->pose_mat[3]);
1233                                         mul_m4_v3(ob->obmat, vec);
1234                                 }
1235                         }
1236                         translate_m4( rv3d->viewmat,-vec[0], -vec[1], -vec[2]);
1237                 }
1238                 else translate_m4( rv3d->viewmat,rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
1239         }
1240 }
1241
1242 /* IGLuint-> GLuint*/
1243 /* Warning: be sure to account for a negative return value
1244 *   This is an error, "Too many objects in select buffer"
1245 *   and no action should be taken (can crash blender) if this happens
1246 */
1247 short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
1248 {
1249         Scene *scene= vc->scene;
1250         View3D *v3d= vc->v3d;
1251         ARegion *ar= vc->ar;
1252         rctf rect;
1253         short code, hits;
1254         char dt, dtx;
1255         
1256         G.f |= G_PICKSEL;
1257         
1258         /* case not a border select */
1259         if(input->xmin==input->xmax) {
1260                 rect.xmin= input->xmin-12;      // seems to be default value for bones only now
1261                 rect.xmax= input->xmin+12;
1262                 rect.ymin= input->ymin-12;
1263                 rect.ymax= input->ymin+12;
1264         }
1265         else {
1266                 rect.xmin= input->xmin;
1267                 rect.xmax= input->xmax;
1268                 rect.ymin= input->ymin;
1269                 rect.ymax= input->ymax;
1270         }
1271         
1272         setwinmatrixview3d(ar, v3d, &rect);
1273         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1274         
1275         if(v3d->drawtype > OB_WIRE) {
1276                 v3d->zbuf= TRUE;
1277                 glEnable(GL_DEPTH_TEST);
1278         }
1279         
1280         if(vc->rv3d->rflag & RV3D_CLIPPING)
1281                 view3d_set_clipping(vc->rv3d);
1282         
1283         glSelectBuffer( bufsize, (GLuint *)buffer);
1284         glRenderMode(GL_SELECT);
1285         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1286         glPushName(-1);
1287         code= 1;
1288         
1289         if(vc->obedit && vc->obedit->type==OB_MBALL) {
1290                 draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1291         }
1292         else if((vc->obedit && vc->obedit->type==OB_ARMATURE)) {
1293                 /* if not drawing sketch, draw bones */
1294                 if(!BDR_drawSketchNames(vc)) {
1295                         draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1296                 }
1297         }
1298         else {
1299                 Base *base;
1300                 
1301                 v3d->xray= TRUE;        // otherwise it postpones drawing
1302                 for(base= scene->base.first; base; base= base->next) {
1303                         if(base->lay & v3d->lay) {
1304                                 
1305                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1306                                         base->selcol= 0;
1307                                 else {
1308                                         base->selcol= code;
1309                                         glLoadName(code);
1310                                         draw_object(scene, ar, v3d, base, DRAW_PICKING|DRAW_CONSTCOLOR);
1311                                         
1312                                         /* we draw group-duplicators for selection too */
1313                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1314                                                 ListBase *lb;
1315                                                 DupliObject *dob;
1316                                                 Base tbase;
1317                                                 
1318                                                 tbase.flag= OB_FROMDUPLI;
1319                                                 lb= object_duplilist(scene, base->object);
1320                                                 
1321                                                 for(dob= lb->first; dob; dob= dob->next) {
1322                                                         tbase.object= dob->ob;
1323                                                         copy_m4_m4(dob->ob->obmat, dob->mat);
1324                                                         
1325                                                         /* extra service: draw the duplicator in drawtype of parent */
1326                                                         /* MIN2 for the drawtype to allow bounding box objects in groups for lods */
1327                                                         dt= tbase.object->dt;   tbase.object->dt= MIN2(tbase.object->dt, base->object->dt);
1328                                                         dtx= tbase.object->dtx; tbase.object->dtx= base->object->dtx;
1329
1330                                                         draw_object(scene, ar, v3d, &tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1331                                                         
1332                                                         tbase.object->dt= dt;
1333                                                         tbase.object->dtx= dtx;
1334
1335                                                         copy_m4_m4(dob->ob->obmat, dob->omat);
1336                                                 }
1337                                                 free_object_duplilist(lb);
1338                                         }
1339                                         code++;
1340                                 }                               
1341                         }
1342                 }
1343                 v3d->xray= FALSE;       // restore
1344         }
1345         
1346         glPopName();    /* see above (pushname) */
1347         hits= glRenderMode(GL_RENDER);
1348         
1349         G.f &= ~G_PICKSEL;
1350         setwinmatrixview3d(ar, v3d, NULL);
1351         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1352         
1353         if(v3d->drawtype > OB_WIRE) {
1354                 v3d->zbuf= 0;
1355                 glDisable(GL_DEPTH_TEST);
1356         }
1357 // XXX  persp(PERSP_WIN);
1358         
1359         if(vc->rv3d->rflag & RV3D_CLIPPING)
1360                 view3d_clr_clipping();
1361         
1362         if(hits<0) printf("Too many objects in select buffer\n");       // XXX make error message
1363         
1364         return hits;
1365 }
1366
1367 /* ********************** local view operator ******************** */
1368
1369 static unsigned int free_localbit(void)
1370 {
1371         unsigned int lay;
1372         ScrArea *sa;
1373         bScreen *sc;
1374         
1375         lay= 0;
1376         
1377         /* sometimes we loose a localview: when an area is closed */
1378         /* check all areas: which localviews are in use? */
1379         for(sc= G.main->screen.first; sc; sc= sc->id.next) {
1380                 for(sa= sc->areabase.first; sa; sa= sa->next) {
1381                         SpaceLink *sl= sa->spacedata.first;
1382                         for(; sl; sl= sl->next) {
1383                                 if(sl->spacetype==SPACE_VIEW3D) {
1384                                         View3D *v3d= (View3D*) sl;
1385                                         lay |= v3d->lay;
1386                                 }
1387                         }
1388                 }
1389         }
1390         
1391         if( (lay & 0x01000000)==0) return 0x01000000;
1392         if( (lay & 0x02000000)==0) return 0x02000000;
1393         if( (lay & 0x04000000)==0) return 0x04000000;
1394         if( (lay & 0x08000000)==0) return 0x08000000;
1395         if( (lay & 0x10000000)==0) return 0x10000000;
1396         if( (lay & 0x20000000)==0) return 0x20000000;
1397         if( (lay & 0x40000000)==0) return 0x40000000;
1398         if( (lay & 0x80000000)==0) return 0x80000000;
1399         
1400         return 0;
1401 }
1402
1403 int ED_view3d_scene_layer_set(int lay, const int *values, int *active)
1404 {
1405         int i, tot= 0;
1406         
1407         /* ensure we always have some layer selected */
1408         for(i=0; i<20; i++)
1409                 if(values[i])
1410                         tot++;
1411         
1412         if(tot==0)
1413                 return lay;
1414         
1415         for(i=0; i<20; i++) {
1416                 
1417                 if (active) {
1418                         /* if this value has just been switched on, make that layer active */
1419                         if (values[i] && (lay & (1<<i))==0) {
1420                                 *active = (1<<i);
1421                         }
1422                 }
1423                         
1424                 if (values[i]) lay |= (1<<i);
1425                 else lay &= ~(1<<i);
1426         }
1427         
1428         /* ensure always an active layer */
1429         if (active && (lay & *active)==0) {
1430                 for(i=0; i<20; i++) {
1431                         if(lay & (1<<i)) {
1432                                 *active= 1<<i;
1433                                 break;
1434                         }
1435                 }
1436         }
1437         
1438         return lay;
1439 }
1440
1441 static void initlocalview(Scene *scene, ScrArea *sa)
1442 {
1443         View3D *v3d= sa->spacedata.first;
1444         Base *base;
1445         float size = 0.0, min[3], max[3], box[3];
1446         unsigned int locallay;
1447         int ok=0;
1448
1449         if(v3d->localvd) return;
1450
1451         INIT_MINMAX(min, max);
1452
1453         locallay= free_localbit();
1454
1455         if(locallay==0) {
1456                 printf("Sorry, no more than 8 localviews\n");   // XXX error 
1457                 ok= 0;
1458         }
1459         else {
1460                 if(scene->obedit) {
1461                         minmax_object(scene->obedit, min, max);
1462                         
1463                         ok= 1;
1464                 
1465                         BASACT->lay |= locallay;
1466                         scene->obedit->lay= BASACT->lay;
1467                 }
1468                 else {
1469                         for(base= FIRSTBASE; base; base= base->next) {
1470                                 if(TESTBASE(v3d, base))  {
1471                                         minmax_object(base->object, min, max);
1472                                         base->lay |= locallay;
1473                                         base->object->lay= base->lay;
1474                                         ok= 1;
1475                                 }
1476                         }
1477                 }
1478                 
1479                 box[0]= (max[0]-min[0]);
1480                 box[1]= (max[1]-min[1]);
1481                 box[2]= (max[2]-min[2]);
1482                 size= MAX3(box[0], box[1], box[2]);
1483                 if(size<=0.01) size= 0.01;
1484         }
1485         
1486         if(ok) {
1487                 ARegion *ar;
1488                 
1489                 v3d->localvd= MEM_mallocN(sizeof(View3D), "localview");
1490                 
1491                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1492
1493                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
1494                         if(ar->regiontype == RGN_TYPE_WINDOW) {
1495                                 RegionView3D *rv3d= ar->regiondata;
1496
1497                                 rv3d->localvd= MEM_mallocN(sizeof(RegionView3D), "localview region");
1498                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1499                                 
1500                                 rv3d->ofs[0]= -(min[0]+max[0])/2.0;
1501                                 rv3d->ofs[1]= -(min[1]+max[1])/2.0;
1502                                 rv3d->ofs[2]= -(min[2]+max[2])/2.0;
1503
1504                                 rv3d->dist= size;
1505                                 /* perspective should be a bit farther away to look nice */
1506                                 if(rv3d->persp==RV3D_ORTHO)
1507                                         rv3d->dist*= 0.7;
1508
1509                                 // correction for window aspect ratio
1510                                 if(ar->winy>2 && ar->winx>2) {
1511                                         float asp= (float)ar->winx/(float)ar->winy;
1512                                         if(asp<1.0) asp= 1.0/asp;
1513                                         rv3d->dist*= asp;
1514                                 }
1515                                 
1516                                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP;
1517                                 
1518                                 v3d->cursor[0]= -rv3d->ofs[0];
1519                                 v3d->cursor[1]= -rv3d->ofs[1];
1520                                 v3d->cursor[2]= -rv3d->ofs[2];
1521                         }
1522                 }
1523                 if (v3d->near> 0.1) v3d->near= 0.1;
1524                 
1525                 v3d->lay= locallay;
1526         }
1527         else {
1528                 /* clear flags */ 
1529                 for(base= FIRSTBASE; base; base= base->next) {
1530                         if( base->lay & locallay ) {
1531                                 base->lay-= locallay;
1532                                 if(base->lay==0) base->lay= v3d->layact;
1533                                 if(base->object != scene->obedit) base->flag |= SELECT;
1534                                 base->object->lay= base->lay;
1535                         }
1536                 }               
1537         }
1538
1539 }
1540
1541 static void restore_localviewdata(ScrArea *sa, int free)
1542 {
1543         ARegion *ar;
1544         View3D *v3d= sa->spacedata.first;
1545         
1546         if(v3d->localvd==NULL) return;
1547         
1548         v3d->near= v3d->localvd->near;
1549         v3d->far= v3d->localvd->far;
1550         v3d->lay= v3d->localvd->lay;
1551         v3d->layact= v3d->localvd->layact;
1552         v3d->drawtype= v3d->localvd->drawtype;
1553         v3d->camera= v3d->localvd->camera;
1554         
1555         if(free) {
1556                 MEM_freeN(v3d->localvd);
1557                 v3d->localvd= NULL;
1558         }
1559         
1560         for(ar= sa->regionbase.first; ar; ar= ar->next) {
1561                 if(ar->regiontype == RGN_TYPE_WINDOW) {
1562                         RegionView3D *rv3d= ar->regiondata;
1563                         
1564                         if(rv3d->localvd) {
1565                                 rv3d->dist= rv3d->localvd->dist;
1566                                 VECCOPY(rv3d->ofs, rv3d->localvd->ofs);
1567                                 QUATCOPY(rv3d->viewquat, rv3d->localvd->viewquat);
1568                                 rv3d->view= rv3d->localvd->view;
1569                                 rv3d->persp= rv3d->localvd->persp;
1570                                 rv3d->camzoom= rv3d->localvd->camzoom;
1571
1572                                 if(free) {
1573                                         MEM_freeN(rv3d->localvd);
1574                                         rv3d->localvd= NULL;
1575                                 }
1576                         }
1577                 }
1578         }
1579 }
1580
1581 static void endlocalview(Scene *scene, ScrArea *sa)
1582 {
1583         View3D *v3d= sa->spacedata.first;
1584         struct Base *base;
1585         unsigned int locallay;
1586         
1587         if(v3d->localvd) {
1588                 
1589                 locallay= v3d->lay & 0xFF000000;
1590                 
1591                 restore_localviewdata(sa, 1); // 1 = free
1592
1593                 /* for when in other window the layers have changed */
1594                 if(v3d->scenelock) v3d->lay= scene->lay;
1595                 
1596                 for(base= FIRSTBASE; base; base= base->next) {
1597                         if( base->lay & locallay ) {
1598                                 base->lay-= locallay;
1599                                 if(base->lay==0) base->lay= v3d->layact;
1600                                 if(base->object != scene->obedit) {
1601                                         base->flag |= SELECT;
1602                                         base->object->flag |= SELECT;
1603                                 }
1604                                 base->object->lay= base->lay;
1605                         }
1606                 }
1607         } 
1608 }
1609
1610 static int localview_exec(bContext *C, wmOperator *unused)
1611 {
1612         View3D *v3d= CTX_wm_view3d(C);
1613         
1614         if(v3d->localvd)
1615                 endlocalview(CTX_data_scene(C), CTX_wm_area(C));
1616         else
1617                 initlocalview(CTX_data_scene(C), CTX_wm_area(C));
1618         
1619         ED_area_tag_redraw(CTX_wm_area(C));
1620         
1621         return OPERATOR_FINISHED;
1622 }
1623
1624 void VIEW3D_OT_localview(wmOperatorType *ot)
1625 {
1626         
1627         /* identifiers */
1628         ot->name= "Local View";
1629         ot->description= "Toggle display of selected object(s) separately and centered in view";
1630         ot->idname= "VIEW3D_OT_localview";
1631         
1632         /* api callbacks */
1633         ot->exec= localview_exec;
1634         
1635         ot->poll= ED_operator_view3d_active;
1636 }
1637
1638 #if GAMEBLENDER == 1
1639
1640 static ListBase queue_back;
1641 static void SaveState(bContext *C)
1642 {
1643         wmWindow *win= CTX_wm_window(C);
1644         Object *obact = CTX_data_active_object(C);
1645         
1646         glPushAttrib(GL_ALL_ATTRIB_BITS);
1647
1648         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1649                 GPU_paint_set_mipmap(1);
1650         
1651         queue_back= win->queue;
1652         
1653         win->queue.first= win->queue.last= NULL;
1654         
1655         //XXX waitcursor(1);
1656 }
1657
1658 static void RestoreState(bContext *C)
1659 {
1660         wmWindow *win= CTX_wm_window(C);
1661         Object *obact = CTX_data_active_object(C);
1662         
1663         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1664                 GPU_paint_set_mipmap(0);
1665
1666         //XXX curarea->win_swap = 0;
1667         //XXX curarea->head_swap=0;
1668         //XXX allqueue(REDRAWVIEW3D, 1);
1669         //XXX allqueue(REDRAWBUTSALL, 0);
1670         //XXX reset_slowparents();
1671         //XXX waitcursor(0);
1672         //XXX G.qual= 0;
1673         
1674         win->queue= queue_back;
1675         
1676         GPU_state_init();
1677
1678         glPopAttrib();
1679 }
1680
1681 /* was space_set_commmandline_options in 2.4x */
1682 void game_set_commmandline_options(GameData *gm)
1683 {
1684         SYS_SystemHandle syshandle;
1685         int test;
1686
1687         if ( (syshandle = SYS_GetSystem()) ) {
1688                 /* User defined settings */
1689                 test= (U.gameflags & USER_DISABLE_MIPMAP);
1690                 GPU_set_mipmap(!test);
1691                 SYS_WriteCommandLineInt(syshandle, "nomipmap", test);
1692
1693                 /* File specific settings: */
1694                 /* Only test the first one. These two are switched
1695                  * simultaneously. */
1696                 test= (gm->flag & GAME_SHOW_FRAMERATE);
1697                 SYS_WriteCommandLineInt(syshandle, "show_framerate", test);
1698                 SYS_WriteCommandLineInt(syshandle, "show_profile", test);
1699
1700                 test = (gm->flag & GAME_SHOW_DEBUG_PROPS);
1701                 SYS_WriteCommandLineInt(syshandle, "show_properties", test);
1702
1703                 test= (gm->flag & GAME_SHOW_PHYSICS);
1704                 SYS_WriteCommandLineInt(syshandle, "show_physics", test);
1705
1706                 test= (gm->flag & GAME_ENABLE_ALL_FRAMES);
1707                 SYS_WriteCommandLineInt(syshandle, "fixedtime", test);
1708
1709                 test= (gm->flag & GAME_ENABLE_ANIMATION_RECORD);
1710                 SYS_WriteCommandLineInt(syshandle, "animation_record", test);
1711
1712                 test= (gm->flag & GAME_IGNORE_DEPRECATION_WARNINGS);
1713                 SYS_WriteCommandLineInt(syshandle, "ignore_deprecation_warnings", test);
1714
1715                 test= (gm->matmode == GAME_MAT_MULTITEX);
1716                 SYS_WriteCommandLineInt(syshandle, "blender_material", test);
1717                 test= (gm->matmode == GAME_MAT_GLSL);
1718                 SYS_WriteCommandLineInt(syshandle, "blender_glsl_material", test);
1719                 test= (gm->flag & GAME_DISPLAY_LISTS);
1720                 SYS_WriteCommandLineInt(syshandle, "displaylists", test);
1721
1722
1723         }
1724 }
1725
1726 /* maybe we need this defined somewhere else */
1727 extern void StartKetsjiShell(struct bContext *C, struct ARegion *ar, rcti *cam_frame, int always_use_expand_framing);
1728
1729 #endif // GAMEBLENDER == 1
1730
1731 int game_engine_poll(bContext *C)
1732 {
1733         /* we need a context and area to launch BGE
1734         it's a temporary solution to avoid crash at load time
1735         if we try to auto run the BGE. Ideally we want the
1736         context to be set as soon as we load the file. */
1737
1738         if(CTX_wm_window(C)==NULL) return 0;
1739         if(CTX_wm_screen(C)==NULL) return 0;
1740         if(CTX_wm_area(C)==NULL) return 0;
1741
1742         if(CTX_data_mode_enum(C)!=CTX_MODE_OBJECT)
1743                 return 0;
1744
1745         return 1;
1746 }
1747
1748 int ED_view3d_context_activate(bContext *C)
1749 {
1750         bScreen *sc= CTX_wm_screen(C);
1751         ScrArea *sa= CTX_wm_area(C);
1752         ARegion *ar;
1753         RegionView3D *rv3d;
1754
1755         /* sa can be NULL when called from python */
1756         if(sa==NULL || sa->spacetype != SPACE_VIEW3D)
1757                 for(sa=sc->areabase.first; sa; sa= sa->next)
1758                         if(sa->spacetype==SPACE_VIEW3D)
1759                                 break;
1760
1761         if(!sa)
1762                 return 0;
1763         
1764         for(ar=sa->regionbase.first; ar; ar=ar->next)
1765                 if(ar->regiontype == RGN_TYPE_WINDOW)
1766                         break;
1767         
1768         if(!ar)
1769                 return 0;
1770         
1771         // bad context switch ..
1772         CTX_wm_area_set(C, sa);
1773         CTX_wm_region_set(C, ar);
1774         rv3d= ar->regiondata;
1775
1776         return 1;
1777 }
1778
1779 static int game_engine_exec(bContext *C, wmOperator *op)
1780 {
1781 #if GAMEBLENDER == 1
1782         Scene *startscene = CTX_data_scene(C);
1783         ScrArea *sa, *prevsa= CTX_wm_area(C);
1784         ARegion *ar, *prevar= CTX_wm_region(C);
1785         wmWindow *prevwin= CTX_wm_window(C);
1786         RegionView3D *rv3d;
1787         rcti cam_frame;
1788
1789         // bad context switch ..
1790         if(!ED_view3d_context_activate(C))
1791                 return OPERATOR_CANCELLED;
1792         
1793         rv3d= CTX_wm_region_view3d(C);
1794         sa= CTX_wm_area(C);
1795         ar= CTX_wm_region(C);
1796
1797         view3d_operator_needs_opengl(C);
1798         
1799         game_set_commmandline_options(&startscene->gm);
1800
1801         if(rv3d->persp==RV3D_CAMOB && startscene->gm.framing.type == SCE_GAMEFRAMING_BARS && startscene->gm.stereoflag != STEREO_DOME) { /* Letterbox */
1802                 rctf cam_framef;
1803                 calc_viewborder(startscene, ar, rv3d, CTX_wm_view3d(C), &cam_framef);
1804                 cam_frame.xmin = cam_framef.xmin + ar->winrct.xmin;
1805                 cam_frame.xmax = cam_framef.xmax + ar->winrct.xmin;
1806                 cam_frame.ymin = cam_framef.ymin + ar->winrct.ymin;
1807                 cam_frame.ymax = cam_framef.ymax + ar->winrct.ymin;
1808                 BLI_isect_rcti(&ar->winrct, &cam_frame, &cam_frame);
1809         }
1810         else {
1811                 cam_frame.xmin = ar->winrct.xmin;
1812                 cam_frame.xmax = ar->winrct.xmax;
1813                 cam_frame.ymin = ar->winrct.ymin;
1814                 cam_frame.ymax = ar->winrct.ymax;
1815         }
1816
1817
1818         SaveState(C);
1819
1820         StartKetsjiShell(C, ar, &cam_frame, 1);
1821         
1822         /* restore context, in case it changed in the meantime, for
1823            example by working in another window or closing it */
1824         CTX_wm_region_set(C, prevar);
1825         CTX_wm_window_set(C, prevwin);
1826         CTX_wm_area_set(C, prevsa);
1827         RestoreState(C);
1828
1829         //XXX restore_all_scene_cfra(scene_cfra_store);
1830         set_scene_bg(startscene);
1831         //XXX scene_update_for_newframe(G.scene, G.scene->lay);
1832         
1833         ED_area_tag_redraw(CTX_wm_area(C));
1834
1835         return OPERATOR_FINISHED;
1836 #else
1837         BKE_report(op->reports, RPT_ERROR, "Game engine is disabled in this build.");
1838         return OPERATOR_CANCELLED;
1839 #endif
1840 }
1841
1842 void VIEW3D_OT_game_start(wmOperatorType *ot)
1843 {
1844         
1845         /* identifiers */
1846         ot->name= "Start Game Engine";
1847         ot->description= "Start game engine";
1848         ot->idname= "VIEW3D_OT_game_start";
1849         
1850         /* api callbacks */
1851         ot->exec= game_engine_exec;
1852         
1853         ot->poll= game_engine_poll;
1854 }
1855
1856
1857 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
1858 #define FLY_MODAL_CANCEL                        1
1859 #define FLY_MODAL_CONFIRM                       2
1860 #define FLY_MODAL_ACCELERATE            3
1861 #define FLY_MODAL_DECELERATE            4
1862 #define FLY_MODAL_PAN_ENABLE            5
1863 #define FLY_MODAL_PAN_DISABLE           6
1864 #define FLY_MODAL_DIR_FORWARD           7
1865 #define FLY_MODAL_DIR_BACKWARD          8
1866 #define FLY_MODAL_DIR_LEFT                      9
1867 #define FLY_MODAL_DIR_RIGHT                     10
1868 #define FLY_MODAL_DIR_UP                        11
1869 #define FLY_MODAL_DIR_DOWN                      12
1870 #define FLY_MODAL_AXIS_LOCK_X           13
1871 #define FLY_MODAL_AXIS_LOCK_Z           14
1872 #define FLY_MODAL_PRECISION_ENABLE      15
1873 #define FLY_MODAL_PRECISION_DISABLE     16
1874
1875 /* called in transform_ops.c, on each regeneration of keymaps  */
1876 void fly_modal_keymap(wmKeyConfig *keyconf)
1877 {
1878         static EnumPropertyItem modal_items[] = {
1879         {FLY_MODAL_CANCEL,      "CANCEL", 0, "Cancel", ""},
1880         {FLY_MODAL_CONFIRM,     "CONFIRM", 0, "Confirm", ""},
1881         {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
1882         {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
1883
1884         {FLY_MODAL_PAN_ENABLE,  "PAN_ENABLE", 0, "Pan Enable", ""},
1885         {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
1886
1887         {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
1888         {FLY_MODAL_DIR_BACKWARD,"BACKWARD", 0, "Fly Backward", ""},
1889         {FLY_MODAL_DIR_LEFT,    "LEFT", 0, "Fly Left", ""},
1890         {FLY_MODAL_DIR_RIGHT,   "RIGHT", 0, "Fly Right", ""},
1891         {FLY_MODAL_DIR_UP,              "UP", 0, "Fly Up", ""},
1892         {FLY_MODAL_DIR_DOWN,    "DOWN", 0, "Fly Down", ""},
1893
1894         {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
1895         {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
1896
1897         {FLY_MODAL_PRECISION_ENABLE,    "PRECISION_ENABLE", 0, "Precision Enable", ""},
1898         {FLY_MODAL_PRECISION_DISABLE,   "PRECISION_DISABLE", 0, "Precision Disable", ""},
1899
1900         {0, NULL, 0, NULL, NULL}};
1901
1902         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Fly Modal");
1903
1904         /* this function is called for each spacetype, only needs to add map once */
1905         if(keymap) return;
1906
1907         keymap= WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
1908
1909         /* items for modal map */
1910         WM_modalkeymap_add_item(keymap, ESCKEY,    KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
1911         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
1912
1913         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
1914         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
1915         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
1916         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
1917
1918         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
1919         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
1920         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
1921         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
1922
1923         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
1924         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PAN_DISABLE); /* XXX - Bug in the event system, middle mouse release doesnt work */
1925
1926         /* WASD */
1927         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
1928         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
1929         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
1930         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
1931         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
1932         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
1933
1934         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
1935         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
1936
1937         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
1938         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
1939
1940         /* assign map to operators */
1941         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
1942
1943 }
1944
1945 typedef struct FlyInfo {
1946         /* context stuff */
1947         RegionView3D *rv3d;
1948         View3D *v3d;
1949         ARegion *ar;
1950         Scene *scene;
1951
1952         wmTimer *timer; /* needed for redraws */
1953
1954         short state;
1955         short use_precision;
1956         short redraw;
1957         short mval[2];
1958
1959         /* fly state state */
1960         float speed; /* the speed the view is moving per redraw */
1961         short axis; /* Axis index to move allong by default Z to move allong the view */
1962         short pan_view; /* when true, pan the view instead of rotating */
1963
1964         /* relative view axis locking - xlock, zlock
1965         0; disabled
1966         1; enabled but not checking because mouse hasnt moved outside the margin since locking was checked an not needed
1967            when the mouse moves, locking is set to 2 so checks are done.
1968         2; mouse moved and checking needed, if no view altering is donem its changed back to 1 */
1969         short xlock, zlock;
1970         float xlock_momentum, zlock_momentum; /* nicer dynamics */
1971         float grid; /* world scale 1.0 default */
1972
1973         /* root most parent */
1974         Object *root_parent;
1975
1976         /* backup values */
1977         float dist_backup; /* backup the views distance since we use a zero dist for fly mode */
1978         float ofs_backup[3]; /* backup the views offset incase the user cancels flying in non camera mode */
1979         float rot_backup[4]; /* backup the views quat incase the user cancels flying in non camera mode. (quat for view, eul for camera) */
1980         short persp_backup; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */
1981
1982         void *obtfm; /* backup the objects transform */
1983
1984         /* compare between last state */
1985         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
1986         double time_lastdraw; /* time between draws */
1987
1988         /* use for some lag */
1989         float dvec_prev[3]; /* old for some lag */
1990
1991 } FlyInfo;
1992
1993 /* FlyInfo->state */
1994 #define FLY_RUNNING             0
1995 #define FLY_CANCEL              1
1996 #define FLY_CONFIRM             2
1997
1998 static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event)
1999 {
2000         float upvec[3]; // tmp
2001         float mat[3][3];
2002
2003         fly->rv3d= CTX_wm_region_view3d(C);
2004         fly->v3d = CTX_wm_view3d(C);
2005         fly->ar = CTX_wm_region(C);
2006         fly->scene= CTX_data_scene(C);
2007
2008         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
2009                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
2010                 return FALSE;
2011         }
2012
2013         if(fly->v3d->ob_centre) {
2014                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
2015                 return FALSE;
2016         }
2017
2018         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
2019                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
2020                 return FALSE;
2021         }
2022
2023         fly->state= FLY_RUNNING;
2024         fly->speed= 0.0f;
2025         fly->axis= 2;
2026         fly->pan_view= FALSE;
2027         fly->xlock= FALSE;
2028         fly->zlock= FALSE;
2029         fly->xlock_momentum=0.0f;
2030         fly->zlock_momentum=0.0f;
2031         fly->grid= 1.0f;
2032         fly->use_precision= 0;
2033
2034         fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
2035
2036         fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
2037
2038         fly->mval[0] = event->x - fly->ar->winrct.xmin;
2039         fly->mval[1] = event->y - fly->ar->winrct.ymin;
2040
2041
2042         fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
2043
2044         fly->rv3d->rflag |= RV3D_FLYMODE|RV3D_NAVIGATING; /* so we draw the corner margins */
2045
2046         /* detect weather to start with Z locking */
2047         upvec[0]=1.0f; upvec[1]=0.0f; upvec[2]=0.0f;
2048         copy_m3_m4(mat, fly->rv3d->viewinv);
2049         mul_m3_v3(mat, upvec);
2050         if (fabs(upvec[2]) < 0.1)
2051                 fly->zlock = 1;
2052         upvec[0]=0; upvec[1]=0; upvec[2]=0;
2053
2054         fly->persp_backup= fly->rv3d->persp;
2055         fly->dist_backup= fly->rv3d->dist;
2056         if (fly->rv3d->persp==RV3D_CAMOB) {
2057                 Object *ob_back;
2058                 if((fly->root_parent=fly->v3d->camera->parent)) {
2059                         while(fly->root_parent->parent)
2060                                 fly->root_parent= fly->root_parent->parent;
2061                         ob_back= fly->root_parent;
2062                 }
2063                 else {
2064                         ob_back= fly->v3d->camera;
2065                 }
2066
2067                 /* store the original camera loc and rot */
2068                 /* TODO. axis angle etc */
2069
2070                 fly->obtfm= object_tfm_backup(ob_back);
2071
2072                 where_is_object(fly->scene, fly->v3d->camera);
2073                 negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
2074
2075                 fly->rv3d->dist=0.0;
2076         } else {
2077                 /* perspective or ortho */
2078                 if (fly->rv3d->persp==RV3D_ORTHO)
2079                         fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
2080                 QUATCOPY(fly->rot_backup, fly->rv3d->viewquat);
2081                 copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
2082                 fly->rv3d->dist= 0.0f;
2083
2084                 upvec[2]= fly->dist_backup; /*x and y are 0*/
2085                 mul_m3_v3(mat, upvec);
2086                 sub_v3_v3(fly->rv3d->ofs, upvec);
2087                 /*Done with correcting for the dist*/
2088         }
2089
2090         
2091         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
2092         WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
2093         
2094         return 1;
2095 }
2096
2097 static int flyEnd(bContext *C, FlyInfo *fly)
2098 {
2099         RegionView3D *rv3d= fly->rv3d;
2100         View3D *v3d = fly->v3d;
2101
2102         float upvec[3];
2103
2104         if(fly->state == FLY_RUNNING)
2105                 return OPERATOR_RUNNING_MODAL;
2106
2107         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
2108
2109         rv3d->dist= fly->dist_backup;
2110
2111         if (fly->state == FLY_CANCEL) {
2112         /* Revert to original view? */
2113                 if (fly->persp_backup==RV3D_CAMOB) { /* a camera view */
2114                         Object *ob_back;
2115                         if(fly->root_parent)ob_back= fly->root_parent;
2116                         else                            ob_back= fly->v3d->camera;
2117
2118                         /* store the original camera loc and rot */
2119                         object_tfm_restore(ob_back, fly->obtfm);
2120
2121                         DAG_id_flush_update(&ob_back->id, OB_RECALC_OB);
2122                 } else {
2123                         /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
2124                         QUATCOPY(rv3d->viewquat, fly->rot_backup);
2125                         VECCOPY(rv3d->ofs, fly->ofs_backup);
2126                         rv3d->persp= fly->persp_backup;
2127                 }
2128         }
2129         else if (fly->persp_backup==RV3D_CAMOB) {       /* camera */
2130                 float mat3[3][3];
2131                 if(fly->root_parent) {
2132                         DAG_id_flush_update(&fly->root_parent->id, OB_RECALC_OB);
2133                 }
2134                 else {
2135                         copy_m3_m4(mat3, v3d->camera->obmat);
2136                         object_mat3_to_rot(v3d->camera, mat3, TRUE);
2137                         DAG_id_flush_update(&v3d->camera->id, OB_RECALC_OB);
2138                 }
2139
2140 #if 0 //XXX2.5
2141                 if (IS_AUTOKEY_MODE(NORMAL)) {
2142                         allqueue(REDRAWIPO, 0);
2143                         allspace(REMAKEIPO, 0);
2144                         allqueue(REDRAWNLA, 0);
2145                         allqueue(REDRAWTIME, 0);
2146                 }
2147 #endif
2148         }
2149         else { /* not camera */
2150                 /* Apply the fly mode view */
2151                 /*restore the dist*/
2152                 float mat[3][3];
2153                 upvec[0]= upvec[1]= 0;
2154                 upvec[2]= fly->dist_backup; /*x and y are 0*/
2155                 copy_m3_m4(mat, rv3d->viewinv);
2156                 mul_m3_v3(mat, upvec);
2157                 add_v3_v3(rv3d->ofs, upvec);
2158                 /*Done with correcting for the dist */
2159         }
2160
2161         rv3d->rflag &= ~(RV3D_FLYMODE|RV3D_NAVIGATING);
2162 //XXX2.5        BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
2163
2164         if(fly->obtfm)
2165                 MEM_freeN(fly->obtfm);
2166
2167         if(fly->state == FLY_CONFIRM) {
2168                 MEM_freeN(fly);
2169                 return OPERATOR_FINISHED;
2170         }
2171
2172         MEM_freeN(fly);
2173         return OPERATOR_CANCELLED;
2174 }
2175
2176 static void flyEvent(FlyInfo *fly, wmEvent *event)
2177 {
2178         if (event->type == TIMER && event->customdata == fly->timer) {
2179                 fly->redraw = 1;
2180         }
2181         else if (event->type == MOUSEMOVE) {
2182                 fly->mval[0] = event->x - fly->ar->winrct.xmin;
2183                 fly->mval[1] = event->y - fly->ar->winrct.ymin;
2184         } /* handle modal keymap first */
2185         else if (event->type == EVT_MODAL_MAP) {
2186                 switch (event->val) {
2187                         case FLY_MODAL_CANCEL:
2188                                 fly->state = FLY_CANCEL;
2189                                 break;
2190                         case FLY_MODAL_CONFIRM:
2191                                 fly->state = FLY_CONFIRM;
2192                                 break;
2193
2194                         case FLY_MODAL_ACCELERATE:
2195                         {
2196                                 double time_currwheel;
2197                                 float time_wheel;
2198
2199                                 time_currwheel= PIL_check_seconds_timer();
2200                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
2201                                 fly->time_lastwheel = time_currwheel;
2202                                 /*printf("Wheel %f\n", time_wheel);*/
2203                                 /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
2204                                 time_wheel = 1+ (10 - (20*MIN2(time_wheel, 0.5))); /* 0-0.5 -> 0-5.0 */
2205
2206                                 if (fly->speed<0.0f) fly->speed= 0.0f;
2207                                 else {
2208                                         if (event->shift)
2209                                                 fly->speed+= fly->grid*time_wheel*0.1;
2210                                         else
2211                                                 fly->speed+= fly->grid*time_wheel;
2212                                 }
2213                                 break;
2214                         }
2215                         case FLY_MODAL_DECELERATE:
2216                         {
2217                                 double time_currwheel;
2218                                 float time_wheel;
2219
2220                                 time_currwheel= PIL_check_seconds_timer();
2221                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
2222                                 fly->time_lastwheel = time_currwheel;
2223                                 time_wheel = 1+ (10 - (20*MIN2(time_wheel, 0.5))); /* 0-0.5 -> 0-5.0 */
2224
2225                                 if (fly->speed>0) fly->speed=0;
2226                                 else {
2227                                         if (event->shift)
2228                                                 fly->speed-= fly->grid*time_wheel*0.1;
2229                                         else
2230                                                 fly->speed-= fly->grid*time_wheel;
2231                                 }
2232                                 break;
2233                         }
2234                         case FLY_MODAL_PAN_ENABLE:
2235                                 fly->pan_view= TRUE;
2236                                 break;
2237                         case FLY_MODAL_PAN_DISABLE:
2238 //XXX2.5                warp_pointer(cent_orig[0], cent_orig[1]);
2239                                 fly->pan_view= FALSE;
2240                                 break;
2241
2242                                 /* impliment WASD keys */
2243                         case FLY_MODAL_DIR_FORWARD:
2244                                 if (fly->speed < 0.0f) fly->speed= -fly->speed; /* flip speed rather then stopping, game like motion */
2245                                 else fly->speed += fly->grid; /* increse like mousewheel if were alredy moving in that difection*/
2246                                 fly->axis= 2;
2247                                 break;
2248                         case FLY_MODAL_DIR_BACKWARD:
2249                                 if (fly->speed>0) fly->speed= -fly->speed;
2250                                 else fly->speed -= fly->grid;
2251                                 fly->axis= 2;
2252                                 break;
2253                         case FLY_MODAL_DIR_LEFT:
2254                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
2255                                 fly->axis= 0;
2256                                 break;
2257                         case FLY_MODAL_DIR_RIGHT:
2258                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
2259                                 fly->axis= 0;
2260                                 break;
2261
2262                         case FLY_MODAL_DIR_UP:
2263                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
2264                                 fly->axis= 1;
2265                                 break;
2266
2267                         case FLY_MODAL_DIR_DOWN:
2268                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
2269                                 fly->axis= 1;
2270                                 break;
2271
2272                         case FLY_MODAL_AXIS_LOCK_X:
2273                                 if (fly->xlock) fly->xlock=0;
2274                                 else {
2275                                         fly->xlock = 2;
2276                                         fly->xlock_momentum = 0.0;
2277                                 }
2278                                 break;
2279                         case FLY_MODAL_AXIS_LOCK_Z:
2280                                 if (fly->zlock) fly->zlock=0;
2281                                 else {
2282                                         fly->zlock = 2;
2283                                         fly->zlock_momentum = 0.0;
2284                                 }
2285                                 break;
2286
2287                         case FLY_MODAL_PRECISION_ENABLE:
2288                                 fly->use_precision= TRUE;
2289                                 break;
2290                         case FLY_MODAL_PRECISION_DISABLE:
2291                                 fly->use_precision= FALSE;
2292                                 break;
2293
2294                 }
2295         }
2296 }
2297
2298 static int flyApply(bContext *C, FlyInfo *fly)
2299 {
2300
2301 #define FLY_ROTATE_FAC 2.5f /* more is faster */
2302 #define FLY_ZUP_CORRECT_FAC 0.1f /* ammount to correct per step */
2303 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
2304
2305         /*
2306         fly mode - Shift+F
2307         a fly loop where the user can move move the view as if they are flying
2308         */
2309         RegionView3D *rv3d= fly->rv3d;
2310         View3D *v3d = fly->v3d;
2311         ARegion *ar = fly->ar;
2312         Scene *scene= fly->scene;
2313
2314         float prev_view_mat[4][4];
2315
2316         float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
2317         dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
2318
2319         /* Camera Uprighting variables */
2320         upvec[3]={0,0,0}, /* stores the view's up vector */
2321
2322         moffset[2], /* mouse offset from the views center */
2323         tmp_quat[4]; /* used for rotating the view */
2324
2325         int cent_orig[2], /* view center */
2326 //XXX- can avoid using //       cent[2], /* view center modified */
2327         xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
2328         unsigned char
2329         apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
2330
2331         if(fly->root_parent)
2332                 view3d_persp_mat4(rv3d, prev_view_mat);
2333
2334         /* the dist defines a vector that is infront of the offset
2335         to rotate the view about.
2336         this is no good for fly mode because we
2337         want to rotate about the viewers center.
2338         but to correct the dist removal we must
2339         alter offset so the view doesn't jump. */
2340
2341         xmargin= ar->winx/20.0f;
2342         ymargin= ar->winy/20.0f;
2343
2344         cent_orig[0]= ar->winrct.xmin + ar->winx/2;
2345         cent_orig[1]= ar->winrct.ymin + ar->winy/2;
2346
2347         {
2348
2349                 /* mouse offset from the center */
2350                 moffset[0]= fly->mval[0]- ar->winx/2;
2351                 moffset[1]= fly->mval[1]- ar->winy/2;
2352
2353                 /* enforce a view margin */
2354                 if (moffset[0]>xmargin)                 moffset[0]-=xmargin;
2355                 else if (moffset[0] < -xmargin) moffset[0]+=xmargin;
2356                 else                                                    moffset[0]=0;
2357
2358                 if (moffset[1]>ymargin)                 moffset[1]-=ymargin;
2359                 else if (moffset[1] < -ymargin) moffset[1]+=ymargin;
2360                 else                                                    moffset[1]=0;
2361
2362
2363                 /* scale the mouse movement by this value - scales mouse movement to the view size
2364                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
2365                  *
2366                  * the mouse moves isnt linear */
2367
2368                 if(moffset[0]) {
2369                         moffset[0] /= ar->winx - (xmargin*2);
2370                         moffset[0] *= fabs(moffset[0]);
2371                 }
2372
2373                 if(moffset[1]) {
2374                         moffset[1] /= ar->winy - (ymargin*2);
2375                         moffset[1] *= fabs(moffset[1]);
2376                 }
2377
2378                 /* Should we redraw? */
2379                 if(fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
2380                         float dvec_tmp[3];
2381                         double time_current, time_redraw; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
2382                         float time_redraw_clamped;
2383
2384                         time_current= PIL_check_seconds_timer();
2385                         time_redraw= (float)(time_current - fly->time_lastdraw);
2386                         time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
2387                         fly->time_lastdraw= time_current;
2388                         /*fprintf(stderr, "%f\n", time_redraw);*/ /* 0.002 is a small redraw 0.02 is larger */
2389
2390                         /* Scale the time to use shift to scale the speed down- just like
2391                         shift slows many other areas of blender down */
2392                         if (fly->use_precision)
2393                                 fly->speed= fly->speed * (1.0f-time_redraw_clamped);
2394
2395                         copy_m3_m4(mat, rv3d->viewinv);
2396
2397                         if (fly->pan_view==TRUE) {
2398                                 /* pan only */
2399                                 dvec_tmp[0]= -moffset[0];
2400                                 dvec_tmp[1]= -moffset[1];
2401                                 dvec_tmp[2]= 0;
2402
2403                                 if (fly->use_precision) {
2404                                         dvec_tmp[0] *= 0.1;
2405                                         dvec_tmp[1] *= 0.1;
2406                                 }
2407
2408                                 mul_m3_v3(mat, dvec_tmp);
2409                                 mul_v3_fl(dvec_tmp, time_redraw*200.0 * fly->grid);
2410
2411                         } else {
2412                                 float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
2413
2414                                 /* rotate about the X axis- look up/down */
2415                                 if (moffset[1]) {
2416                                         upvec[0]=1;
2417                                         upvec[1]=0;
2418                                         upvec[2]=0;
2419                                         mul_m3_v3(mat, upvec);
2420                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); /* Rotate about the relative up vec */
2421                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2422
2423                                         if (fly->xlock) fly->xlock = 2; /*check for rotation*/
2424                                         if (fly->zlock) fly->zlock = 2;
2425                                         fly->xlock_momentum= 0.0f;
2426                                 }
2427
2428                                 /* rotate about the Y axis- look left/right */
2429                                 if (moffset[0]) {
2430
2431                                         /* if we're upside down invert the moffset */
2432                                         upvec[0]=0;
2433                                         upvec[1]=1;
2434                                         upvec[2]=0;
2435                                         mul_m3_v3(mat, upvec);
2436
2437                                         if(upvec[2] < 0.0f)
2438                                                 moffset[0]= -moffset[0];
2439
2440                                         /* make the lock vectors */
2441                                         if (fly->zlock) {
2442                                                 upvec[0]=0;
2443                                                 upvec[1]=0;
2444                                                 upvec[2]=1;
2445                                         } else {
2446                                                 upvec[0]=0;
2447                                                 upvec[1]=1;
2448                                                 upvec[2]=0;
2449                                                 mul_m3_v3(mat, upvec);
2450                                         }
2451
2452                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
2453                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2454
2455                                         if (fly->xlock) fly->xlock = 2;/*check for rotation*/
2456                                         if (fly->zlock) fly->zlock = 2;
2457                                 }
2458
2459                                 if (fly->zlock==2) {
2460                                         upvec[0]=1;
2461                                         upvec[1]=0;
2462                                         upvec[2]=0;
2463                                         mul_m3_v3(mat, upvec);
2464
2465                                         /*make sure we have some z rolling*/
2466                                         if (fabs(upvec[2]) > 0.00001f) {
2467                                                 roll= upvec[2]*5;
2468                                                 upvec[0]=0; /*rotate the view about this axis*/
2469                                                 upvec[1]=0;
2470                                                 upvec[2]=1;
2471
2472                                                 mul_m3_v3(mat, upvec);
2473                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
2474                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2475
2476                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
2477                                         } else {
2478                                                 fly->zlock=1; /* dont check until the view rotates again */
2479                                                 fly->zlock_momentum= 0.0f;
2480                                         }
2481                                 }
2482
2483                                 if (fly->xlock==2 && moffset[1]==0) { /*only apply xcorrect when mouse isnt applying x rot*/
2484                                         upvec[0]=0;
2485                                         upvec[1]=0;
2486                                         upvec[2]=1;
2487                                         mul_m3_v3(mat, upvec);
2488                                         /*make sure we have some z rolling*/
2489                                         if (fabs(upvec[2]) > 0.00001) {
2490                                                 roll= upvec[2] * -5;
2491
2492                                                 upvec[0]= 1.0f; /*rotate the view about this axis*/
2493                                                 upvec[1]= 0.0f;
2494                                                 upvec[2]= 0.0f;
2495
2496                                                 mul_m3_v3(mat, upvec);
2497
2498                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->xlock_momentum*0.1f); /* Rotate about the relative up vec */
2499                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2500
2501                                                 fly->xlock_momentum += 0.05f;
2502                                         } else {
2503                                                 fly->xlock=1; /* see above */
2504                                                 fly->xlock_momentum= 0.0f;
2505                                         }
2506                                 }
2507
2508
2509                                 if (apply_rotation) {
2510                                         /* Normal operation */
2511                                         /* define dvec, view direction vector */
2512                                         dvec_tmp[0]= dvec_tmp[1]= dvec_tmp[2]= 0.0f;
2513                                         /* move along the current axis */
2514                                         dvec_tmp[fly->axis]= 1.0f;
2515
2516                                         mul_m3_v3(mat, dvec_tmp);
2517
2518                                         mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
2519                                 }
2520                         }
2521
2522                         /* impose a directional lag */
2523                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f/(1.0f+(time_redraw*5.0f))));
2524
2525                         if (rv3d->persp==RV3D_CAMOB) {
2526                                 Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
2527                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
2528                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
2529                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
2530                         }
2531
2532                         add_v3_v3(rv3d->ofs, dvec);
2533
2534                         /* todo, dynamic keys */
2535 #if 0
2536                         if (fly->zlock && fly->xlock)
2537                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2538                         else if (fly->zlock)
2539                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2540                         else if (fly->xlock)
2541                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2542                         else
2543                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2544 #endif
2545
2546                         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
2547                         if (rv3d->persp==RV3D_CAMOB) {
2548                                 ID *id_key;
2549                                 /* transform the parent or the camera? */
2550                                 if(fly->root_parent) {
2551                                         Object *ob_update;
2552                     
2553                                         float view_mat[4][4];
2554                                         float prev_view_imat[4][4];
2555                                         float diff_mat[4][4];
2556                                         float parent_mat[4][4];
2557
2558                                         invert_m4_m4(prev_view_imat, prev_view_mat);
2559                                         view3d_persp_mat4(rv3d, view_mat);
2560                                         mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
2561                                         mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
2562                                         object_apply_mat4(fly->root_parent, parent_mat);
2563
2564                                         // where_is_object(scene, fly->root_parent);
2565
2566                                         ob_update= v3d->camera->parent;
2567                                         while(ob_update) {
2568                                                 DAG_id_flush_update(&ob_update->id, OB_RECALC_OB);
2569                                                 ob_update= ob_update->parent;
2570                                         }
2571
2572                                         copy_m4_m4(prev_view_mat, view_mat);
2573
2574                                         id_key= &fly->root_parent->id;
2575
2576                                 }
2577                                 else {
2578                                         float view_mat[4][4];
2579                                         view3d_persp_mat4(rv3d, view_mat);
2580                                         object_apply_mat4(v3d->camera, view_mat);
2581                                         id_key= &v3d->camera->id;
2582                                 }
2583
2584                                 /* record the motion */
2585                                 if (autokeyframe_cfra_can_key(scene, id_key)) {
2586                                         ListBase dsources = {NULL, NULL};
2587                                         
2588                                         /* add datasource override for the camera object */
2589                                         ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
2590                                         
2591                                         /* insert keyframes 
2592                                          *      1) on the first frame
2593                                          *      2) on each subsequent frame
2594                                          *              TODO: need to check in future that frame changed before doing this 
2595                                          */
2596                                         if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
2597                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
2598                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
2599                                         }
2600                                         if (fly->speed) {
2601                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
2602                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
2603                                         }
2604                                         
2605                                         /* free temp data */
2606                                         BLI_freelistN(&dsources);
2607                                 }
2608                         }
2609                 } else
2610                         /*were not redrawing but we need to update the time else the view will jump */
2611                         fly->time_lastdraw= PIL_check_seconds_timer();
2612                 /* end drawing */
2613                 VECCOPY(fly->dvec_prev, dvec);
2614         }
2615
2616 /* moved to flyEnd() */
2617
2618         return OPERATOR_FINISHED;
2619 }
2620
2621
2622
2623 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
2624 {
2625         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2626         FlyInfo *fly;
2627
2628         if(rv3d->viewlock)
2629                 return OPERATOR_CANCELLED;
2630
2631         fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
2632
2633         op->customdata= fly;
2634
2635         if(initFlyInfo(C, fly, op, event)==FALSE) {
2636                 MEM_freeN(op->customdata);
2637                 return OPERATOR_CANCELLED;
2638         }
2639
2640         flyEvent(fly, event);
2641
2642         WM_event_add_modal_handler(C, op);
2643
2644         return OPERATOR_RUNNING_MODAL;
2645 }
2646
2647 static int fly_cancel(bContext *C, wmOperator *op)
2648 {
2649         FlyInfo *fly = op->customdata;
2650
2651         fly->state = FLY_CANCEL;
2652         flyEnd(C, fly);
2653         op->customdata= NULL;
2654
2655         return OPERATOR_CANCELLED;
2656 }
2657
2658 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
2659 {
2660         int exit_code;
2661
2662         FlyInfo *fly = op->customdata;
2663
2664         fly->redraw= 0;
2665
2666         flyEvent(fly, event);
2667
2668         if(event->type==TIMER && event->customdata == fly->timer)
2669                 flyApply(C, fly);
2670
2671         if(fly->redraw) {
2672                 ED_region_tag_redraw(CTX_wm_region(C));
2673         }
2674
2675         exit_code = flyEnd(C, fly);
2676
2677         if(exit_code!=OPERATOR_RUNNING_MODAL)
2678                 ED_region_tag_redraw(CTX_wm_region(C));
2679
2680         return exit_code;
2681 }
2682
2683 void VIEW3D_OT_fly(wmOperatorType *ot)
2684 {
2685
2686         /* identifiers */
2687         ot->name= "Fly Navigation";
2688         ot->description= "Interactively fly around the scene";
2689         ot->idname= "VIEW3D_OT_fly";
2690
2691         /* api callbacks */
2692         ot->invoke= fly_invoke;
2693         ot->cancel= fly_cancel;
2694         ot->modal= fly_modal;
2695         ot->poll= ED_operator_view3d_active;
2696
2697         /* flags */
2698         ot->flag= OPTYPE_BLOCKING;
2699
2700 }
2701
2702 /* ************************************** */
2703
2704 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
2705 {
2706         float alignaxis[3] = {0.0, 0.0, 0.0};
2707         float norm[3], axis[3], angle, new_quat[4];
2708         
2709         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
2710         else alignaxis[-axisidx-1]= -1.0;
2711
2712         normalize_v3_v3(norm, vec);
2713
2714         angle= (float)acos(dot_v3v3(alignaxis, norm));
2715         cross_v3_v3v3(axis, alignaxis, norm);
2716         axis_angle_to_quat( new_quat,axis, -angle);
2717         
2718         rv3d->view= 0;
2719         
2720         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
2721                 /* switch out of camera view */
2722                 float orig_ofs[3];
2723                 float orig_dist= rv3d->dist;
2724                 float orig_lens= v3d->lens;
2725                 
2726                 VECCOPY(orig_ofs, rv3d->ofs);
2727                 rv3d->persp= RV3D_PERSP;
2728                 rv3d->dist= 0.0;
2729                 view3d_settings_from_ob(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
2730                 smooth_view(NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
2731         } else {
2732                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP; /* switch out of camera mode */
2733                 smooth_view(NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
2734         }
2735 }
2736