eeaf87757cecfd2aa4801562eb33da7db62ae624
[blender.git] / source / blender / editors / space_view3d / view3d_view.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/editors/space_view3d/view3d_view.c
30  *  \ingroup spview3d
31  */
32
33
34 #include "DNA_camera_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_lamp_types.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_math.h"
42 #include "BLI_rect.h"
43 #include "BLI_listbase.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_anim.h"
47 #include "BKE_action.h"
48 #include "BKE_context.h"
49 #include "BKE_depsgraph.h"
50 #include "BKE_object.h"
51 #include "BKE_global.h"
52 #include "BKE_main.h"
53 #include "BKE_report.h"
54 #include "BKE_scene.h"
55 #include "BKE_screen.h"
56
57 #include "BIF_gl.h"
58 #include "BIF_glutil.h"
59
60 #include "GPU_draw.h"
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64
65 #include "ED_screen.h"
66 #include "ED_armature.h"
67
68 #ifdef WITH_GAMEENGINE
69 #include "BL_System.h"
70 #endif
71
72 #include "view3d_intern.h"      // own include
73
74 /* use this call when executing an operator,
75    event system doesn't set for each event the
76    opengl drawing context */
77 void view3d_operator_needs_opengl(const bContext *C)
78 {
79         wmWindow *win = CTX_wm_window(C);
80         ARegion *ar= CTX_wm_region(C);
81         
82         view3d_region_operator_needs_opengl(win, ar);
83 }
84
85 void view3d_region_operator_needs_opengl(wmWindow *win, ARegion *ar)
86 {
87         /* for debugging purpose, context should always be OK */
88         if ((ar == NULL) || (ar->regiontype!=RGN_TYPE_WINDOW))
89                 printf("view3d_region_operator_needs_opengl error, wrong region\n");
90         else {
91                 RegionView3D *rv3d= ar->regiondata;
92                 
93                 wmSubWindowSet(win, 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 void ED_view3d_ob_clip_range_get(Object *ob, float *lens, float *clipsta, float *clipend)
110 {
111         if(ob->type==OB_LAMP ) {
112                 Lamp *la = ob->data;
113                 if (lens) {
114                         float x1, fac;
115                         fac= cosf((float)M_PI*la->spotsize/360.0f);
116                         x1= saacos(fac);
117                         *lens= 16.0f*fac/sinf(x1);
118                 }
119                 if (clipsta)    *clipsta= la->clipsta;
120                 if (clipend)    *clipend= la->clipend;
121         }
122         else if(ob->type==OB_CAMERA) {
123                 Camera *cam= ob->data;
124                 if (lens)               *lens= cam->lens;
125                 if (clipsta)    *clipsta= cam->clipsta;
126                 if (clipend)    *clipend= cam->clipend;
127         }
128         else {
129                 if (lens)               *lens= 35.0f;
130         }
131 }
132
133 /* ****************** smooth view operator ****************** */
134 /* This operator is one of the 'timer refresh' ones like animation playback */
135
136 struct SmoothViewStore {
137         float orig_dist, new_dist;
138         float orig_lens, new_lens;
139         float orig_quat[4], new_quat[4];
140         float orig_ofs[3], new_ofs[3];
141         
142         int to_camera, orig_view;
143         
144         double time_allowed;
145 };
146
147 /* will start timer if appropriate */
148 /* the arguments are the desired situation */
149 void smooth_view(bContext *C, View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera, float *ofs, float *quat, float *dist, float *lens)
150 {
151         wmWindowManager *wm= CTX_wm_manager(C);
152         wmWindow *win= CTX_wm_window(C);
153         ScrArea *sa= CTX_wm_area(C);
154
155         RegionView3D *rv3d= ar->regiondata;
156         struct SmoothViewStore sms= {0};
157         short ok= FALSE;
158         
159         /* initialize sms */
160         copy_v3_v3(sms.new_ofs, rv3d->ofs);
161         copy_qt_qt(sms.new_quat, rv3d->viewquat);
162         sms.new_dist= rv3d->dist;
163         sms.new_lens= v3d->lens;
164         sms.to_camera= 0;
165
166         /* note on camera locking, this is a little confusing but works ok.
167          * we may be changing the view 'as if' there is no active camera, but infact
168          * there is an active camera which is locked to the view.
169          *
170          * In the case where smooth view is moving _to_ a camera we dont want that
171          * camera to be moved or changed, so only when the camera is not being set should
172          * we allow camera option locking to initialize the view settings from the camera.
173          */
174         if(camera == NULL && oldcamera == NULL) {
175                 ED_view3d_camera_lock_init(v3d, rv3d);
176         }
177
178         /* store the options we want to end with */
179         if(ofs) copy_v3_v3(sms.new_ofs, ofs);
180         if(quat) copy_qt_qt(sms.new_quat, quat);
181         if(dist) sms.new_dist= *dist;
182         if(lens) sms.new_lens= *lens;
183
184         if (camera) {
185                 ED_view3d_from_object(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens);
186                 sms.to_camera= 1; /* restore view3d values in end */
187         }
188         
189         if (C && U.smooth_viewtx) {
190                 int changed = 0; /* zero means no difference */
191                 
192                 if (oldcamera != camera)
193                         changed = 1;
194                 else if (sms.new_dist != rv3d->dist)
195                         changed = 1;
196                 else if (sms.new_lens != v3d->lens)
197                         changed = 1;
198                 else if (!equals_v3v3(sms.new_ofs, rv3d->ofs))
199                         changed = 1;
200                 else if (!equals_v4v4(sms.new_quat, rv3d->viewquat))
201                         changed = 1;
202                 
203                 /* The new view is different from the old one
204                         * so animate the view */
205                 if (changed) {
206
207                         /* original values */
208                         if (oldcamera) {
209                                 sms.orig_dist= rv3d->dist; // below function does weird stuff with it...
210                                 ED_view3d_from_object(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens);
211                         }
212                         else {
213                                 copy_v3_v3(sms.orig_ofs, rv3d->ofs);
214                                 copy_qt_qt(sms.orig_quat, rv3d->viewquat);
215                                 sms.orig_dist= rv3d->dist;
216                                 sms.orig_lens= v3d->lens;
217                         }
218                         /* grid draw as floor */
219                         if((rv3d->viewlock & RV3D_LOCKED)==0) {
220                                 /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
221                                 sms.orig_view= rv3d->sms ? rv3d->sms->orig_view : rv3d->view;
222                                 rv3d->view= RV3D_VIEW_USER;
223                         }
224
225                         sms.time_allowed= (double)U.smooth_viewtx / 1000.0;
226                         
227                         /* if this is view rotation only
228                                 * we can decrease the time allowed by
229                                 * the angle between quats 
230                                 * this means small rotations wont lag */
231                         if (quat && !ofs && !dist) {
232                                 float vec1[3]={0,0,1}, vec2[3]= {0,0,1};
233                                 float q1[4], q2[4];
234
235                                 invert_qt_qt(q1, sms.new_quat);
236                                 invert_qt_qt(q2, sms.orig_quat);
237
238                                 mul_qt_v3(q1, vec1);
239                                 mul_qt_v3(q2, vec2);
240
241                                 /* scale the time allowed by the rotation */
242                                 sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */
243                         }
244
245                         /* ensure it shows correct */
246                         if(sms.to_camera) rv3d->persp= RV3D_PERSP;
247
248                         rv3d->rflag |= RV3D_NAVIGATING;
249                         
250                         /* keep track of running timer! */
251                         if(rv3d->sms==NULL)
252                                 rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d");
253                         *rv3d->sms= sms;
254                         if(rv3d->smooth_timer)
255                                 WM_event_remove_timer(wm, win, rv3d->smooth_timer);
256                         /* TIMER1 is hardcoded in keymap */
257                         rv3d->smooth_timer= WM_event_add_timer(wm, win, TIMER1, 1.0/100.0);     /* max 30 frs/sec */
258                         
259                         ok= TRUE;
260                 }
261         }
262         
263         /* if we get here nothing happens */
264         if(ok == FALSE) {
265                 if(sms.to_camera==0) {
266                         copy_v3_v3(rv3d->ofs, sms.new_ofs);
267                         copy_qt_qt(rv3d->viewquat, sms.new_quat);
268                         rv3d->dist = sms.new_dist;
269                         v3d->lens = sms.new_lens;
270                 }
271
272                 if(rv3d->viewlock & RV3D_BOXVIEW)
273                         view3d_boxview_copy(sa, ar);
274
275                 ED_region_tag_redraw(ar);
276         }
277 }
278
279 /* only meant for timer usage */
280 static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
281 {
282         View3D *v3d = CTX_wm_view3d(C);
283         RegionView3D *rv3d= CTX_wm_region_view3d(C);
284         struct SmoothViewStore *sms= rv3d->sms;
285         float step, step_inv;
286         
287         /* escape if not our timer */
288         if(rv3d->smooth_timer==NULL || rv3d->smooth_timer!=event->customdata)
289                 return OPERATOR_PASS_THROUGH;
290         
291         if(sms->time_allowed != 0.0)
292                 step = (float)((rv3d->smooth_timer->duration)/sms->time_allowed);
293         else
294                 step = 1.0f;
295         
296         /* end timer */
297         if(step >= 1.0f) {
298                 
299                 /* if we went to camera, store the original */
300                 if(sms->to_camera) {
301                         rv3d->persp= RV3D_CAMOB;
302                         copy_v3_v3(rv3d->ofs, sms->orig_ofs);
303                         copy_qt_qt(rv3d->viewquat, sms->orig_quat);
304                         rv3d->dist = sms->orig_dist;
305                         v3d->lens = sms->orig_lens;
306                 }
307                 else {
308                         copy_v3_v3(rv3d->ofs, sms->new_ofs);
309                         copy_qt_qt(rv3d->viewquat, sms->new_quat);
310                         rv3d->dist = sms->new_dist;
311                         v3d->lens = sms->new_lens;
312
313                         ED_view3d_camera_lock_sync(v3d, rv3d);
314                 }
315                 
316                 if((rv3d->viewlock & RV3D_LOCKED)==0) {
317                         rv3d->view= sms->orig_view;
318                 }
319
320                 MEM_freeN(rv3d->sms);
321                 rv3d->sms= NULL;
322                 
323                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
324                 rv3d->smooth_timer= NULL;
325                 rv3d->rflag &= ~RV3D_NAVIGATING;
326         }
327         else {
328                 int i;
329                 
330                 /* ease in/out */
331                 if (step < 0.5f)        step = (float)pow(step*2.0f, 2.0)/2.0f;
332                 else                            step = (float)1.0f-(powf(2.0f*(1.0f-step),2.0f)/2.0f);
333
334                 step_inv = 1.0f-step;
335
336                 for (i=0; i<3; i++)
337                         rv3d->ofs[i] = sms->new_ofs[i] * step + sms->orig_ofs[i]*step_inv;
338
339                 interp_qt_qtqt(rv3d->viewquat, sms->orig_quat, sms->new_quat, step);
340                 
341                 rv3d->dist = sms->new_dist * step + sms->orig_dist*step_inv;
342                 v3d->lens = sms->new_lens * step + sms->orig_lens*step_inv;
343
344                 ED_view3d_camera_lock_sync(v3d, rv3d);
345         }
346         
347         if(rv3d->viewlock & RV3D_BOXVIEW)
348                 view3d_boxview_copy(CTX_wm_area(C), CTX_wm_region(C));
349         
350         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, v3d);
351         
352         return OPERATOR_FINISHED;
353 }
354
355 void VIEW3D_OT_smoothview(wmOperatorType *ot)
356 {
357         
358         /* identifiers */
359         ot->name= "Smooth View";
360         ot->idname= "VIEW3D_OT_smoothview";
361         ot->description="The time to animate the change of view (in milliseconds)";
362         
363         /* api callbacks */
364         ot->invoke= view3d_smoothview_invoke;
365         
366         ot->poll= ED_operator_view3d_active;
367 }
368
369 /* ****************** change view operators ****************** */
370
371 static int view3d_setcameratoview_exec(bContext *C, wmOperator *UNUSED(op))
372 {
373         View3D *v3d = CTX_wm_view3d(C);
374         RegionView3D *rv3d= CTX_wm_region_view3d(C);
375
376         copy_qt_qt(rv3d->lviewquat, rv3d->viewquat);
377         rv3d->lview= rv3d->view;
378         if(rv3d->persp != RV3D_CAMOB) {
379                 rv3d->lpersp= rv3d->persp;
380         }
381
382         ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
383         DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
384         rv3d->persp = RV3D_CAMOB;
385         
386         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, v3d->camera);
387         
388         return OPERATOR_FINISHED;
389
390 }
391
392 static int view3d_setcameratoview_poll(bContext *C)
393 {
394         View3D *v3d= CTX_wm_view3d(C);
395         if(v3d && v3d->camera && v3d->camera->id.lib==NULL) {
396                 RegionView3D *rv3d= CTX_wm_region_view3d(C);
397                 if(rv3d && !rv3d->viewlock) {
398                         return 1;
399                 }
400         }
401
402         return 0;
403 }
404
405 void VIEW3D_OT_setcameratoview(wmOperatorType *ot)
406 {
407         
408         /* identifiers */
409         ot->name= "Align Camera To View";
410         ot->description= "Set camera view to active view";
411         ot->idname= "VIEW3D_OT_camera_to_view";
412         
413         /* api callbacks */
414         ot->exec= view3d_setcameratoview_exec;  
415         ot->poll= view3d_setcameratoview_poll;
416         
417         /* flags */
418         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
419 }
420
421
422 static int view3d_setobjectascamera_exec(bContext *C, wmOperator *UNUSED(op))
423 {
424         View3D *v3d = CTX_wm_view3d(C);
425         ARegion *ar= ED_view3d_context_region_unlock(C);
426         RegionView3D *rv3d= ar->regiondata; /* no NULL check is needed, poll checks */
427         Scene *scene= CTX_data_scene(C);
428         Object *ob = CTX_data_active_object(C);
429
430         if(ob) {
431                 Object *camera_old= (rv3d->persp == RV3D_CAMOB) ? V3D_CAMERA_SCENE(scene, v3d) : NULL;
432                 rv3d->persp= RV3D_CAMOB;
433                 v3d->camera= ob;
434                 if(v3d->scenelock)
435                         scene->camera= ob;
436
437                 if(camera_old != ob) /* unlikely but looks like a glitch when set to the same */
438                         smooth_view(C, v3d, ar, camera_old, v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens);
439
440                 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_OPTIONS|NC_OBJECT|ND_DRAW, CTX_data_scene(C));
441         }
442         
443         return OPERATOR_FINISHED;
444 }
445
446 int ED_operator_rv3d_unlock_poll(bContext *C)
447 {
448         return ED_view3d_context_region_unlock(C) != NULL;
449 }
450
451 void VIEW3D_OT_object_as_camera(wmOperatorType *ot)
452 {
453         
454         /* identifiers */
455         ot->name= "Set Active Object as Camera";
456         ot->description= "Set the active object as the active camera for this view or scene";
457         ot->idname= "VIEW3D_OT_object_as_camera";
458         
459         /* api callbacks */
460         ot->exec= view3d_setobjectascamera_exec;        
461         ot->poll= ED_operator_rv3d_unlock_poll;
462         
463         /* flags */
464         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
465 }
466
467 /* ********************************** */
468
469 void ED_view3d_calc_clipping(BoundBox *bb, float planes[4][4], bglMats *mats, rcti *rect)
470 {
471         float modelview[4][4];
472         double xs, ys, p[3];
473         int val, flip_sign, a;
474
475         /* near zero floating point values can give issues with gluUnProject
476                 in side view on some implementations */
477         if(fabs(mats->modelview[0]) < 1e-6) mats->modelview[0]= 0.0;
478         if(fabs(mats->modelview[5]) < 1e-6) mats->modelview[5]= 0.0;
479
480         /* Set up viewport so that gluUnProject will give correct values */
481         mats->viewport[0] = 0;
482         mats->viewport[1] = 0;
483
484         /* four clipping planes and bounding volume */
485         /* first do the bounding volume */
486         for(val=0; val<4; val++) {
487                 xs= (val==0||val==3)?rect->xmin:rect->xmax;
488                 ys= (val==0||val==1)?rect->ymin:rect->ymax;
489
490                 gluUnProject(xs, ys, 0.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
491                 VECCOPY(bb->vec[val], p);
492
493                 gluUnProject(xs, ys, 1.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
494                 VECCOPY(bb->vec[4+val], p);
495         }
496
497         /* verify if we have negative scale. doing the transform before cross
498            product flips the sign of the vector compared to doing cross product
499            before transform then, so we correct for that. */
500         for(a=0; a<16; a++)
501                 ((float*)modelview)[a] = mats->modelview[a];
502         flip_sign = is_negative_m4(modelview);
503
504         /* then plane equations */
505         for(val=0; val<4; val++) {
506
507                 normal_tri_v3(planes[val], bb->vec[val], bb->vec[val==3?0:val+1], bb->vec[val+4]);
508
509                 if(flip_sign)
510                         negate_v3(planes[val]);
511
512                 planes[val][3]= - planes[val][0]*bb->vec[val][0]
513                         - planes[val][1]*bb->vec[val][1]
514                         - planes[val][2]*bb->vec[val][2];
515         }
516 }
517
518 /* create intersection coordinates in view Z direction at mouse coordinates */
519 void ED_view3d_win_to_segment_clip(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_end[3])
520 {
521         RegionView3D *rv3d= ar->regiondata;
522         
523         if(rv3d->is_persp) {
524                 float vec[3];
525                 ED_view3d_win_to_vector(ar, mval, vec);
526
527                 copy_v3_v3(ray_start, rv3d->viewinv[3]);
528                 VECADDFAC(ray_start, rv3d->viewinv[3], vec, v3d->near);
529                 VECADDFAC(ray_end, rv3d->viewinv[3], vec, v3d->far);
530         }
531         else {
532                 float vec[4];
533                 vec[0] = 2.0f * mval[0] / ar->winx - 1;
534                 vec[1] = 2.0f * mval[1] / ar->winy - 1;
535                 vec[2] = 0.0f;
536                 vec[3] = 1.0f;
537                 
538                 mul_m4_v4(rv3d->persinv, vec);
539                 
540                 VECADDFAC(ray_start, vec, rv3d->viewinv[2],  1000.0f);
541                 VECADDFAC(ray_end, vec, rv3d->viewinv[2], -1000.0f);
542         }
543
544         /* clipping */
545         if(rv3d->rflag & RV3D_CLIPPING) {
546                 int a;
547                 for(a=0; a<4; a++) {
548                         clip_line_plane(ray_start, ray_end, rv3d->clip[a]);
549                 }
550         }
551 }
552
553 /* create intersection ray in view Z direction at mouse coordinates */
554 void ED_view3d_win_to_ray(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_normal[3])
555 {
556         float ray_end[3];
557         
558         ED_view3d_win_to_segment_clip(ar, v3d, mval, ray_start, ray_end);
559         sub_v3_v3v3(ray_normal, ray_end, ray_start);
560         normalize_v3(ray_normal);
561 }
562
563 void ED_view3d_global_to_vector(RegionView3D *rv3d, const float coord[3], float vec[3])
564 {
565         if (rv3d->is_persp) {
566                 float p1[4], p2[4];
567
568                 copy_v3_v3(p1, coord);
569                 p1[3] = 1.0f;
570                 copy_v3_v3(p2, p1);
571                 p2[3] = 1.0f;
572                 mul_m4_v4(rv3d->viewmat, p2);
573
574                 mul_v3_fl(p2, 2.0f);
575
576                 mul_m4_v4(rv3d->viewinv, p2);
577
578                 sub_v3_v3v3(vec, p1, p2);
579         }
580         else {
581                 copy_v3_v3(vec, rv3d->viewinv[2]);
582         }
583         normalize_v3(vec);
584 }
585
586 int initgrabz(RegionView3D *rv3d, float x, float y, float z)
587 {
588         int flip= FALSE;
589         if(rv3d==NULL) return flip;
590         rv3d->zfac= rv3d->persmat[0][3]*x+ rv3d->persmat[1][3]*y+ rv3d->persmat[2][3]*z+ rv3d->persmat[3][3];
591         if (rv3d->zfac < 0.0f)
592                 flip= TRUE;
593         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that 
594                 * (accounting for near zero values)
595                 * */
596         if (rv3d->zfac < 1.e-6f && rv3d->zfac > -1.e-6f) rv3d->zfac = 1.0f;
597         
598         /* Negative zfac means x, y, z was behind the camera (in perspective).
599                 * This gives flipped directions, so revert back to ok default case.
600         */
601         // NOTE: I've changed this to flip zfac to be positive again for now so that GPencil draws ok
602         //      -- Aligorith, 2009Aug31
603         //if (rv3d->zfac < 0.0f) rv3d->zfac = 1.0f;
604         if (rv3d->zfac < 0.0f) rv3d->zfac= -rv3d->zfac;
605         
606         return flip;
607 }
608
609 void ED_view3d_win_to_3d(ARegion *ar, const float depth_pt[3], const float mval[2], float out[3])
610 {
611         RegionView3D *rv3d= ar->regiondata;
612         
613         float line_sta[3];
614         float line_end[3];
615
616         if(rv3d->is_persp) {
617                 float mousevec[3];
618                 copy_v3_v3(line_sta, rv3d->viewinv[3]);
619                 ED_view3d_win_to_vector(ar, mval, mousevec);
620                 add_v3_v3v3(line_end, line_sta, mousevec);
621
622                 if(isect_line_plane_v3(out, line_sta, line_end, depth_pt, rv3d->viewinv[2], TRUE) == 0) {
623                         /* highly unlikely to ever happen, mouse vec paralelle with view plane */
624                         zero_v3(out);
625                 }
626         }
627         else {
628         const float dx= (2.0f * mval[0] / (float)ar->winx) - 1.0f;
629         const float dy= (2.0f * mval[1] / (float)ar->winy) - 1.0f;
630                 line_sta[0]= (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
631                 line_sta[1]= (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
632                 line_sta[2]= (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
633
634                 add_v3_v3v3(line_end, line_sta, rv3d->viewinv[2]);
635                 closest_to_line_v3(out, depth_pt, line_sta, line_end);
636         }
637 }
638
639 /* always call initgrabz */
640 /* only to detect delta motion */
641 void ED_view3d_win_to_delta(ARegion *ar, const float mval[2], float out[3])
642 {
643         RegionView3D *rv3d= ar->regiondata;
644         float dx, dy;
645         
646         dx= 2.0f*mval[0]*rv3d->zfac/ar->winx;
647         dy= 2.0f*mval[1]*rv3d->zfac/ar->winy;
648         
649         out[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy);
650         out[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy);
651         out[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy);
652 }
653
654 /* doesn't rely on initgrabz */
655 /* for perspective view, get the vector direction to
656  * the mouse cursor as a normalized vector */
657 void ED_view3d_win_to_vector(ARegion *ar, const float mval[2], float out[3])
658 {
659         RegionView3D *rv3d= ar->regiondata;
660
661         if(rv3d->is_persp) {
662                 out[0]= 2.0f * (mval[0] / ar->winx) - 1.0f;
663                 out[1]= 2.0f * (mval[1] / ar->winy) - 1.0f;
664                 out[2]= -0.5f;
665                 mul_project_m4_v3(rv3d->persinv, out);
666                 sub_v3_v3(out, rv3d->viewinv[3]);
667         }
668         else {
669                 copy_v3_v3(out, rv3d->viewinv[2]);
670         }
671         normalize_v3(out);
672 }
673
674 float ED_view3d_depth_read_cached(ViewContext *vc, int x, int y)
675 {
676         ViewDepths *vd = vc->rv3d->depths;
677                 
678         x -= vc->ar->winrct.xmin;
679         y -= vc->ar->winrct.ymin;
680
681         if(vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
682                 return vd->depths[y * vd->w + x];
683         else
684                 return 1;
685 }
686
687 void ED_view3d_depth_tag_update(RegionView3D *rv3d)
688 {
689         if(rv3d->depths)
690                 rv3d->depths->damaged= 1;
691 }
692
693 void ED_view3d_ob_project_mat_get(RegionView3D *rv3d, Object *ob, float pmat[4][4])
694 {
695         float vmat[4][4];
696         
697         mul_m4_m4m4(vmat, ob->obmat, rv3d->viewmat);
698         mul_m4_m4m4(pmat, vmat, rv3d->winmat);
699 }
700
701 #if 0
702 /* Uses window coordinates (x,y) and depth component z to find a point in
703    modelspace */
704 void view3d_unproject(bglMats *mats, float out[3], const short x, const short y, const float z)
705 {
706         double ux, uy, uz;
707
708                 gluUnProject(x,y,z, mats->modelview, mats->projection,
709                          (GLint *)mats->viewport, &ux, &uy, &uz );
710         out[0] = ux;
711         out[1] = uy;
712         out[2] = uz;
713 }
714 #endif
715
716 /* use above call to get projecting mat */
717 void ED_view3d_project_float(ARegion *ar, const float vec[3], float adr[2], float mat[4][4])
718 {
719         float vec4[4];
720         
721         adr[0]= IS_CLIPPED;
722         copy_v3_v3(vec4, vec);
723         vec4[3]= 1.0;
724         
725         mul_m4_v4(mat, vec4);
726         
727         if( vec4[3]>FLT_EPSILON ) {
728                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
729                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
730         } else {
731                 adr[0] = adr[1] = 0.0f;
732         }
733 }
734
735 int ED_view3d_boundbox_clip(RegionView3D *rv3d, float obmat[][4], BoundBox *bb)
736 {
737         /* return 1: draw */
738         
739         float mat[4][4];
740         float vec[4], min, max;
741         int a, flag= -1, fl;
742         
743         if(bb==NULL) return 1;
744         if(bb->flag & OB_BB_DISABLED) return 1;
745         
746         mul_m4_m4m4(mat, obmat, rv3d->persmat);
747         
748         for(a=0; a<8; a++) {
749                 copy_v3_v3(vec, bb->vec[a]);
750                 vec[3]= 1.0;
751                 mul_m4_v4(mat, vec);
752                 max= vec[3];
753                 min= -vec[3];
754                 
755                 fl= 0;
756                 if(vec[0] < min) fl+= 1;
757                 if(vec[0] > max) fl+= 2;
758                 if(vec[1] < min) fl+= 4;
759                 if(vec[1] > max) fl+= 8;
760                 if(vec[2] < min) fl+= 16;
761                 if(vec[2] > max) fl+= 32;
762                 
763                 flag &= fl;
764                 if(flag==0) return 1;
765         }
766         
767         return 0;
768 }
769
770 void project_short(ARegion *ar, const float vec[3], short adr[2])       /* clips */
771 {
772         RegionView3D *rv3d= ar->regiondata;
773         float fx, fy, vec4[4];
774         
775         adr[0]= IS_CLIPPED;
776         
777         if(rv3d->rflag & RV3D_CLIPPING) {
778                 if(ED_view3d_test_clipping(rv3d, vec, 0))
779                         return;
780         }
781         
782         copy_v3_v3(vec4, vec);
783         vec4[3]= 1.0;
784         mul_m4_v4(rv3d->persmat, vec4);
785         
786         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
787                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
788                 
789                 if( fx>0 && fx<ar->winx) {
790                         
791                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
792                         
793                         if(fy > 0.0f && fy < (float)ar->winy) {
794                                 adr[0]= (short)floor(fx); 
795                                 adr[1]= (short)floor(fy);
796                         }
797                 }
798         }
799 }
800
801 void project_int(ARegion *ar, const float vec[3], int adr[2])
802 {
803         RegionView3D *rv3d= ar->regiondata;
804         float fx, fy, vec4[4];
805         
806         adr[0]= (int)2140000000.0f;
807         copy_v3_v3(vec4, vec);
808         vec4[3]= 1.0;
809         
810         mul_m4_v4(rv3d->persmat, vec4);
811         
812         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
813                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
814                 
815                 if( fx>-2140000000.0f && fx<2140000000.0f) {
816                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
817                         
818                         if(fy>-2140000000.0f && fy<2140000000.0f) {
819                                 adr[0]= (int)floor(fx); 
820                                 adr[1]= (int)floor(fy);
821                         }
822                 }
823         }
824 }
825
826 void project_int_noclip(ARegion *ar, const float vec[3], int adr[2])
827 {
828         RegionView3D *rv3d= ar->regiondata;
829         float fx, fy, vec4[4];
830         
831         copy_v3_v3(vec4, vec);
832         vec4[3]= 1.0;
833         
834         mul_m4_v4(rv3d->persmat, vec4);
835         
836         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
837                 fx = (ar->winx/2)*(1 + vec4[0]/vec4[3]);
838                 fy = (ar->winy/2)*(1 + vec4[1]/vec4[3]);
839                 
840                 adr[0] = (int)floor(fx); 
841                 adr[1] = (int)floor(fy);
842         }
843         else
844         {
845                 adr[0] = ar->winx / 2;
846                 adr[1] = ar->winy / 2;
847         }
848 }
849
850 void project_short_noclip(ARegion *ar, const float vec[3], short adr[2])
851 {
852         RegionView3D *rv3d= ar->regiondata;
853         float fx, fy, vec4[4];
854         
855         adr[0]= IS_CLIPPED;
856         copy_v3_v3(vec4, vec);
857         vec4[3]= 1.0;
858         
859         mul_m4_v4(rv3d->persmat, vec4);
860         
861         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
862                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
863                 
864                 if( fx>-32700 && fx<32700) {
865                         
866                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
867                         
868                         if(fy > -32700.0f && fy < 32700.0f) {
869                                 adr[0]= (short)floor(fx); 
870                                 adr[1]= (short)floor(fy);
871                         }
872                 }
873         }
874 }
875
876 void project_float(ARegion *ar, const float vec[3], float adr[2])
877 {
878         RegionView3D *rv3d= ar->regiondata;
879         float vec4[4];
880         
881         adr[0]= IS_CLIPPED;
882         copy_v3_v3(vec4, vec);
883         vec4[3]= 1.0;
884         
885         mul_m4_v4(rv3d->persmat, vec4);
886         
887         if(vec4[3] > (float)BL_NEAR_CLIP) {
888                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
889                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
890         }
891 }
892
893 void project_float_noclip(ARegion *ar, const float vec[3], float adr[2])
894 {
895         RegionView3D *rv3d= ar->regiondata;
896         float vec4[4];
897         
898         copy_v3_v3(vec4, vec);
899         vec4[3]= 1.0;
900         
901         mul_m4_v4(rv3d->persmat, vec4);
902         
903         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
904                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
905                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
906         }
907         else
908         {
909                 adr[0] = ar->winx / 2.0f;
910                 adr[1] = ar->winy / 2.0f;
911         }
912 }
913
914 /* copies logic of get_view3d_viewplane(), keep in sync */
915 int ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
916 {
917         int orth= 0;
918
919         *clipsta= v3d->near;
920         *clipend= v3d->far;
921
922         if(rv3d->persp==RV3D_CAMOB) {
923                 if(v3d->camera) {
924                         if(v3d->camera->type==OB_LAMP ) {
925                                 Lamp *la= v3d->camera->data;
926                                 *clipsta= la->clipsta;
927                                 *clipend= la->clipend;
928                         }
929                         else if(v3d->camera->type==OB_CAMERA) {
930                                 Camera *cam= v3d->camera->data;
931                                 *clipsta= cam->clipsta;
932                                 *clipend= cam->clipend;
933
934                                 if(cam->type==CAM_ORTHO)
935                                         orth= 1;
936                         }
937                 }
938         }
939
940         if(rv3d->persp==RV3D_ORTHO) {
941                 *clipend *= 0.5f;       // otherwise too extreme low zbuffer quality
942                 *clipsta= - *clipend;
943                 orth= 1;
944         }
945
946         return orth;
947 }
948
949 /* also exposed in previewrender.c */
950 int ED_view3d_viewplane_get(View3D *v3d, RegionView3D *rv3d, int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend, float *pixsize)
951 {
952         Camera *cam=NULL;
953         float lens, fac, x1, y1, x2, y2;
954         float winx= (float)winxi, winy= (float)winyi;
955         int orth= 0;
956         
957         lens= v3d->lens;        
958         
959         *clipsta= v3d->near;
960         *clipend= v3d->far;
961         
962         if(rv3d->persp==RV3D_CAMOB) {
963                 if(v3d->camera) {
964                         if(v3d->camera->type==OB_LAMP ) {
965                                 Lamp *la;
966                                 
967                                 la= v3d->camera->data;
968                                 fac= cosf(((float)M_PI)*la->spotsize/360.0f);
969                                 
970                                 x1= saacos(fac);
971                                 lens= 16.0f*fac/sinf(x1);
972                                 
973                                 *clipsta= la->clipsta;
974                                 *clipend= la->clipend;
975                         }
976                         else if(v3d->camera->type==OB_CAMERA) {
977                                 cam= v3d->camera->data;
978                                 lens= cam->lens;
979                                 *clipsta= cam->clipsta;
980                                 *clipend= cam->clipend;
981                         }
982                 }
983         }
984         
985         if(rv3d->persp==RV3D_ORTHO) {
986                 if(winx>winy) x1= -rv3d->dist;
987                 else x1= -winx*rv3d->dist/winy;
988                 x2= -x1;
989                 
990                 if(winx>winy) y1= -winy*rv3d->dist/winx;
991                 else y1= -rv3d->dist;
992                 y2= -y1;
993                 
994                 *clipend *= 0.5f;       // otherwise too extreme low zbuffer quality
995                 *clipsta= - *clipend;
996                 orth= 1;
997         }
998         else {
999                 /* fac for zoom, also used for camdx */
1000                 if(rv3d->persp==RV3D_CAMOB) {
1001                         fac= BKE_screen_view3d_zoom_to_fac((float)rv3d->camzoom) * 4.0f;
1002                 }
1003                 else {
1004                         fac= 2.0;
1005                 }
1006                 
1007                 /* viewplane size depends... */
1008                 if(cam && cam->type==CAM_ORTHO) {
1009                         /* ortho_scale == 1 means exact 1 to 1 mapping */
1010                         float dfac= 2.0f*cam->ortho_scale/fac;
1011                         
1012                         if(winx>winy) x1= -dfac;
1013                         else x1= -winx*dfac/winy;
1014                         x2= -x1;
1015                         
1016                         if(winx>winy) y1= -winy*dfac/winx;
1017                         else y1= -dfac;
1018                         y2= -y1;
1019                         orth= 1;
1020                 }
1021                 else {
1022                         float dfac;
1023                         
1024                         if(winx>winy) dfac= 64.0f/(fac*winx*lens);
1025                         else dfac= 64.0f/(fac*winy*lens);
1026                         
1027                         x1= - *clipsta * winx*dfac;
1028                         x2= -x1;
1029                         y1= - *clipsta * winy*dfac;
1030                         y2= -y1;
1031                         orth= 0;
1032                 }
1033                 /* cam view offset */
1034                 if(cam) {
1035                         float dx= 0.5f*fac*rv3d->camdx*(x2-x1);
1036                         float dy= 0.5f*fac*rv3d->camdy*(y2-y1);
1037
1038                         /* shift offset */              
1039                         if(cam->type==CAM_ORTHO) {
1040                                 dx += cam->shiftx * cam->ortho_scale;
1041                                 dy += cam->shifty * cam->ortho_scale;
1042                         }
1043                         else {
1044                                 dx += cam->shiftx * (cam->clipsta / cam->lens) * 32.0f;
1045                                 dy += cam->shifty * (cam->clipsta / cam->lens) * 32.0f;
1046                         }
1047
1048                         x1+= dx;
1049                         x2+= dx;
1050                         y1+= dy;
1051                         y2+= dy;
1052                 }
1053         }
1054         
1055         if(pixsize) {
1056                 float viewfac;
1057                 
1058                 if(orth) {
1059                         viewfac= (winx >= winy)? winx: winy;
1060                         *pixsize= 1.0f/viewfac;
1061                 }
1062                 else {
1063                         viewfac= (((winx >= winy)? winx: winy)*lens)/32.0f;
1064                         *pixsize= *clipsta/viewfac;
1065                 }
1066         }
1067         
1068         viewplane->xmin= x1;
1069         viewplane->ymin= y1;
1070         viewplane->xmax= x2;
1071         viewplane->ymax= y2;
1072         
1073         return orth;
1074 }
1075
1076 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
1077 {
1078         RegionView3D *rv3d= ar->regiondata;
1079         rctf viewplane;
1080         float clipsta, clipend, x1, y1, x2, y2;
1081         int orth;
1082         
1083         orth= ED_view3d_viewplane_get(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
1084         rv3d->is_persp= !orth;
1085
1086         //      printf("%d %d %f %f %f %f %f %f\n", winx, winy, viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax, clipsta, clipend);
1087         x1= viewplane.xmin;
1088         y1= viewplane.ymin;
1089         x2= viewplane.xmax;
1090         y2= viewplane.ymax;
1091         
1092         if(rect) {              /* picking */
1093                 rect->xmin/= (float)ar->winx;
1094                 rect->xmin= x1+rect->xmin*(x2-x1);
1095                 rect->ymin/= (float)ar->winy;
1096                 rect->ymin= y1+rect->ymin*(y2-y1);
1097                 rect->xmax/= (float)ar->winx;
1098                 rect->xmax= x1+rect->xmax*(x2-x1);
1099                 rect->ymax/= (float)ar->winy;
1100                 rect->ymax= y1+rect->ymax*(y2-y1);
1101                 
1102                 if(orth) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
1103                 else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
1104                 
1105         }
1106         else {
1107                 if(orth) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
1108                 else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
1109         }
1110
1111         /* update matrix in 3d view region */
1112         glGetFloatv(GL_PROJECTION_MATRIX, (float*)rv3d->winmat);
1113 }
1114
1115 static void obmat_to_viewmat(View3D *v3d, RegionView3D *rv3d, Object *ob, short smooth)
1116 {
1117         float bmat[4][4];
1118         float tmat[3][3];
1119         
1120         rv3d->view= RV3D_VIEW_USER; /* dont show the grid */
1121         
1122         copy_m4_m4(bmat, ob->obmat);
1123         normalize_m4(bmat);
1124         invert_m4_m4(rv3d->viewmat, bmat);
1125         
1126         /* view quat calculation, needed for add object */
1127         copy_m3_m4(tmat, rv3d->viewmat);
1128         if (smooth) {
1129                 float new_quat[4];
1130                 if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1131                         /* were from a camera view */
1132                         
1133                         float orig_ofs[3];
1134                         float orig_dist= rv3d->dist;
1135                         float orig_lens= v3d->lens;
1136                         copy_v3_v3(orig_ofs, rv3d->ofs);
1137                         
1138                         /* Switch from camera view */
1139                         mat3_to_quat( new_quat,tmat);
1140                         
1141                         rv3d->persp=RV3D_PERSP;
1142                         rv3d->dist= 0.0;
1143                         
1144                         ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1145                         smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1146                         
1147                         rv3d->persp=RV3D_CAMOB; /* just to be polite, not needed */
1148                         
1149                 } else {
1150                         mat3_to_quat( new_quat,tmat);
1151                         smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1152                 }
1153         } else {
1154                 mat3_to_quat( rv3d->viewquat,tmat);
1155         }
1156 }
1157
1158 #define QUATSET(a, b, c, d, e)  a[0]=b; a[1]=c; a[2]=d; a[3]=e; 
1159
1160 int ED_view3d_lock(RegionView3D *rv3d)
1161 {
1162         switch(rv3d->view) {
1163         case RV3D_VIEW_BOTTOM :
1164                 QUATSET(rv3d->viewquat,0.0, -1.0, 0.0, 0.0);
1165                 break;
1166
1167         case RV3D_VIEW_BACK:
1168                 QUATSET(rv3d->viewquat,0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0));
1169                 break;
1170
1171         case RV3D_VIEW_LEFT:
1172                 QUATSET(rv3d->viewquat,0.5, -0.5, 0.5, 0.5);
1173                 break;
1174
1175         case RV3D_VIEW_TOP:
1176                 QUATSET(rv3d->viewquat,1.0, 0.0, 0.0, 0.0);
1177                 break;
1178
1179         case RV3D_VIEW_FRONT:
1180                 QUATSET(rv3d->viewquat,(float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0);
1181                 break;
1182
1183         case RV3D_VIEW_RIGHT:
1184                 QUATSET(rv3d->viewquat, 0.5, -0.5, -0.5, -0.5);
1185                 break;
1186         default:
1187                 return FALSE;
1188         }
1189
1190         return TRUE;
1191 }
1192
1193 /* dont set windows active in in here, is used by renderwin too */
1194 void setviewmatrixview3d(Scene *scene, View3D *v3d, RegionView3D *rv3d)
1195 {
1196         if(rv3d->persp==RV3D_CAMOB) {       /* obs/camera */
1197                 if(v3d->camera) {
1198                         where_is_object(scene, v3d->camera);    
1199                         obmat_to_viewmat(v3d, rv3d, v3d->camera, 0);
1200                 }
1201                 else {
1202                         quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1203                         rv3d->viewmat[3][2]-= rv3d->dist;
1204                 }
1205         }
1206         else {
1207                 /* should be moved to better initialize later on XXX */
1208                 if(rv3d->viewlock)
1209                         ED_view3d_lock(rv3d);
1210                 
1211                 quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1212                 if(rv3d->persp==RV3D_PERSP) rv3d->viewmat[3][2]-= rv3d->dist;
1213                 if(v3d->ob_centre) {
1214                         Object *ob= v3d->ob_centre;
1215                         float vec[3];
1216                         
1217                         copy_v3_v3(vec, ob->obmat[3]);
1218                         if(ob->type==OB_ARMATURE && v3d->ob_centre_bone[0]) {
1219                                 bPoseChannel *pchan= get_pose_channel(ob->pose, v3d->ob_centre_bone);
1220                                 if(pchan) {
1221                                         copy_v3_v3(vec, pchan->pose_mat[3]);
1222                                         mul_m4_v3(ob->obmat, vec);
1223                                 }
1224                         }
1225                         translate_m4( rv3d->viewmat,-vec[0], -vec[1], -vec[2]);
1226                 }
1227                 else if (v3d->ob_centre_cursor) {
1228                         float vec[3];
1229                         copy_v3_v3(vec, give_cursor(scene, v3d));
1230                         translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]);
1231                 }
1232                 else translate_m4( rv3d->viewmat,rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
1233         }
1234 }
1235
1236 /* IGLuint-> GLuint*/
1237 /* Warning: be sure to account for a negative return value
1238 *   This is an error, "Too many objects in select buffer"
1239 *   and no action should be taken (can crash blender) if this happens
1240 */
1241 short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
1242 {
1243         Scene *scene= vc->scene;
1244         View3D *v3d= vc->v3d;
1245         ARegion *ar= vc->ar;
1246         rctf rect;
1247         short code, hits;
1248         char dt, dtx;
1249         
1250         G.f |= G_PICKSEL;
1251         
1252         /* case not a border select */
1253         if(input->xmin==input->xmax) {
1254                 rect.xmin= input->xmin-12;      // seems to be default value for bones only now
1255                 rect.xmax= input->xmin+12;
1256                 rect.ymin= input->ymin-12;
1257                 rect.ymax= input->ymin+12;
1258         }
1259         else {
1260                 rect.xmin= input->xmin;
1261                 rect.xmax= input->xmax;
1262                 rect.ymin= input->ymin;
1263                 rect.ymax= input->ymax;
1264         }
1265         
1266         setwinmatrixview3d(ar, v3d, &rect);
1267         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1268         
1269         if(v3d->drawtype > OB_WIRE) {
1270                 v3d->zbuf= TRUE;
1271                 glEnable(GL_DEPTH_TEST);
1272         }
1273         
1274         if(vc->rv3d->rflag & RV3D_CLIPPING)
1275                 view3d_set_clipping(vc->rv3d);
1276         
1277         glSelectBuffer( bufsize, (GLuint *)buffer);
1278         glRenderMode(GL_SELECT);
1279         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1280         glPushName(-1);
1281         code= 1;
1282         
1283         if(vc->obedit && vc->obedit->type==OB_MBALL) {
1284                 draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1285         }
1286         else if((vc->obedit && vc->obedit->type==OB_ARMATURE)) {
1287                 /* if not drawing sketch, draw bones */
1288                 if(!BDR_drawSketchNames(vc)) {
1289                         draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1290                 }
1291         }
1292         else {
1293                 Base *base;
1294                 
1295                 v3d->xray= TRUE;        // otherwise it postpones drawing
1296                 for(base= scene->base.first; base; base= base->next) {
1297                         if(base->lay & v3d->lay) {
1298                                 
1299                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1300                                         base->selcol= 0;
1301                                 else {
1302                                         base->selcol= code;
1303                                         glLoadName(code);
1304                                         draw_object(scene, ar, v3d, base, DRAW_PICKING|DRAW_CONSTCOLOR);
1305                                         
1306                                         /* we draw group-duplicators for selection too */
1307                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1308                                                 ListBase *lb;
1309                                                 DupliObject *dob;
1310                                                 Base tbase;
1311                                                 
1312                                                 tbase.flag= OB_FROMDUPLI;
1313                                                 lb= object_duplilist(scene, base->object);
1314                                                 
1315                                                 for(dob= lb->first; dob; dob= dob->next) {
1316                                                         tbase.object= dob->ob;
1317                                                         copy_m4_m4(dob->ob->obmat, dob->mat);
1318                                                         
1319                                                         /* extra service: draw the duplicator in drawtype of parent */
1320                                                         /* MIN2 for the drawtype to allow bounding box objects in groups for lods */
1321                                                         dt= tbase.object->dt;   tbase.object->dt= MIN2(tbase.object->dt, base->object->dt);
1322                                                         dtx= tbase.object->dtx; tbase.object->dtx= base->object->dtx;
1323
1324                                                         draw_object(scene, ar, v3d, &tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1325                                                         
1326                                                         tbase.object->dt= dt;
1327                                                         tbase.object->dtx= dtx;
1328
1329                                                         copy_m4_m4(dob->ob->obmat, dob->omat);
1330                                                 }
1331                                                 free_object_duplilist(lb);
1332                                         }
1333                                         code++;
1334                                 }                               
1335                         }
1336                 }
1337                 v3d->xray= FALSE;       // restore
1338         }
1339         
1340         glPopName();    /* see above (pushname) */
1341         hits= glRenderMode(GL_RENDER);
1342         
1343         G.f &= ~G_PICKSEL;
1344         setwinmatrixview3d(ar, v3d, NULL);
1345         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1346         
1347         if(v3d->drawtype > OB_WIRE) {
1348                 v3d->zbuf= 0;
1349                 glDisable(GL_DEPTH_TEST);
1350         }
1351 // XXX  persp(PERSP_WIN);
1352         
1353         if(vc->rv3d->rflag & RV3D_CLIPPING)
1354                 view3d_clr_clipping();
1355         
1356         if(hits<0) printf("Too many objects in select buffer\n");       // XXX make error message
1357         
1358         return hits;
1359 }
1360
1361 /* ********************** local view operator ******************** */
1362
1363 static unsigned int free_localbit(Main *bmain)
1364 {
1365         unsigned int lay;
1366         ScrArea *sa;
1367         bScreen *sc;
1368         
1369         lay= 0;
1370         
1371         /* sometimes we loose a localview: when an area is closed */
1372         /* check all areas: which localviews are in use? */
1373         for(sc= bmain->screen.first; sc; sc= sc->id.next) {
1374                 for(sa= sc->areabase.first; sa; sa= sa->next) {
1375                         SpaceLink *sl= sa->spacedata.first;
1376                         for(; sl; sl= sl->next) {
1377                                 if(sl->spacetype==SPACE_VIEW3D) {
1378                                         View3D *v3d= (View3D*) sl;
1379                                         lay |= v3d->lay;
1380                                 }
1381                         }
1382                 }
1383         }
1384         
1385         if( (lay & 0x01000000)==0) return 0x01000000;
1386         if( (lay & 0x02000000)==0) return 0x02000000;
1387         if( (lay & 0x04000000)==0) return 0x04000000;
1388         if( (lay & 0x08000000)==0) return 0x08000000;
1389         if( (lay & 0x10000000)==0) return 0x10000000;
1390         if( (lay & 0x20000000)==0) return 0x20000000;
1391         if( (lay & 0x40000000)==0) return 0x40000000;
1392         if( (lay & 0x80000000)==0) return 0x80000000;
1393         
1394         return 0;
1395 }
1396
1397 int ED_view3d_scene_layer_set(int lay, const int *values, int *active)
1398 {
1399         int i, tot= 0;
1400         
1401         /* ensure we always have some layer selected */
1402         for(i=0; i<20; i++)
1403                 if(values[i])
1404                         tot++;
1405         
1406         if(tot==0)
1407                 return lay;
1408         
1409         for(i=0; i<20; i++) {
1410                 
1411                 if (active) {
1412                         /* if this value has just been switched on, make that layer active */
1413                         if (values[i] && (lay & (1<<i))==0) {
1414                                 *active = (1<<i);
1415                         }
1416                 }
1417                         
1418                 if (values[i]) lay |= (1<<i);
1419                 else lay &= ~(1<<i);
1420         }
1421         
1422         /* ensure always an active layer */
1423         if (active && (lay & *active)==0) {
1424                 for(i=0; i<20; i++) {
1425                         if(lay & (1<<i)) {
1426                                 *active= 1<<i;
1427                                 break;
1428                         }
1429                 }
1430         }
1431         
1432         return lay;
1433 }
1434
1435 static void initlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1436 {
1437         View3D *v3d= sa->spacedata.first;
1438         Base *base;
1439         float size = 0.0, min[3], max[3], box[3];
1440         unsigned int locallay;
1441         int ok=0;
1442
1443         if(v3d->localvd) return;
1444
1445         INIT_MINMAX(min, max);
1446
1447         locallay= free_localbit(bmain);
1448
1449         if(locallay==0) {
1450                 printf("Sorry, no more than 8 localviews\n");   // XXX error 
1451                 ok= 0;
1452         }
1453         else {
1454                 if(scene->obedit) {
1455                         minmax_object(scene->obedit, min, max);
1456                         
1457                         ok= 1;
1458                 
1459                         BASACT->lay |= locallay;
1460                         scene->obedit->lay= BASACT->lay;
1461                 }
1462                 else {
1463                         for(base= FIRSTBASE; base; base= base->next) {
1464                                 if(TESTBASE(v3d, base))  {
1465                                         minmax_object(base->object, min, max);
1466                                         base->lay |= locallay;
1467                                         base->object->lay= base->lay;
1468                                         ok= 1;
1469                                 }
1470                         }
1471                 }
1472                 
1473                 box[0]= (max[0]-min[0]);
1474                 box[1]= (max[1]-min[1]);
1475                 box[2]= (max[2]-min[2]);
1476                 size= MAX3(box[0], box[1], box[2]);
1477                 if(size <= 0.01f) size= 0.01f;
1478         }
1479         
1480         if(ok) {
1481                 ARegion *ar;
1482                 
1483                 v3d->localvd= MEM_mallocN(sizeof(View3D), "localview");
1484                 
1485                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1486
1487                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
1488                         if(ar->regiontype == RGN_TYPE_WINDOW) {
1489                                 RegionView3D *rv3d= ar->regiondata;
1490
1491                                 rv3d->localvd= MEM_mallocN(sizeof(RegionView3D), "localview region");
1492                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1493                                 
1494                                 rv3d->ofs[0]= -(min[0]+max[0])/2.0f;
1495                                 rv3d->ofs[1]= -(min[1]+max[1])/2.0f;
1496                                 rv3d->ofs[2]= -(min[2]+max[2])/2.0f;
1497
1498                                 rv3d->dist= size;
1499                                 /* perspective should be a bit farther away to look nice */
1500                                 if(rv3d->persp==RV3D_ORTHO)
1501                                         rv3d->dist*= 0.7f;
1502
1503                                 // correction for window aspect ratio
1504                                 if(ar->winy>2 && ar->winx>2) {
1505                                         float asp= (float)ar->winx/(float)ar->winy;
1506                                         if(asp < 1.0f) asp= 1.0f/asp;
1507                                         rv3d->dist*= asp;
1508                                 }
1509                                 
1510                                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP;
1511                                 
1512                                 v3d->cursor[0]= -rv3d->ofs[0];
1513                                 v3d->cursor[1]= -rv3d->ofs[1];
1514                                 v3d->cursor[2]= -rv3d->ofs[2];
1515                         }
1516                 }
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(Main *bmain, 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                 DAG_on_visible_update(bmain, FALSE);
1602         } 
1603 }
1604
1605 static int localview_exec(bContext *C, wmOperator *UNUSED(unused))
1606 {
1607         View3D *v3d= CTX_wm_view3d(C);
1608         
1609         if(v3d->localvd)
1610                 endlocalview(CTX_data_main(C), CTX_data_scene(C), CTX_wm_area(C));
1611         else
1612                 initlocalview(CTX_data_main(C), CTX_data_scene(C), CTX_wm_area(C));
1613         
1614         ED_area_tag_redraw(CTX_wm_area(C));
1615         
1616         return OPERATOR_FINISHED;
1617 }
1618
1619 void VIEW3D_OT_localview(wmOperatorType *ot)
1620 {
1621         
1622         /* identifiers */
1623         ot->name= "Local View";
1624         ot->description= "Toggle display of selected object(s) separately and centered in view";
1625         ot->idname= "VIEW3D_OT_localview";
1626         
1627         /* api callbacks */
1628         ot->exec= localview_exec;
1629         ot->flag= OPTYPE_UNDO; /* localview changes object layer bitflags */
1630         
1631         ot->poll= ED_operator_view3d_active;
1632 }
1633
1634 #ifdef WITH_GAMEENGINE
1635
1636 static ListBase queue_back;
1637 static void SaveState(bContext *C, wmWindow *win)
1638 {
1639         Object *obact = CTX_data_active_object(C);
1640         
1641         glPushAttrib(GL_ALL_ATTRIB_BITS);
1642
1643         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1644                 GPU_paint_set_mipmap(1);
1645         
1646         queue_back= win->queue;
1647         
1648         win->queue.first= win->queue.last= NULL;
1649         
1650         //XXX waitcursor(1);
1651 }
1652
1653 static void RestoreState(bContext *C, wmWindow *win)
1654 {
1655         Object *obact = CTX_data_active_object(C);
1656         
1657         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1658                 GPU_paint_set_mipmap(0);
1659
1660         //XXX curarea->win_swap = 0;
1661         //XXX curarea->head_swap=0;
1662         //XXX allqueue(REDRAWVIEW3D, 1);
1663         //XXX allqueue(REDRAWBUTSALL, 0);
1664         //XXX reset_slowparents();
1665         //XXX waitcursor(0);
1666         //XXX G.qual= 0;
1667         
1668         if(win) /* check because closing win can set to NULL */
1669                 win->queue= queue_back;
1670         
1671         GPU_state_init();
1672         GPU_set_tpage(NULL, 0);
1673
1674         glPopAttrib();
1675 }
1676
1677 /* was space_set_commmandline_options in 2.4x */
1678 static void game_set_commmandline_options(GameData *gm)
1679 {
1680         SYS_SystemHandle syshandle;
1681         int test;
1682
1683         if ( (syshandle = SYS_GetSystem()) ) {
1684                 /* User defined settings */
1685                 test= (U.gameflags & USER_DISABLE_MIPMAP);
1686                 GPU_set_mipmap(!test);
1687                 SYS_WriteCommandLineInt(syshandle, "nomipmap", test);
1688
1689                 /* File specific settings: */
1690                 /* Only test the first one. These two are switched
1691                  * simultaneously. */
1692                 test= (gm->flag & GAME_SHOW_FRAMERATE);
1693                 SYS_WriteCommandLineInt(syshandle, "show_framerate", test);
1694                 SYS_WriteCommandLineInt(syshandle, "show_profile", test);
1695
1696                 test = (gm->flag & GAME_SHOW_DEBUG_PROPS);
1697                 SYS_WriteCommandLineInt(syshandle, "show_properties", test);
1698
1699                 test= (gm->flag & GAME_SHOW_PHYSICS);
1700                 SYS_WriteCommandLineInt(syshandle, "show_physics", test);
1701
1702                 test= (gm->flag & GAME_ENABLE_ALL_FRAMES);
1703                 SYS_WriteCommandLineInt(syshandle, "fixedtime", test);
1704
1705                 test= (gm->flag & GAME_ENABLE_ANIMATION_RECORD);
1706                 SYS_WriteCommandLineInt(syshandle, "animation_record", test);
1707
1708                 test= (gm->flag & GAME_IGNORE_DEPRECATION_WARNINGS);
1709                 SYS_WriteCommandLineInt(syshandle, "ignore_deprecation_warnings", test);
1710
1711                 test= (gm->matmode == GAME_MAT_MULTITEX);
1712                 SYS_WriteCommandLineInt(syshandle, "blender_material", test);
1713                 test= (gm->matmode == GAME_MAT_GLSL);
1714                 SYS_WriteCommandLineInt(syshandle, "blender_glsl_material", test);
1715                 test= (gm->flag & GAME_DISPLAY_LISTS);
1716                 SYS_WriteCommandLineInt(syshandle, "displaylists", test);
1717
1718
1719         }
1720 }
1721
1722 #endif // WITH_GAMEENGINE
1723
1724 static int game_engine_poll(bContext *C)
1725 {
1726         /* we need a context and area to launch BGE
1727         it's a temporary solution to avoid crash at load time
1728         if we try to auto run the BGE. Ideally we want the
1729         context to be set as soon as we load the file. */
1730
1731         if(CTX_wm_window(C)==NULL) return 0;
1732         if(CTX_wm_screen(C)==NULL) return 0;
1733         if(CTX_wm_area(C)==NULL) return 0;
1734
1735         if(CTX_data_mode_enum(C)!=CTX_MODE_OBJECT)
1736                 return 0;
1737
1738         return 1;
1739 }
1740
1741 int ED_view3d_context_activate(bContext *C)
1742 {
1743         bScreen *sc= CTX_wm_screen(C);
1744         ScrArea *sa= CTX_wm_area(C);
1745         ARegion *ar;
1746
1747         /* sa can be NULL when called from python */
1748         if(sa==NULL || sa->spacetype != SPACE_VIEW3D)
1749                 for(sa=sc->areabase.first; sa; sa= sa->next)
1750                         if(sa->spacetype==SPACE_VIEW3D)
1751                                 break;
1752
1753         if(!sa)
1754                 return 0;
1755         
1756         for(ar=sa->regionbase.first; ar; ar=ar->next)
1757                 if(ar->regiontype == RGN_TYPE_WINDOW)
1758                         break;
1759         
1760         if(!ar)
1761                 return 0;
1762         
1763         // bad context switch ..
1764         CTX_wm_area_set(C, sa);
1765         CTX_wm_region_set(C, ar);
1766
1767         return 1;
1768 }
1769
1770 static int game_engine_exec(bContext *C, wmOperator *op)
1771 {
1772 #ifdef WITH_GAMEENGINE
1773         Scene *startscene = CTX_data_scene(C);
1774         ScrArea *sa, *prevsa= CTX_wm_area(C);
1775         ARegion *ar, *prevar= CTX_wm_region(C);
1776         wmWindow *prevwin= CTX_wm_window(C);
1777         RegionView3D *rv3d;
1778         rcti cam_frame;
1779
1780         (void)op; /* unused */
1781         
1782         // bad context switch ..
1783         if(!ED_view3d_context_activate(C))
1784                 return OPERATOR_CANCELLED;
1785         
1786         rv3d= CTX_wm_region_view3d(C);
1787         sa= CTX_wm_area(C);
1788         ar= CTX_wm_region(C);
1789
1790         view3d_operator_needs_opengl(C);
1791         
1792         game_set_commmandline_options(&startscene->gm);
1793
1794         if(rv3d->persp==RV3D_CAMOB && startscene->gm.framing.type == SCE_GAMEFRAMING_BARS && startscene->gm.stereoflag != STEREO_DOME) { /* Letterbox */
1795                 rctf cam_framef;
1796                 ED_view3d_calc_camera_border(startscene, ar, CTX_wm_view3d(C), rv3d, &cam_framef, FALSE);
1797                 cam_frame.xmin = cam_framef.xmin + ar->winrct.xmin;
1798                 cam_frame.xmax = cam_framef.xmax + ar->winrct.xmin;
1799                 cam_frame.ymin = cam_framef.ymin + ar->winrct.ymin;
1800                 cam_frame.ymax = cam_framef.ymax + ar->winrct.ymin;
1801                 BLI_isect_rcti(&ar->winrct, &cam_frame, &cam_frame);
1802         }
1803         else {
1804                 cam_frame.xmin = ar->winrct.xmin;
1805                 cam_frame.xmax = ar->winrct.xmax;
1806                 cam_frame.ymin = ar->winrct.ymin;
1807                 cam_frame.ymax = ar->winrct.ymax;
1808         }
1809
1810
1811         SaveState(C, prevwin);
1812
1813         StartKetsjiShell(C, ar, &cam_frame, 1);
1814
1815         /* window wasnt closed while the BGE was running */
1816         if(BLI_findindex(&CTX_wm_manager(C)->windows, prevwin) == -1) {
1817                 prevwin= NULL;
1818                 CTX_wm_window_set(C, NULL);
1819         }
1820         
1821         if(prevwin) {
1822                 /* restore context, in case it changed in the meantime, for
1823                    example by working in another window or closing it */
1824                 CTX_wm_region_set(C, prevar);
1825                 CTX_wm_window_set(C, prevwin);
1826                 CTX_wm_area_set(C, prevsa);
1827         }
1828
1829         RestoreState(C, prevwin);
1830
1831         //XXX restore_all_scene_cfra(scene_cfra_store);
1832         set_scene_bg(CTX_data_main(C), startscene);
1833         //XXX scene_update_for_newframe(bmain, scene, scene->lay);
1834         
1835         ED_area_tag_redraw(CTX_wm_area(C));
1836
1837         return OPERATOR_FINISHED;
1838 #else
1839         (void)C; /* unused */
1840         BKE_report(op->reports, RPT_ERROR, "Game engine is disabled in this build.");
1841         return OPERATOR_CANCELLED;
1842 #endif
1843 }
1844
1845 void VIEW3D_OT_game_start(wmOperatorType *ot)
1846 {
1847         
1848         /* identifiers */
1849         ot->name= "Start Game Engine";
1850         ot->description= "Start game engine";
1851         ot->idname= "VIEW3D_OT_game_start";
1852         
1853         /* api callbacks */
1854         ot->exec= game_engine_exec;
1855         
1856         ot->poll= game_engine_poll;
1857 }
1858
1859 /* ************************************** */
1860
1861 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
1862 {
1863         float alignaxis[3] = {0.0, 0.0, 0.0};
1864         float norm[3], axis[3], angle, new_quat[4];
1865         
1866         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
1867         else alignaxis[-axisidx-1]= -1.0;
1868
1869         normalize_v3_v3(norm, vec);
1870
1871         angle= (float)acos(dot_v3v3(alignaxis, norm));
1872         cross_v3_v3v3(axis, alignaxis, norm);
1873         axis_angle_to_quat( new_quat,axis, -angle);
1874         
1875         rv3d->view= RV3D_VIEW_USER;
1876         
1877         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1878                 /* switch out of camera view */
1879                 float orig_ofs[3];
1880                 float orig_dist= rv3d->dist;
1881                 float orig_lens= v3d->lens;
1882                 
1883                 copy_v3_v3(orig_ofs, rv3d->ofs);
1884                 rv3d->persp= RV3D_PERSP;
1885                 rv3d->dist= 0.0;
1886                 ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1887                 smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1888         } else {
1889                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP; /* switch out of camera mode */
1890                 smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1891         }
1892 }
1893
1894 float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3])
1895 {
1896         return  (rv3d->persmat[3][3] + (
1897                                 rv3d->persmat[0][3]*co[0] +
1898                                 rv3d->persmat[1][3]*co[1] +
1899                                 rv3d->persmat[2][3]*co[2])
1900                         ) * rv3d->pixsize;
1901 }