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