bugfix [#24122] Shift-C doesn't work in "Camera View"
[blender.git] / source / blender / editors / space_view3d / view3d_view.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_anim_types.h"
35 #include "DNA_camera_types.h"
36 #include "DNA_lamp_types.h"
37 #include "DNA_scene_types.h"
38 #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 *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(View3D *v3d, 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 *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(v3d, 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 *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)
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
1671         glPopAttrib();
1672 }
1673
1674 /* was space_set_commmandline_options in 2.4x */
1675 void game_set_commmandline_options(GameData *gm)
1676 {
1677         SYS_SystemHandle syshandle;
1678         int test;
1679
1680         if ( (syshandle = SYS_GetSystem()) ) {
1681                 /* User defined settings */
1682                 test= (U.gameflags & USER_DISABLE_MIPMAP);
1683                 GPU_set_mipmap(!test);
1684                 SYS_WriteCommandLineInt(syshandle, "nomipmap", test);
1685
1686                 /* File specific settings: */
1687                 /* Only test the first one. These two are switched
1688                  * simultaneously. */
1689                 test= (gm->flag & GAME_SHOW_FRAMERATE);
1690                 SYS_WriteCommandLineInt(syshandle, "show_framerate", test);
1691                 SYS_WriteCommandLineInt(syshandle, "show_profile", test);
1692
1693                 test = (gm->flag & GAME_SHOW_DEBUG_PROPS);
1694                 SYS_WriteCommandLineInt(syshandle, "show_properties", test);
1695
1696                 test= (gm->flag & GAME_SHOW_PHYSICS);
1697                 SYS_WriteCommandLineInt(syshandle, "show_physics", test);
1698
1699                 test= (gm->flag & GAME_ENABLE_ALL_FRAMES);
1700                 SYS_WriteCommandLineInt(syshandle, "fixedtime", test);
1701
1702                 test= (gm->flag & GAME_ENABLE_ANIMATION_RECORD);
1703                 SYS_WriteCommandLineInt(syshandle, "animation_record", test);
1704
1705                 test= (gm->flag & GAME_IGNORE_DEPRECATION_WARNINGS);
1706                 SYS_WriteCommandLineInt(syshandle, "ignore_deprecation_warnings", test);
1707
1708                 test= (gm->matmode == GAME_MAT_MULTITEX);
1709                 SYS_WriteCommandLineInt(syshandle, "blender_material", test);
1710                 test= (gm->matmode == GAME_MAT_GLSL);
1711                 SYS_WriteCommandLineInt(syshandle, "blender_glsl_material", test);
1712                 test= (gm->flag & GAME_DISPLAY_LISTS);
1713                 SYS_WriteCommandLineInt(syshandle, "displaylists", test);
1714
1715
1716         }
1717 }
1718
1719 /* maybe we need this defined somewhere else */
1720 extern void StartKetsjiShell(struct bContext *C, struct ARegion *ar, rcti *cam_frame, int always_use_expand_framing);
1721
1722 #endif // GAMEBLENDER == 1
1723
1724 int game_engine_poll(bContext *C)
1725 {
1726         /* we need a context and area to launch BGE
1727         it's a temporary solution to avoid crash at load time
1728         if we try to auto run the BGE. Ideally we want the
1729         context to be set as soon as we load the file. */
1730
1731         if(CTX_wm_window(C)==NULL) return 0;
1732         if(CTX_wm_screen(C)==NULL) return 0;
1733         if(CTX_wm_area(C)==NULL) return 0;
1734
1735         if(CTX_data_mode_enum(C)!=CTX_MODE_OBJECT)
1736                 return 0;
1737
1738         return 1;
1739 }
1740
1741 int ED_view3d_context_activate(bContext *C)
1742 {
1743         bScreen *sc= CTX_wm_screen(C);
1744         ScrArea *sa= CTX_wm_area(C);
1745         ARegion *ar;
1746         RegionView3D *rv3d;
1747
1748         /* sa can be NULL when called from python */
1749         if(sa==NULL || sa->spacetype != SPACE_VIEW3D)
1750                 for(sa=sc->areabase.first; sa; sa= sa->next)
1751                         if(sa->spacetype==SPACE_VIEW3D)
1752                                 break;
1753
1754         if(!sa)
1755                 return 0;
1756         
1757         for(ar=sa->regionbase.first; ar; ar=ar->next)
1758                 if(ar->regiontype == RGN_TYPE_WINDOW)
1759                         break;
1760         
1761         if(!ar)
1762                 return 0;
1763         
1764         // bad context switch ..
1765         CTX_wm_area_set(C, sa);
1766         CTX_wm_region_set(C, ar);
1767         rv3d= ar->regiondata;
1768
1769         return 1;
1770 }
1771
1772 static int game_engine_exec(bContext *C, wmOperator *op)
1773 {
1774 #if GAMEBLENDER == 1
1775         Scene *startscene = CTX_data_scene(C);
1776         ScrArea *sa, *prevsa= CTX_wm_area(C);
1777         ARegion *ar, *prevar= CTX_wm_region(C);
1778         wmWindow *prevwin= CTX_wm_window(C);
1779         RegionView3D *rv3d;
1780         rcti cam_frame;
1781
1782         // bad context switch ..
1783         if(!ED_view3d_context_activate(C))
1784                 return OPERATOR_CANCELLED;
1785         
1786         rv3d= CTX_wm_region_view3d(C);
1787         sa= CTX_wm_area(C);
1788         ar= CTX_wm_region(C);
1789
1790         view3d_operator_needs_opengl(C);
1791         
1792         game_set_commmandline_options(&startscene->gm);
1793
1794         if(rv3d->persp==RV3D_CAMOB && startscene->gm.framing.type == SCE_GAMEFRAMING_BARS && startscene->gm.stereoflag != STEREO_DOME) { /* Letterbox */
1795                 rctf cam_framef;
1796                 view3d_calc_camera_border(startscene, ar, rv3d, CTX_wm_view3d(C), &cam_framef);
1797                 cam_frame.xmin = cam_framef.xmin + ar->winrct.xmin;
1798                 cam_frame.xmax = cam_framef.xmax + ar->winrct.xmin;
1799                 cam_frame.ymin = cam_framef.ymin + ar->winrct.ymin;
1800                 cam_frame.ymax = cam_framef.ymax + ar->winrct.ymin;
1801                 BLI_isect_rcti(&ar->winrct, &cam_frame, &cam_frame);
1802         }
1803         else {
1804                 cam_frame.xmin = ar->winrct.xmin;
1805                 cam_frame.xmax = ar->winrct.xmax;
1806                 cam_frame.ymin = ar->winrct.ymin;
1807                 cam_frame.ymax = ar->winrct.ymax;
1808         }
1809
1810
1811         SaveState(C);
1812
1813         StartKetsjiShell(C, ar, &cam_frame, 1);
1814         
1815         /* restore context, in case it changed in the meantime, for
1816            example by working in another window or closing it */
1817         CTX_wm_region_set(C, prevar);
1818         CTX_wm_window_set(C, prevwin);
1819         CTX_wm_area_set(C, prevsa);
1820         RestoreState(C);
1821
1822         //XXX restore_all_scene_cfra(scene_cfra_store);
1823         set_scene_bg(CTX_data_main(C), startscene);
1824         //XXX scene_update_for_newframe(bmain, scene, scene->lay);
1825         
1826         ED_area_tag_redraw(CTX_wm_area(C));
1827
1828         return OPERATOR_FINISHED;
1829 #else
1830         BKE_report(op->reports, RPT_ERROR, "Game engine is disabled in this build.");
1831         return OPERATOR_CANCELLED;
1832 #endif
1833 }
1834
1835 void VIEW3D_OT_game_start(wmOperatorType *ot)
1836 {
1837         
1838         /* identifiers */
1839         ot->name= "Start Game Engine";
1840         ot->description= "Start game engine";
1841         ot->idname= "VIEW3D_OT_game_start";
1842         
1843         /* api callbacks */
1844         ot->exec= game_engine_exec;
1845         
1846         ot->poll= game_engine_poll;
1847 }
1848
1849
1850 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
1851 #define FLY_MODAL_CANCEL                        1
1852 #define FLY_MODAL_CONFIRM                       2
1853 #define FLY_MODAL_ACCELERATE            3
1854 #define FLY_MODAL_DECELERATE            4
1855 #define FLY_MODAL_PAN_ENABLE            5
1856 #define FLY_MODAL_PAN_DISABLE           6
1857 #define FLY_MODAL_DIR_FORWARD           7
1858 #define FLY_MODAL_DIR_BACKWARD          8
1859 #define FLY_MODAL_DIR_LEFT                      9
1860 #define FLY_MODAL_DIR_RIGHT                     10
1861 #define FLY_MODAL_DIR_UP                        11
1862 #define FLY_MODAL_DIR_DOWN                      12
1863 #define FLY_MODAL_AXIS_LOCK_X           13
1864 #define FLY_MODAL_AXIS_LOCK_Z           14
1865 #define FLY_MODAL_PRECISION_ENABLE      15
1866 #define FLY_MODAL_PRECISION_DISABLE     16
1867
1868 /* called in transform_ops.c, on each regeneration of keymaps  */
1869 void fly_modal_keymap(wmKeyConfig *keyconf)
1870 {
1871         static EnumPropertyItem modal_items[] = {
1872         {FLY_MODAL_CANCEL,      "CANCEL", 0, "Cancel", ""},
1873         {FLY_MODAL_CONFIRM,     "CONFIRM", 0, "Confirm", ""},
1874         {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
1875         {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
1876
1877         {FLY_MODAL_PAN_ENABLE,  "PAN_ENABLE", 0, "Pan Enable", ""},
1878         {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
1879
1880         {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
1881         {FLY_MODAL_DIR_BACKWARD,"BACKWARD", 0, "Fly Backward", ""},
1882         {FLY_MODAL_DIR_LEFT,    "LEFT", 0, "Fly Left", ""},
1883         {FLY_MODAL_DIR_RIGHT,   "RIGHT", 0, "Fly Right", ""},
1884         {FLY_MODAL_DIR_UP,              "UP", 0, "Fly Up", ""},
1885         {FLY_MODAL_DIR_DOWN,    "DOWN", 0, "Fly Down", ""},
1886
1887         {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
1888         {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
1889
1890         {FLY_MODAL_PRECISION_ENABLE,    "PRECISION_ENABLE", 0, "Precision Enable", ""},
1891         {FLY_MODAL_PRECISION_DISABLE,   "PRECISION_DISABLE", 0, "Precision Disable", ""},
1892
1893         {0, NULL, 0, NULL, NULL}};
1894
1895         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Fly Modal");
1896
1897         /* this function is called for each spacetype, only needs to add map once */
1898         if(keymap) return;
1899
1900         keymap= WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
1901
1902         /* items for modal map */
1903         WM_modalkeymap_add_item(keymap, ESCKEY,    KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
1904         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
1905
1906         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
1907         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
1908         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
1909         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
1910
1911         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
1912         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
1913         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
1914         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
1915
1916         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
1917         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 */
1918
1919         /* WASD */
1920         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
1921         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
1922         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
1923         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
1924         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
1925         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
1926
1927         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
1928         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
1929
1930         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
1931         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
1932
1933         /* assign map to operators */
1934         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
1935
1936 }
1937
1938 typedef struct FlyInfo {
1939         /* context stuff */
1940         RegionView3D *rv3d;
1941         View3D *v3d;
1942         ARegion *ar;
1943         Scene *scene;
1944
1945         wmTimer *timer; /* needed for redraws */
1946
1947         short state;
1948         short use_precision;
1949         short redraw;
1950         short mval[2];
1951
1952         /* fly state state */
1953         float speed; /* the speed the view is moving per redraw */
1954         short axis; /* Axis index to move allong by default Z to move allong the view */
1955         short pan_view; /* when true, pan the view instead of rotating */
1956
1957         /* relative view axis locking - xlock, zlock
1958         0; disabled
1959         1; enabled but not checking because mouse hasnt moved outside the margin since locking was checked an not needed
1960            when the mouse moves, locking is set to 2 so checks are done.
1961         2; mouse moved and checking needed, if no view altering is donem its changed back to 1 */
1962         short xlock, zlock;
1963         float xlock_momentum, zlock_momentum; /* nicer dynamics */
1964         float grid; /* world scale 1.0 default */
1965
1966         /* root most parent */
1967         Object *root_parent;
1968
1969         /* backup values */
1970         float dist_backup; /* backup the views distance since we use a zero dist for fly mode */
1971         float ofs_backup[3]; /* backup the views offset incase the user cancels flying in non camera mode */
1972         float rot_backup[4]; /* backup the views quat incase the user cancels flying in non camera mode. (quat for view, eul for camera) */
1973         short persp_backup; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */
1974
1975         void *obtfm; /* backup the objects transform */
1976
1977         /* compare between last state */
1978         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
1979         double time_lastdraw; /* time between draws */
1980
1981         void *draw_handle_pixel;
1982
1983         /* use for some lag */
1984         float dvec_prev[3]; /* old for some lag */
1985
1986 } FlyInfo;
1987
1988 static void drawFlyPixel(const struct bContext *C, struct ARegion *ar, void *arg)
1989 {
1990         FlyInfo *fly = arg;
1991
1992         /* draws 4 edge brackets that frame the safe area where the
1993         mouse can move during fly mode without spinning the view */
1994         float x1, x2, y1, y2;
1995         
1996         x1= 0.45*(float)fly->ar->winx;
1997         y1= 0.45*(float)fly->ar->winy;
1998         x2= 0.55*(float)fly->ar->winx;
1999         y2= 0.55*(float)fly->ar->winy;
2000         cpack(0);
2001         
2002         
2003         glBegin(GL_LINES);
2004         /* bottom left */
2005         glVertex2f(x1,y1); 
2006         glVertex2f(x1,y1+5);
2007         
2008         glVertex2f(x1,y1); 
2009         glVertex2f(x1+5,y1);
2010         
2011         /* top right */
2012         glVertex2f(x2,y2); 
2013         glVertex2f(x2,y2-5);
2014         
2015         glVertex2f(x2,y2); 
2016         glVertex2f(x2-5,y2);
2017         
2018         /* top left */
2019         glVertex2f(x1,y2); 
2020         glVertex2f(x1,y2-5);
2021         
2022         glVertex2f(x1,y2); 
2023         glVertex2f(x1+5,y2);
2024         
2025         /* bottom right */
2026         glVertex2f(x2,y1); 
2027         glVertex2f(x2,y1+5);
2028         
2029         glVertex2f(x2,y1); 
2030         glVertex2f(x2-5,y1);
2031         glEnd();
2032 }
2033
2034 /* FlyInfo->state */
2035 #define FLY_RUNNING             0
2036 #define FLY_CANCEL              1
2037 #define FLY_CONFIRM             2
2038
2039 static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event)
2040 {
2041         float upvec[3]; // tmp
2042         float mat[3][3];
2043
2044         fly->rv3d= CTX_wm_region_view3d(C);
2045         fly->v3d = CTX_wm_view3d(C);
2046         fly->ar = CTX_wm_region(C);
2047         fly->scene= CTX_data_scene(C);
2048
2049         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
2050                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
2051                 return FALSE;
2052         }
2053
2054         if(fly->v3d->ob_centre) {
2055                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
2056                 return FALSE;
2057         }
2058
2059         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
2060                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
2061                 return FALSE;
2062         }
2063
2064         fly->state= FLY_RUNNING;
2065         fly->speed= 0.0f;
2066         fly->axis= 2;
2067         fly->pan_view= FALSE;
2068         fly->xlock= FALSE;
2069         fly->zlock= FALSE;
2070         fly->xlock_momentum=0.0f;
2071         fly->zlock_momentum=0.0f;
2072         fly->grid= 1.0f;
2073         fly->use_precision= 0;
2074
2075         fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
2076
2077         fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
2078
2079         fly->mval[0] = event->x - fly->ar->winrct.xmin;
2080         fly->mval[1] = event->y - fly->ar->winrct.ymin;
2081
2082
2083         fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
2084
2085         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
2086
2087         fly->rv3d->rflag |= RV3D_NAVIGATING; /* so we draw the corner margins */
2088
2089         /* detect weather to start with Z locking */
2090         upvec[0]=1.0f; upvec[1]=0.0f; upvec[2]=0.0f;
2091         copy_m3_m4(mat, fly->rv3d->viewinv);
2092         mul_m3_v3(mat, upvec);
2093         if (fabs(upvec[2]) < 0.1)
2094                 fly->zlock = 1;
2095         upvec[0]=0; upvec[1]=0; upvec[2]=0;
2096
2097         fly->persp_backup= fly->rv3d->persp;
2098         fly->dist_backup= fly->rv3d->dist;
2099         if (fly->rv3d->persp==RV3D_CAMOB) {
2100                 Object *ob_back;
2101                 if((fly->root_parent=fly->v3d->camera->parent)) {
2102                         while(fly->root_parent->parent)
2103                                 fly->root_parent= fly->root_parent->parent;
2104                         ob_back= fly->root_parent;
2105                 }
2106                 else {
2107                         ob_back= fly->v3d->camera;
2108                 }
2109
2110                 /* store the original camera loc and rot */
2111                 /* TODO. axis angle etc */
2112
2113                 fly->obtfm= object_tfm_backup(ob_back);
2114
2115                 where_is_object(fly->scene, fly->v3d->camera);
2116                 negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
2117
2118                 fly->rv3d->dist=0.0;
2119         } else {
2120                 /* perspective or ortho */
2121                 if (fly->rv3d->persp==RV3D_ORTHO)
2122                         fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
2123                 copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
2124                 copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
2125                 fly->rv3d->dist= 0.0f;
2126
2127                 upvec[2]= fly->dist_backup; /*x and y are 0*/
2128                 mul_m3_v3(mat, upvec);
2129                 sub_v3_v3(fly->rv3d->ofs, upvec);
2130                 /*Done with correcting for the dist*/
2131         }
2132
2133         
2134         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
2135         WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
2136         
2137         return 1;
2138 }
2139
2140 static int flyEnd(bContext *C, FlyInfo *fly)
2141 {
2142         RegionView3D *rv3d= fly->rv3d;
2143         View3D *v3d = fly->v3d;
2144
2145         float upvec[3];
2146
2147         if(fly->state == FLY_RUNNING)
2148                 return OPERATOR_RUNNING_MODAL;
2149
2150         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
2151
2152         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
2153
2154         rv3d->dist= fly->dist_backup;
2155
2156         if (fly->state == FLY_CANCEL) {
2157         /* Revert to original view? */
2158                 if (fly->persp_backup==RV3D_CAMOB) { /* a camera view */
2159                         Object *ob_back;
2160                         if(fly->root_parent)ob_back= fly->root_parent;
2161                         else                            ob_back= fly->v3d->camera;
2162
2163                         /* store the original camera loc and rot */
2164                         object_tfm_restore(ob_back, fly->obtfm);
2165
2166                         DAG_id_flush_update(&ob_back->id, OB_RECALC_OB);
2167                 } else {
2168                         /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
2169                         copy_qt_qt(rv3d->viewquat, fly->rot_backup);
2170                         copy_v3_v3(rv3d->ofs, fly->ofs_backup);
2171                         rv3d->persp= fly->persp_backup;
2172                 }
2173         }
2174         else if (fly->persp_backup==RV3D_CAMOB) {       /* camera */
2175                 float mat3[3][3];
2176                 if(fly->root_parent) {
2177                         DAG_id_flush_update(&fly->root_parent->id, OB_RECALC_OB);
2178                 }
2179                 else {
2180                         copy_m3_m4(mat3, v3d->camera->obmat);
2181                         object_mat3_to_rot(v3d->camera, mat3, TRUE);
2182                         DAG_id_flush_update(&v3d->camera->id, OB_RECALC_OB);
2183                 }
2184         }
2185         else { /* not camera */
2186                 /* Apply the fly mode view */
2187                 /*restore the dist*/
2188                 float mat[3][3];
2189                 upvec[0]= upvec[1]= 0;
2190                 upvec[2]= fly->dist_backup; /*x and y are 0*/
2191                 copy_m3_m4(mat, rv3d->viewinv);
2192                 mul_m3_v3(mat, upvec);
2193                 add_v3_v3(rv3d->ofs, upvec);
2194                 /*Done with correcting for the dist */
2195         }
2196
2197         rv3d->rflag &= ~RV3D_NAVIGATING;
2198 //XXX2.5        BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
2199
2200         if(fly->obtfm)
2201                 MEM_freeN(fly->obtfm);
2202
2203         if(fly->state == FLY_CONFIRM) {
2204                 MEM_freeN(fly);
2205                 return OPERATOR_FINISHED;
2206         }
2207
2208         MEM_freeN(fly);
2209         return OPERATOR_CANCELLED;
2210 }
2211
2212 static void flyEvent(FlyInfo *fly, wmEvent *event)
2213 {
2214         if (event->type == TIMER && event->customdata == fly->timer) {
2215                 fly->redraw = 1;
2216         }
2217         else if (event->type == MOUSEMOVE) {
2218                 fly->mval[0] = event->x - fly->ar->winrct.xmin;
2219                 fly->mval[1] = event->y - fly->ar->winrct.ymin;
2220         } /* handle modal keymap first */
2221         else if (event->type == EVT_MODAL_MAP) {
2222                 switch (event->val) {
2223                         case FLY_MODAL_CANCEL:
2224                                 fly->state = FLY_CANCEL;
2225                                 break;
2226                         case FLY_MODAL_CONFIRM:
2227                                 fly->state = FLY_CONFIRM;
2228                                 break;
2229
2230                         case FLY_MODAL_ACCELERATE:
2231                         {
2232                                 double time_currwheel;
2233                                 float time_wheel;
2234
2235                                 time_currwheel= PIL_check_seconds_timer();
2236                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
2237                                 fly->time_lastwheel = time_currwheel;
2238                                 /*printf("Wheel %f\n", time_wheel);*/
2239                                 /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
2240                                 time_wheel = 1+ (10 - (20*MIN2(time_wheel, 0.5))); /* 0-0.5 -> 0-5.0 */
2241
2242                                 if (fly->speed<0.0f) fly->speed= 0.0f;
2243                                 else {
2244                                         if (event->shift)
2245                                                 fly->speed+= fly->grid*time_wheel*0.1;
2246                                         else
2247                                                 fly->speed+= fly->grid*time_wheel;
2248                                 }
2249                                 break;
2250                         }
2251                         case FLY_MODAL_DECELERATE:
2252                         {
2253                                 double time_currwheel;
2254                                 float time_wheel;
2255
2256                                 time_currwheel= PIL_check_seconds_timer();
2257                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
2258                                 fly->time_lastwheel = time_currwheel;
2259                                 time_wheel = 1+ (10 - (20*MIN2(time_wheel, 0.5))); /* 0-0.5 -> 0-5.0 */
2260
2261                                 if (fly->speed>0) fly->speed=0;
2262                                 else {
2263                                         if (event->shift)
2264                                                 fly->speed-= fly->grid*time_wheel*0.1;
2265                                         else
2266                                                 fly->speed-= fly->grid*time_wheel;
2267                                 }
2268                                 break;
2269                         }
2270                         case FLY_MODAL_PAN_ENABLE:
2271                                 fly->pan_view= TRUE;
2272                                 break;
2273                         case FLY_MODAL_PAN_DISABLE:
2274 //XXX2.5                warp_pointer(cent_orig[0], cent_orig[1]);
2275                                 fly->pan_view= FALSE;
2276                                 break;
2277
2278                                 /* impliment WASD keys */
2279                         case FLY_MODAL_DIR_FORWARD:
2280                                 if (fly->speed < 0.0f) fly->speed= -fly->speed; /* flip speed rather then stopping, game like motion */
2281                                 else fly->speed += fly->grid; /* increse like mousewheel if were already moving in that difection*/
2282                                 fly->axis= 2;
2283                                 break;
2284                         case FLY_MODAL_DIR_BACKWARD:
2285                                 if (fly->speed>0) fly->speed= -fly->speed;
2286                                 else fly->speed -= fly->grid;
2287                                 fly->axis= 2;
2288                                 break;
2289                         case FLY_MODAL_DIR_LEFT:
2290                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
2291                                 fly->axis= 0;
2292                                 break;
2293                         case FLY_MODAL_DIR_RIGHT:
2294                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
2295                                 fly->axis= 0;
2296                                 break;
2297
2298                         case FLY_MODAL_DIR_UP:
2299                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
2300                                 fly->axis= 1;
2301                                 break;
2302
2303                         case FLY_MODAL_DIR_DOWN:
2304                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
2305                                 fly->axis= 1;
2306                                 break;
2307
2308                         case FLY_MODAL_AXIS_LOCK_X:
2309                                 if (fly->xlock) fly->xlock=0;
2310                                 else {
2311                                         fly->xlock = 2;
2312                                         fly->xlock_momentum = 0.0;
2313                                 }
2314                                 break;
2315                         case FLY_MODAL_AXIS_LOCK_Z:
2316                                 if (fly->zlock) fly->zlock=0;
2317                                 else {
2318                                         fly->zlock = 2;
2319                                         fly->zlock_momentum = 0.0;
2320                                 }
2321                                 break;
2322
2323                         case FLY_MODAL_PRECISION_ENABLE:
2324                                 fly->use_precision= TRUE;
2325                                 break;
2326                         case FLY_MODAL_PRECISION_DISABLE:
2327                                 fly->use_precision= FALSE;
2328                                 break;
2329
2330                 }
2331         }
2332 }
2333
2334 static int flyApply(bContext *C, FlyInfo *fly)
2335 {
2336
2337 #define FLY_ROTATE_FAC 2.5f /* more is faster */
2338 #define FLY_ZUP_CORRECT_FAC 0.1f /* ammount to correct per step */
2339 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
2340
2341         /*
2342         fly mode - Shift+F
2343         a fly loop where the user can move move the view as if they are flying
2344         */
2345         RegionView3D *rv3d= fly->rv3d;
2346         View3D *v3d = fly->v3d;
2347         ARegion *ar = fly->ar;
2348         Scene *scene= fly->scene;
2349
2350         float prev_view_mat[4][4];
2351
2352         float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
2353         dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
2354
2355         /* Camera Uprighting variables */
2356         upvec[3]={0,0,0}, /* stores the view's up vector */
2357
2358         moffset[2], /* mouse offset from the views center */
2359         tmp_quat[4]; /* used for rotating the view */
2360
2361         int cent_orig[2], /* view center */
2362 //XXX- can avoid using //       cent[2], /* view center modified */
2363         xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
2364         unsigned char
2365         apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
2366
2367         if(fly->root_parent)
2368                 view3d_persp_mat4(rv3d, prev_view_mat);
2369
2370         /* the dist defines a vector that is infront of the offset
2371         to rotate the view about.
2372         this is no good for fly mode because we
2373         want to rotate about the viewers center.
2374         but to correct the dist removal we must
2375         alter offset so the view doesn't jump. */
2376
2377         xmargin= ar->winx/20.0f;
2378         ymargin= ar->winy/20.0f;
2379
2380         cent_orig[0]= ar->winrct.xmin + ar->winx/2;
2381         cent_orig[1]= ar->winrct.ymin + ar->winy/2;
2382
2383         {
2384
2385                 /* mouse offset from the center */
2386                 moffset[0]= fly->mval[0]- ar->winx/2;
2387                 moffset[1]= fly->mval[1]- ar->winy/2;
2388
2389                 /* enforce a view margin */
2390                 if (moffset[0]>xmargin)                 moffset[0]-=xmargin;
2391                 else if (moffset[0] < -xmargin) moffset[0]+=xmargin;
2392                 else                                                    moffset[0]=0;
2393
2394                 if (moffset[1]>ymargin)                 moffset[1]-=ymargin;
2395                 else if (moffset[1] < -ymargin) moffset[1]+=ymargin;
2396                 else                                                    moffset[1]=0;
2397
2398
2399                 /* scale the mouse movement by this value - scales mouse movement to the view size
2400                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
2401                  *
2402                  * the mouse moves isnt linear */
2403
2404                 if(moffset[0]) {
2405                         moffset[0] /= ar->winx - (xmargin*2);
2406                         moffset[0] *= fabs(moffset[0]);
2407                 }
2408
2409                 if(moffset[1]) {
2410                         moffset[1] /= ar->winy - (ymargin*2);
2411                         moffset[1] *= fabs(moffset[1]);
2412                 }
2413
2414                 /* Should we redraw? */
2415                 if(fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
2416                         float dvec_tmp[3];
2417                         double time_current, time_redraw; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
2418                         float time_redraw_clamped;
2419
2420                         time_current= PIL_check_seconds_timer();
2421                         time_redraw= (float)(time_current - fly->time_lastdraw);
2422                         time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
2423                         fly->time_lastdraw= time_current;
2424                         /*fprintf(stderr, "%f\n", time_redraw);*/ /* 0.002 is a small redraw 0.02 is larger */
2425
2426                         /* Scale the time to use shift to scale the speed down- just like
2427                         shift slows many other areas of blender down */
2428                         if (fly->use_precision)
2429                                 fly->speed= fly->speed * (1.0f-time_redraw_clamped);
2430
2431                         copy_m3_m4(mat, rv3d->viewinv);
2432
2433                         if (fly->pan_view==TRUE) {
2434                                 /* pan only */
2435                                 dvec_tmp[0]= -moffset[0];
2436                                 dvec_tmp[1]= -moffset[1];
2437                                 dvec_tmp[2]= 0;
2438
2439                                 if (fly->use_precision) {
2440                                         dvec_tmp[0] *= 0.1;
2441                                         dvec_tmp[1] *= 0.1;
2442                                 }
2443
2444                                 mul_m3_v3(mat, dvec_tmp);
2445                                 mul_v3_fl(dvec_tmp, time_redraw*200.0 * fly->grid);
2446
2447                         } else {
2448                                 float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
2449
2450                                 /* rotate about the X axis- look up/down */
2451                                 if (moffset[1]) {
2452                                         upvec[0]=1;
2453                                         upvec[1]=0;
2454                                         upvec[2]=0;
2455                                         mul_m3_v3(mat, upvec);
2456                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); /* Rotate about the relative up vec */
2457                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2458
2459                                         if (fly->xlock) fly->xlock = 2; /*check for rotation*/
2460                                         if (fly->zlock) fly->zlock = 2;
2461                                         fly->xlock_momentum= 0.0f;
2462                                 }
2463
2464                                 /* rotate about the Y axis- look left/right */
2465                                 if (moffset[0]) {
2466
2467                                         /* if we're upside down invert the moffset */
2468                                         upvec[0]=0;
2469                                         upvec[1]=1;
2470                                         upvec[2]=0;
2471                                         mul_m3_v3(mat, upvec);
2472
2473                                         if(upvec[2] < 0.0f)
2474                                                 moffset[0]= -moffset[0];
2475
2476                                         /* make the lock vectors */
2477                                         if (fly->zlock) {
2478                                                 upvec[0]=0;
2479                                                 upvec[1]=0;
2480                                                 upvec[2]=1;
2481                                         } else {
2482                                                 upvec[0]=0;
2483                                                 upvec[1]=1;
2484                                                 upvec[2]=0;
2485                                                 mul_m3_v3(mat, upvec);
2486                                         }
2487
2488                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
2489                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2490
2491                                         if (fly->xlock) fly->xlock = 2;/*check for rotation*/
2492                                         if (fly->zlock) fly->zlock = 2;
2493                                 }
2494
2495                                 if (fly->zlock==2) {
2496                                         upvec[0]=1;
2497                                         upvec[1]=0;
2498                                         upvec[2]=0;
2499                                         mul_m3_v3(mat, upvec);
2500
2501                                         /*make sure we have some z rolling*/
2502                                         if (fabs(upvec[2]) > 0.00001f) {
2503                                                 roll= upvec[2]*5;
2504                                                 upvec[0]=0; /*rotate the view about this axis*/
2505                                                 upvec[1]=0;
2506                                                 upvec[2]=1;
2507
2508                                                 mul_m3_v3(mat, upvec);
2509                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
2510                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2511
2512                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
2513                                         } else {
2514                                                 fly->zlock=1; /* dont check until the view rotates again */
2515                                                 fly->zlock_momentum= 0.0f;
2516                                         }
2517                                 }
2518
2519                                 if (fly->xlock==2 && moffset[1]==0) { /*only apply xcorrect when mouse isnt applying x rot*/
2520                                         upvec[0]=0;
2521                                         upvec[1]=0;
2522                                         upvec[2]=1;
2523                                         mul_m3_v3(mat, upvec);
2524                                         /*make sure we have some z rolling*/
2525                                         if (fabs(upvec[2]) > 0.00001) {
2526                                                 roll= upvec[2] * -5;
2527
2528                                                 upvec[0]= 1.0f; /*rotate the view about this axis*/
2529                                                 upvec[1]= 0.0f;
2530                                                 upvec[2]= 0.0f;
2531
2532                                                 mul_m3_v3(mat, upvec);
2533
2534                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->xlock_momentum*0.1f); /* Rotate about the relative up vec */
2535                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
2536
2537                                                 fly->xlock_momentum += 0.05f;
2538                                         } else {
2539                                                 fly->xlock=1; /* see above */
2540                                                 fly->xlock_momentum= 0.0f;
2541                                         }
2542                                 }
2543
2544
2545                                 if (apply_rotation) {
2546                                         /* Normal operation */
2547                                         /* define dvec, view direction vector */
2548                                         dvec_tmp[0]= dvec_tmp[1]= dvec_tmp[2]= 0.0f;
2549                                         /* move along the current axis */
2550                                         dvec_tmp[fly->axis]= 1.0f;
2551
2552                                         mul_m3_v3(mat, dvec_tmp);
2553
2554                                         mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
2555                                 }
2556                         }
2557
2558                         /* impose a directional lag */
2559                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f/(1.0f+(time_redraw*5.0f))));
2560
2561                         if (rv3d->persp==RV3D_CAMOB) {
2562                                 Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
2563                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
2564                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
2565                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
2566                         }
2567
2568                         add_v3_v3(rv3d->ofs, dvec);
2569
2570                         /* todo, dynamic keys */
2571 #if 0
2572                         if (fly->zlock && fly->xlock)
2573                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2574                         else if (fly->zlock)
2575                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2576                         else if (fly->xlock)
2577                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2578                         else
2579                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
2580 #endif
2581
2582                         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
2583                         if (rv3d->persp==RV3D_CAMOB) {
2584                                 ID *id_key;
2585                                 /* transform the parent or the camera? */
2586                                 if(fly->root_parent) {
2587                                         Object *ob_update;
2588                     
2589                                         float view_mat[4][4];
2590                                         float prev_view_imat[4][4];
2591                                         float diff_mat[4][4];
2592                                         float parent_mat[4][4];
2593
2594                                         invert_m4_m4(prev_view_imat, prev_view_mat);
2595                                         view3d_persp_mat4(rv3d, view_mat);
2596                                         mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
2597                                         mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
2598                                         object_apply_mat4(fly->root_parent, parent_mat);
2599
2600                                         // where_is_object(scene, fly->root_parent);
2601
2602                                         ob_update= v3d->camera->parent;
2603                                         while(ob_update) {
2604                                                 DAG_id_flush_update(&ob_update->id, OB_RECALC_OB);
2605                                                 ob_update= ob_update->parent;
2606                                         }
2607
2608                                         copy_m4_m4(prev_view_mat, view_mat);
2609
2610                                         id_key= &fly->root_parent->id;
2611
2612                                 }
2613                                 else {
2614                                         float view_mat[4][4];
2615                                         view3d_persp_mat4(rv3d, view_mat);
2616                                         object_apply_mat4(v3d->camera, view_mat);
2617                                         id_key= &v3d->camera->id;
2618                                 }
2619
2620                                 /* record the motion */
2621                                 if (autokeyframe_cfra_can_key(scene, id_key)) {
2622                                         ListBase dsources = {NULL, NULL};
2623                                         
2624                                         /* add datasource override for the camera object */
2625                                         ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
2626                                         
2627                                         /* insert keyframes 
2628                                          *      1) on the first frame
2629                                          *      2) on each subsequent frame
2630                                          *              TODO: need to check in future that frame changed before doing this 
2631                                          */
2632                                         if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
2633                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
2634                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
2635                                         }
2636                                         if (fly->speed) {
2637                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
2638                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
2639                                         }
2640                                         
2641                                         /* free temp data */
2642                                         BLI_freelistN(&dsources);
2643                                 }
2644                         }
2645                 } else
2646                         /*were not redrawing but we need to update the time else the view will jump */
2647                         fly->time_lastdraw= PIL_check_seconds_timer();
2648                 /* end drawing */
2649                 copy_v3_v3(fly->dvec_prev, dvec);
2650         }
2651
2652 /* moved to flyEnd() */
2653
2654         return OPERATOR_FINISHED;
2655 }
2656
2657
2658
2659 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
2660 {
2661         RegionView3D *rv3d= CTX_wm_region_view3d(C);
2662         FlyInfo *fly;
2663
2664         if(rv3d->viewlock)
2665                 return OPERATOR_CANCELLED;
2666
2667         fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
2668
2669         op->customdata= fly;
2670
2671         if(initFlyInfo(C, fly, op, event)==FALSE) {
2672                 MEM_freeN(op->customdata);
2673                 return OPERATOR_CANCELLED;
2674         }
2675
2676         flyEvent(fly, event);
2677
2678         WM_event_add_modal_handler(C, op);
2679
2680         return OPERATOR_RUNNING_MODAL;
2681 }
2682
2683 static int fly_cancel(bContext *C, wmOperator *op)
2684 {
2685         FlyInfo *fly = op->customdata;
2686
2687         fly->state = FLY_CANCEL;
2688         flyEnd(C, fly);
2689         op->customdata= NULL;
2690
2691         return OPERATOR_CANCELLED;
2692 }
2693
2694 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
2695 {
2696         int exit_code;
2697
2698         FlyInfo *fly = op->customdata;
2699
2700         fly->redraw= 0;
2701
2702         flyEvent(fly, event);
2703
2704         if(event->type==TIMER && event->customdata == fly->timer)
2705                 flyApply(C, fly);
2706
2707         if(fly->redraw) {
2708                 ED_region_tag_redraw(CTX_wm_region(C));
2709         }
2710
2711         exit_code = flyEnd(C, fly);
2712
2713         if(exit_code!=OPERATOR_RUNNING_MODAL)
2714                 ED_region_tag_redraw(CTX_wm_region(C));
2715
2716         return exit_code;
2717 }
2718
2719 void VIEW3D_OT_fly(wmOperatorType *ot)
2720 {
2721
2722         /* identifiers */
2723         ot->name= "Fly Navigation";
2724         ot->description= "Interactively fly around the scene";
2725         ot->idname= "VIEW3D_OT_fly";
2726
2727         /* api callbacks */
2728         ot->invoke= fly_invoke;
2729         ot->cancel= fly_cancel;
2730         ot->modal= fly_modal;
2731         ot->poll= ED_operator_view3d_active;
2732
2733         /* flags */
2734         ot->flag= OPTYPE_BLOCKING;
2735
2736 }
2737
2738 /* ************************************** */
2739
2740 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
2741 {
2742         float alignaxis[3] = {0.0, 0.0, 0.0};
2743         float norm[3], axis[3], angle, new_quat[4];
2744         
2745         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
2746         else alignaxis[-axisidx-1]= -1.0;
2747
2748         normalize_v3_v3(norm, vec);
2749
2750         angle= (float)acos(dot_v3v3(alignaxis, norm));
2751         cross_v3_v3v3(axis, alignaxis, norm);
2752         axis_angle_to_quat( new_quat,axis, -angle);
2753         
2754         rv3d->view= 0;
2755         
2756         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
2757                 /* switch out of camera view */
2758                 float orig_ofs[3];
2759                 float orig_dist= rv3d->dist;
2760                 float orig_lens= v3d->lens;
2761                 
2762                 copy_v3_v3(orig_ofs, rv3d->ofs);
2763                 rv3d->persp= RV3D_PERSP;
2764                 rv3d->dist= 0.0;
2765                 view3d_settings_from_ob(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
2766                 smooth_view(NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
2767         } else {
2768                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP; /* switch out of camera mode */
2769                 smooth_view(NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
2770         }
2771 }
2772
2773 int view3d_is_ortho(View3D *v3d, RegionView3D *rv3d)
2774 {
2775         return (rv3d->persp == RV3D_ORTHO || (v3d->camera && ((Camera *)v3d->camera->data)->type == CAM_ORTHO));
2776 }