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