44ae6837aa2ab7c8f3cd9f4b75e986a5c6f916e8
[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                 adr[0] = ar->winx / 2;
845                 adr[1] = ar->winy / 2;
846         }
847 }
848
849 void project_short_noclip(ARegion *ar, const float vec[3], short adr[2])
850 {
851         RegionView3D *rv3d= ar->regiondata;
852         float fx, fy, vec4[4];
853         
854         adr[0]= IS_CLIPPED;
855         copy_v3_v3(vec4, vec);
856         vec4[3]= 1.0;
857         
858         mul_m4_v4(rv3d->persmat, vec4);
859         
860         if( vec4[3] > (float)BL_NEAR_CLIP ) {   /* 0.001 is the NEAR clipping cutoff for picking */
861                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
862                 
863                 if( fx>-32700 && fx<32700) {
864                         
865                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
866                         
867                         if(fy > -32700.0f && fy < 32700.0f) {
868                                 adr[0]= (short)floor(fx); 
869                                 adr[1]= (short)floor(fy);
870                         }
871                 }
872         }
873 }
874
875 void project_float(ARegion *ar, const float vec[3], float adr[2])
876 {
877         RegionView3D *rv3d= ar->regiondata;
878         float vec4[4];
879         
880         adr[0]= IS_CLIPPED;
881         copy_v3_v3(vec4, vec);
882         vec4[3]= 1.0;
883         
884         mul_m4_v4(rv3d->persmat, vec4);
885         
886         if(vec4[3] > (float)BL_NEAR_CLIP) {
887                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
888                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
889         }
890 }
891
892 void project_float_noclip(ARegion *ar, const float vec[3], float adr[2])
893 {
894         RegionView3D *rv3d= ar->regiondata;
895         float vec4[4];
896         
897         copy_v3_v3(vec4, vec);
898         vec4[3]= 1.0;
899         
900         mul_m4_v4(rv3d->persmat, vec4);
901         
902         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
903                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];
904                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
905         }
906         else {
907                 adr[0] = ar->winx / 2.0f;
908                 adr[1] = ar->winy / 2.0f;
909         }
910 }
911
912 /* copies logic of get_view3d_viewplane(), keep in sync */
913 int ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend)
914 {
915         int orth= 0;
916
917         *clipsta= v3d->near;
918         *clipend= v3d->far;
919
920         if(rv3d->persp==RV3D_CAMOB) {
921                 if(v3d->camera) {
922                         if(v3d->camera->type==OB_LAMP ) {
923                                 Lamp *la= v3d->camera->data;
924                                 *clipsta= la->clipsta;
925                                 *clipend= la->clipend;
926                         }
927                         else if(v3d->camera->type==OB_CAMERA) {
928                                 Camera *cam= v3d->camera->data;
929                                 *clipsta= cam->clipsta;
930                                 *clipend= cam->clipend;
931
932                                 if(cam->type==CAM_ORTHO)
933                                         orth= 1;
934                         }
935                 }
936         }
937
938         if(rv3d->persp==RV3D_ORTHO) {
939                 *clipend *= 0.5f;       // otherwise too extreme low zbuffer quality
940                 *clipsta= - *clipend;
941                 orth= 1;
942         }
943
944         return orth;
945 }
946
947 /* also exposed in previewrender.c */
948 int ED_view3d_viewplane_get(View3D *v3d, RegionView3D *rv3d, int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend, float *pixsize)
949 {
950         Camera *cam=NULL;
951         float lens, fac, x1, y1, x2, y2;
952         float winx= (float)winxi, winy= (float)winyi;
953         int orth= 0;
954         
955         lens= v3d->lens;        
956         
957         *clipsta= v3d->near;
958         *clipend= v3d->far;
959         
960         if(rv3d->persp==RV3D_CAMOB) {
961                 if(v3d->camera) {
962                         if(v3d->camera->type==OB_LAMP ) {
963                                 Lamp *la;
964                                 
965                                 la= v3d->camera->data;
966                                 fac= cosf(((float)M_PI)*la->spotsize/360.0f);
967                                 
968                                 x1= saacos(fac);
969                                 lens= 16.0f*fac/sinf(x1);
970                                 
971                                 *clipsta= la->clipsta;
972                                 *clipend= la->clipend;
973                         }
974                         else if(v3d->camera->type==OB_CAMERA) {
975                                 cam= v3d->camera->data;
976                                 lens= cam->lens;
977                                 *clipsta= cam->clipsta;
978                                 *clipend= cam->clipend;
979                         }
980                 }
981         }
982         
983         if(rv3d->persp==RV3D_ORTHO) {
984                 if(winx>winy) x1= -rv3d->dist;
985                 else x1= -winx*rv3d->dist/winy;
986                 x2= -x1;
987                 
988                 if(winx>winy) y1= -winy*rv3d->dist/winx;
989                 else y1= -rv3d->dist;
990                 y2= -y1;
991                 
992                 *clipend *= 0.5f;       // otherwise too extreme low zbuffer quality
993                 *clipsta= - *clipend;
994                 orth= 1;
995         }
996         else {
997                 /* fac for zoom, also used for camdx */
998                 if(rv3d->persp==RV3D_CAMOB) {
999                         fac= BKE_screen_view3d_zoom_to_fac((float)rv3d->camzoom) * 4.0f;
1000                 }
1001                 else {
1002                         fac= 2.0;
1003                 }
1004                 
1005                 /* viewplane size depends... */
1006                 if(cam && cam->type==CAM_ORTHO) {
1007                         /* ortho_scale == 1 means exact 1 to 1 mapping */
1008                         float dfac= 2.0f*cam->ortho_scale/fac;
1009                         
1010                         if(winx>winy) x1= -dfac;
1011                         else x1= -winx*dfac/winy;
1012                         x2= -x1;
1013                         
1014                         if(winx>winy) y1= -winy*dfac/winx;
1015                         else y1= -dfac;
1016                         y2= -y1;
1017                         orth= 1;
1018                 }
1019                 else {
1020                         float dfac;
1021                         
1022                         if(winx>winy) dfac= 64.0f/(fac*winx*lens);
1023                         else dfac= 64.0f/(fac*winy*lens);
1024                         
1025                         x1= - *clipsta * winx*dfac;
1026                         x2= -x1;
1027                         y1= - *clipsta * winy*dfac;
1028                         y2= -y1;
1029                         orth= 0;
1030                 }
1031                 /* cam view offset */
1032                 if(cam) {
1033                         float dx= 0.5f*fac*rv3d->camdx*(x2-x1);
1034                         float dy= 0.5f*fac*rv3d->camdy*(y2-y1);
1035
1036                         /* shift offset */              
1037                         if(cam->type==CAM_ORTHO) {
1038                                 dx += cam->shiftx * cam->ortho_scale;
1039                                 dy += cam->shifty * cam->ortho_scale;
1040                         }
1041                         else {
1042                                 dx += cam->shiftx * (cam->clipsta / cam->lens) * 32.0f;
1043                                 dy += cam->shifty * (cam->clipsta / cam->lens) * 32.0f;
1044                         }
1045
1046                         x1+= dx;
1047                         x2+= dx;
1048                         y1+= dy;
1049                         y2+= dy;
1050                 }
1051         }
1052         
1053         if(pixsize) {
1054                 float viewfac;
1055                 
1056                 if(orth) {
1057                         viewfac= (winx >= winy)? winx: winy;
1058                         *pixsize= 1.0f/viewfac;
1059                 }
1060                 else {
1061                         viewfac= (((winx >= winy)? winx: winy)*lens)/32.0f;
1062                         *pixsize= *clipsta/viewfac;
1063                 }
1064         }
1065         
1066         viewplane->xmin= x1;
1067         viewplane->ymin= y1;
1068         viewplane->xmax= x2;
1069         viewplane->ymax= y2;
1070         
1071         return orth;
1072 }
1073
1074 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
1075 {
1076         RegionView3D *rv3d= ar->regiondata;
1077         rctf viewplane;
1078         float clipsta, clipend, x1, y1, x2, y2;
1079         int orth;
1080         
1081         orth= ED_view3d_viewplane_get(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
1082         rv3d->is_persp= !orth;
1083
1084         //      printf("%d %d %f %f %f %f %f %f\n", winx, winy, viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax, clipsta, clipend);
1085         x1= viewplane.xmin;
1086         y1= viewplane.ymin;
1087         x2= viewplane.xmax;
1088         y2= viewplane.ymax;
1089         
1090         if(rect) {              /* picking */
1091                 rect->xmin/= (float)ar->winx;
1092                 rect->xmin= x1+rect->xmin*(x2-x1);
1093                 rect->ymin/= (float)ar->winy;
1094                 rect->ymin= y1+rect->ymin*(y2-y1);
1095                 rect->xmax/= (float)ar->winx;
1096                 rect->xmax= x1+rect->xmax*(x2-x1);
1097                 rect->ymax/= (float)ar->winy;
1098                 rect->ymax= y1+rect->ymax*(y2-y1);
1099                 
1100                 if(orth) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
1101                 else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
1102                 
1103         }
1104         else {
1105                 if(orth) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
1106                 else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
1107         }
1108
1109         /* update matrix in 3d view region */
1110         glGetFloatv(GL_PROJECTION_MATRIX, (float*)rv3d->winmat);
1111 }
1112
1113 static void obmat_to_viewmat(View3D *v3d, RegionView3D *rv3d, Object *ob, short smooth)
1114 {
1115         float bmat[4][4];
1116         float tmat[3][3];
1117         
1118         rv3d->view= RV3D_VIEW_USER; /* dont show the grid */
1119         
1120         copy_m4_m4(bmat, ob->obmat);
1121         normalize_m4(bmat);
1122         invert_m4_m4(rv3d->viewmat, bmat);
1123         
1124         /* view quat calculation, needed for add object */
1125         copy_m3_m4(tmat, rv3d->viewmat);
1126         if (smooth) {
1127                 float new_quat[4];
1128                 if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1129                         /* were from a camera view */
1130                         
1131                         float orig_ofs[3];
1132                         float orig_dist= rv3d->dist;
1133                         float orig_lens= v3d->lens;
1134                         copy_v3_v3(orig_ofs, rv3d->ofs);
1135                         
1136                         /* Switch from camera view */
1137                         mat3_to_quat( new_quat,tmat);
1138                         
1139                         rv3d->persp=RV3D_PERSP;
1140                         rv3d->dist= 0.0;
1141                         
1142                         ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1143                         smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1144                         
1145                         rv3d->persp=RV3D_CAMOB; /* just to be polite, not needed */
1146                         
1147                 } else {
1148                         mat3_to_quat( new_quat,tmat);
1149                         smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1150                 }
1151         } else {
1152                 mat3_to_quat( rv3d->viewquat,tmat);
1153         }
1154 }
1155
1156 #define QUATSET(a, b, c, d, e)  a[0]=b; a[1]=c; a[2]=d; a[3]=e; 
1157
1158 int ED_view3d_lock(RegionView3D *rv3d)
1159 {
1160         switch(rv3d->view) {
1161         case RV3D_VIEW_BOTTOM :
1162                 QUATSET(rv3d->viewquat,0.0, -1.0, 0.0, 0.0);
1163                 break;
1164
1165         case RV3D_VIEW_BACK:
1166                 QUATSET(rv3d->viewquat,0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0));
1167                 break;
1168
1169         case RV3D_VIEW_LEFT:
1170                 QUATSET(rv3d->viewquat,0.5, -0.5, 0.5, 0.5);
1171                 break;
1172
1173         case RV3D_VIEW_TOP:
1174                 QUATSET(rv3d->viewquat,1.0, 0.0, 0.0, 0.0);
1175                 break;
1176
1177         case RV3D_VIEW_FRONT:
1178                 QUATSET(rv3d->viewquat,(float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0);
1179                 break;
1180
1181         case RV3D_VIEW_RIGHT:
1182                 QUATSET(rv3d->viewquat, 0.5, -0.5, -0.5, -0.5);
1183                 break;
1184         default:
1185                 return FALSE;
1186         }
1187
1188         return TRUE;
1189 }
1190
1191 /* dont set windows active in here, is used by renderwin too */
1192 void setviewmatrixview3d(Scene *scene, View3D *v3d, RegionView3D *rv3d)
1193 {
1194         if(rv3d->persp==RV3D_CAMOB) {       /* obs/camera */
1195                 if(v3d->camera) {
1196                         where_is_object(scene, v3d->camera);    
1197                         obmat_to_viewmat(v3d, rv3d, v3d->camera, 0);
1198                 }
1199                 else {
1200                         quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1201                         rv3d->viewmat[3][2]-= rv3d->dist;
1202                 }
1203         }
1204         else {
1205                 /* should be moved to better initialize later on XXX */
1206                 if(rv3d->viewlock)
1207                         ED_view3d_lock(rv3d);
1208                 
1209                 quat_to_mat4( rv3d->viewmat,rv3d->viewquat);
1210                 if(rv3d->persp==RV3D_PERSP) rv3d->viewmat[3][2]-= rv3d->dist;
1211                 if(v3d->ob_centre) {
1212                         Object *ob= v3d->ob_centre;
1213                         float vec[3];
1214                         
1215                         copy_v3_v3(vec, ob->obmat[3]);
1216                         if(ob->type==OB_ARMATURE && v3d->ob_centre_bone[0]) {
1217                                 bPoseChannel *pchan= get_pose_channel(ob->pose, v3d->ob_centre_bone);
1218                                 if(pchan) {
1219                                         copy_v3_v3(vec, pchan->pose_mat[3]);
1220                                         mul_m4_v3(ob->obmat, vec);
1221                                 }
1222                         }
1223                         translate_m4( rv3d->viewmat,-vec[0], -vec[1], -vec[2]);
1224                 }
1225                 else if (v3d->ob_centre_cursor) {
1226                         float vec[3];
1227                         copy_v3_v3(vec, give_cursor(scene, v3d));
1228                         translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]);
1229                 }
1230                 else translate_m4( rv3d->viewmat,rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
1231         }
1232 }
1233
1234 /* IGLuint-> GLuint*/
1235 /* Warning: be sure to account for a negative return value
1236 *   This is an error, "Too many objects in select buffer"
1237 *   and no action should be taken (can crash blender) if this happens
1238 */
1239 short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
1240 {
1241         Scene *scene= vc->scene;
1242         View3D *v3d= vc->v3d;
1243         ARegion *ar= vc->ar;
1244         rctf rect;
1245         short code, hits;
1246         char dt, dtx;
1247         
1248         G.f |= G_PICKSEL;
1249         
1250         /* case not a border select */
1251         if(input->xmin==input->xmax) {
1252                 rect.xmin= input->xmin-12;      // seems to be default value for bones only now
1253                 rect.xmax= input->xmin+12;
1254                 rect.ymin= input->ymin-12;
1255                 rect.ymax= input->ymin+12;
1256         }
1257         else {
1258                 rect.xmin= input->xmin;
1259                 rect.xmax= input->xmax;
1260                 rect.ymin= input->ymin;
1261                 rect.ymax= input->ymax;
1262         }
1263         
1264         setwinmatrixview3d(ar, v3d, &rect);
1265         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1266         
1267         if(v3d->drawtype > OB_WIRE) {
1268                 v3d->zbuf= TRUE;
1269                 glEnable(GL_DEPTH_TEST);
1270         }
1271         
1272         if(vc->rv3d->rflag & RV3D_CLIPPING)
1273                 view3d_set_clipping(vc->rv3d);
1274         
1275         glSelectBuffer( bufsize, (GLuint *)buffer);
1276         glRenderMode(GL_SELECT);
1277         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1278         glPushName(-1);
1279         code= 1;
1280         
1281         if(vc->obedit && vc->obedit->type==OB_MBALL) {
1282                 draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1283         }
1284         else if((vc->obedit && vc->obedit->type==OB_ARMATURE)) {
1285                 /* if not drawing sketch, draw bones */
1286                 if(!BDR_drawSketchNames(vc)) {
1287                         draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1288                 }
1289         }
1290         else {
1291                 Base *base;
1292                 
1293                 v3d->xray= TRUE;        // otherwise it postpones drawing
1294                 for(base= scene->base.first; base; base= base->next) {
1295                         if(base->lay & v3d->lay) {
1296                                 
1297                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1298                                         base->selcol= 0;
1299                                 else {
1300                                         base->selcol= code;
1301                                         glLoadName(code);
1302                                         draw_object(scene, ar, v3d, base, DRAW_PICKING|DRAW_CONSTCOLOR);
1303                                         
1304                                         /* we draw group-duplicators for selection too */
1305                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1306                                                 ListBase *lb;
1307                                                 DupliObject *dob;
1308                                                 Base tbase;
1309                                                 
1310                                                 tbase.flag= OB_FROMDUPLI;
1311                                                 lb= object_duplilist(scene, base->object);
1312                                                 
1313                                                 for(dob= lb->first; dob; dob= dob->next) {
1314                                                         tbase.object= dob->ob;
1315                                                         copy_m4_m4(dob->ob->obmat, dob->mat);
1316                                                         
1317                                                         /* extra service: draw the duplicator in drawtype of parent */
1318                                                         /* MIN2 for the drawtype to allow bounding box objects in groups for lods */
1319                                                         dt= tbase.object->dt;   tbase.object->dt= MIN2(tbase.object->dt, base->object->dt);
1320                                                         dtx= tbase.object->dtx; tbase.object->dtx= base->object->dtx;
1321
1322                                                         draw_object(scene, ar, v3d, &tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1323                                                         
1324                                                         tbase.object->dt= dt;
1325                                                         tbase.object->dtx= dtx;
1326
1327                                                         copy_m4_m4(dob->ob->obmat, dob->omat);
1328                                                 }
1329                                                 free_object_duplilist(lb);
1330                                         }
1331                                         code++;
1332                                 }                               
1333                         }
1334                 }
1335                 v3d->xray= FALSE;       // restore
1336         }
1337         
1338         glPopName();    /* see above (pushname) */
1339         hits= glRenderMode(GL_RENDER);
1340         
1341         G.f &= ~G_PICKSEL;
1342         setwinmatrixview3d(ar, v3d, NULL);
1343         mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1344         
1345         if(v3d->drawtype > OB_WIRE) {
1346                 v3d->zbuf= 0;
1347                 glDisable(GL_DEPTH_TEST);
1348         }
1349 // XXX  persp(PERSP_WIN);
1350         
1351         if(vc->rv3d->rflag & RV3D_CLIPPING)
1352                 view3d_clr_clipping();
1353         
1354         if(hits<0) printf("Too many objects in select buffer\n");       // XXX make error message
1355         
1356         return hits;
1357 }
1358
1359 /* ********************** local view operator ******************** */
1360
1361 static unsigned int free_localbit(Main *bmain)
1362 {
1363         unsigned int lay;
1364         ScrArea *sa;
1365         bScreen *sc;
1366         
1367         lay= 0;
1368         
1369         /* sometimes we loose a localview: when an area is closed */
1370         /* check all areas: which localviews are in use? */
1371         for(sc= bmain->screen.first; sc; sc= sc->id.next) {
1372                 for(sa= sc->areabase.first; sa; sa= sa->next) {
1373                         SpaceLink *sl= sa->spacedata.first;
1374                         for(; sl; sl= sl->next) {
1375                                 if(sl->spacetype==SPACE_VIEW3D) {
1376                                         View3D *v3d= (View3D*) sl;
1377                                         lay |= v3d->lay;
1378                                 }
1379                         }
1380                 }
1381         }
1382         
1383         if( (lay & 0x01000000)==0) return 0x01000000;
1384         if( (lay & 0x02000000)==0) return 0x02000000;
1385         if( (lay & 0x04000000)==0) return 0x04000000;
1386         if( (lay & 0x08000000)==0) return 0x08000000;
1387         if( (lay & 0x10000000)==0) return 0x10000000;
1388         if( (lay & 0x20000000)==0) return 0x20000000;
1389         if( (lay & 0x40000000)==0) return 0x40000000;
1390         if( (lay & 0x80000000)==0) return 0x80000000;
1391         
1392         return 0;
1393 }
1394
1395 int ED_view3d_scene_layer_set(int lay, const int *values, int *active)
1396 {
1397         int i, tot= 0;
1398         
1399         /* ensure we always have some layer selected */
1400         for(i=0; i<20; i++)
1401                 if(values[i])
1402                         tot++;
1403         
1404         if(tot==0)
1405                 return lay;
1406         
1407         for(i=0; i<20; i++) {
1408                 
1409                 if (active) {
1410                         /* if this value has just been switched on, make that layer active */
1411                         if (values[i] && (lay & (1<<i))==0) {
1412                                 *active = (1<<i);
1413                         }
1414                 }
1415                         
1416                 if (values[i]) lay |= (1<<i);
1417                 else lay &= ~(1<<i);
1418         }
1419         
1420         /* ensure always an active layer */
1421         if (active && (lay & *active)==0) {
1422                 for(i=0; i<20; i++) {
1423                         if(lay & (1<<i)) {
1424                                 *active= 1<<i;
1425                                 break;
1426                         }
1427                 }
1428         }
1429         
1430         return lay;
1431 }
1432
1433 static void initlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1434 {
1435         View3D *v3d= sa->spacedata.first;
1436         Base *base;
1437         float size = 0.0, min[3], max[3], box[3];
1438         unsigned int locallay;
1439         int ok=0;
1440
1441         if(v3d->localvd) return;
1442
1443         INIT_MINMAX(min, max);
1444
1445         locallay= free_localbit(bmain);
1446
1447         if(locallay==0) {
1448                 printf("Sorry, no more than 8 localviews\n");   // XXX error 
1449                 ok= 0;
1450         }
1451         else {
1452                 if(scene->obedit) {
1453                         minmax_object(scene->obedit, min, max);
1454                         
1455                         ok= 1;
1456                 
1457                         BASACT->lay |= locallay;
1458                         scene->obedit->lay= BASACT->lay;
1459                 }
1460                 else {
1461                         for(base= FIRSTBASE; base; base= base->next) {
1462                                 if(TESTBASE(v3d, base))  {
1463                                         minmax_object(base->object, min, max);
1464                                         base->lay |= locallay;
1465                                         base->object->lay= base->lay;
1466                                         ok= 1;
1467                                 }
1468                         }
1469                 }
1470                 
1471                 box[0]= (max[0]-min[0]);
1472                 box[1]= (max[1]-min[1]);
1473                 box[2]= (max[2]-min[2]);
1474                 size= MAX3(box[0], box[1], box[2]);
1475                 if(size <= 0.01f) size= 0.01f;
1476         }
1477         
1478         if(ok) {
1479                 ARegion *ar;
1480                 
1481                 v3d->localvd= MEM_mallocN(sizeof(View3D), "localview");
1482                 
1483                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1484
1485                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
1486                         if(ar->regiontype == RGN_TYPE_WINDOW) {
1487                                 RegionView3D *rv3d= ar->regiondata;
1488
1489                                 rv3d->localvd= MEM_mallocN(sizeof(RegionView3D), "localview region");
1490                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1491                                 
1492                                 rv3d->ofs[0]= -(min[0]+max[0])/2.0f;
1493                                 rv3d->ofs[1]= -(min[1]+max[1])/2.0f;
1494                                 rv3d->ofs[2]= -(min[2]+max[2])/2.0f;
1495
1496                                 rv3d->dist= size;
1497                                 /* perspective should be a bit farther away to look nice */
1498                                 if(rv3d->persp==RV3D_ORTHO)
1499                                         rv3d->dist*= 0.7f;
1500
1501                                 // correction for window aspect ratio
1502                                 if(ar->winy>2 && ar->winx>2) {
1503                                         float asp= (float)ar->winx/(float)ar->winy;
1504                                         if(asp < 1.0f) asp= 1.0f/asp;
1505                                         rv3d->dist*= asp;
1506                                 }
1507                                 
1508                                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP;
1509                                 
1510                                 v3d->cursor[0]= -rv3d->ofs[0];
1511                                 v3d->cursor[1]= -rv3d->ofs[1];
1512                                 v3d->cursor[2]= -rv3d->ofs[2];
1513                         }
1514                 }
1515                 
1516                 v3d->lay= locallay;
1517         }
1518         else {
1519                 /* clear flags */ 
1520                 for(base= FIRSTBASE; base; base= base->next) {
1521                         if( base->lay & locallay ) {
1522                                 base->lay-= locallay;
1523                                 if(base->lay==0) base->lay= v3d->layact;
1524                                 if(base->object != scene->obedit) base->flag |= SELECT;
1525                                 base->object->lay= base->lay;
1526                         }
1527                 }               
1528         }
1529
1530 }
1531
1532 static void restore_localviewdata(ScrArea *sa, int free)
1533 {
1534         ARegion *ar;
1535         View3D *v3d= sa->spacedata.first;
1536         
1537         if(v3d->localvd==NULL) return;
1538         
1539         v3d->near= v3d->localvd->near;
1540         v3d->far= v3d->localvd->far;
1541         v3d->lay= v3d->localvd->lay;
1542         v3d->layact= v3d->localvd->layact;
1543         v3d->drawtype= v3d->localvd->drawtype;
1544         v3d->camera= v3d->localvd->camera;
1545         
1546         if(free) {
1547                 MEM_freeN(v3d->localvd);
1548                 v3d->localvd= NULL;
1549         }
1550         
1551         for(ar= sa->regionbase.first; ar; ar= ar->next) {
1552                 if(ar->regiontype == RGN_TYPE_WINDOW) {
1553                         RegionView3D *rv3d= ar->regiondata;
1554                         
1555                         if(rv3d->localvd) {
1556                                 rv3d->dist= rv3d->localvd->dist;
1557                                 copy_v3_v3(rv3d->ofs, rv3d->localvd->ofs);
1558                                 copy_qt_qt(rv3d->viewquat, rv3d->localvd->viewquat);
1559                                 rv3d->view= rv3d->localvd->view;
1560                                 rv3d->persp= rv3d->localvd->persp;
1561                                 rv3d->camzoom= rv3d->localvd->camzoom;
1562
1563                                 if(free) {
1564                                         MEM_freeN(rv3d->localvd);
1565                                         rv3d->localvd= NULL;
1566                                 }
1567                         }
1568                 }
1569         }
1570 }
1571
1572 static void endlocalview(Main *bmain, Scene *scene, ScrArea *sa)
1573 {
1574         View3D *v3d= sa->spacedata.first;
1575         struct Base *base;
1576         unsigned int locallay;
1577         
1578         if(v3d->localvd) {
1579                 
1580                 locallay= v3d->lay & 0xFF000000;
1581                 
1582                 restore_localviewdata(sa, 1); // 1 = free
1583
1584                 /* for when in other window the layers have changed */
1585                 if(v3d->scenelock) v3d->lay= scene->lay;
1586                 
1587                 for(base= FIRSTBASE; base; base= base->next) {
1588                         if( base->lay & locallay ) {
1589                                 base->lay-= locallay;
1590                                 if(base->lay==0) base->lay= v3d->layact;
1591                                 if(base->object != scene->obedit) {
1592                                         base->flag |= SELECT;
1593                                         base->object->flag |= SELECT;
1594                                 }
1595                                 base->object->lay= base->lay;
1596                         }
1597                 }
1598                 
1599                 DAG_on_visible_update(bmain, FALSE);
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_main(C), 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         ot->flag= OPTYPE_UNDO; /* localview changes object layer bitflags */
1628         
1629         ot->poll= ED_operator_view3d_active;
1630 }
1631
1632 #ifdef WITH_GAMEENGINE
1633
1634 static ListBase queue_back;
1635 static void SaveState(bContext *C, wmWindow *win)
1636 {
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, wmWindow *win)
1652 {
1653         Object *obact = CTX_data_active_object(C);
1654         
1655         if(obact && obact->mode & OB_MODE_TEXTURE_PAINT)
1656                 GPU_paint_set_mipmap(0);
1657
1658         //XXX curarea->win_swap = 0;
1659         //XXX curarea->head_swap=0;
1660         //XXX allqueue(REDRAWVIEW3D, 1);
1661         //XXX allqueue(REDRAWBUTSALL, 0);
1662         //XXX reset_slowparents();
1663         //XXX waitcursor(0);
1664         //XXX G.qual= 0;
1665         
1666         if(win) /* check because closing win can set to NULL */
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 static 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 #endif // WITH_GAMEENGINE
1721
1722 static int game_engine_poll(bContext *C)
1723 {
1724         /* we need a context and area to launch BGE
1725         it's a temporary solution to avoid crash at load time
1726         if we try to auto run the BGE. Ideally we want the
1727         context to be set as soon as we load the file. */
1728
1729         if(CTX_wm_window(C)==NULL) return 0;
1730         if(CTX_wm_screen(C)==NULL) return 0;
1731         if(CTX_wm_area(C)==NULL) return 0;
1732
1733         if(CTX_data_mode_enum(C)!=CTX_MODE_OBJECT)
1734                 return 0;
1735
1736         return 1;
1737 }
1738
1739 int ED_view3d_context_activate(bContext *C)
1740 {
1741         bScreen *sc= CTX_wm_screen(C);
1742         ScrArea *sa= CTX_wm_area(C);
1743         ARegion *ar;
1744
1745         /* sa can be NULL when called from python */
1746         if(sa==NULL || sa->spacetype != SPACE_VIEW3D)
1747                 for(sa=sc->areabase.first; sa; sa= sa->next)
1748                         if(sa->spacetype==SPACE_VIEW3D)
1749                                 break;
1750
1751         if(!sa)
1752                 return 0;
1753         
1754         for(ar=sa->regionbase.first; ar; ar=ar->next)
1755                 if(ar->regiontype == RGN_TYPE_WINDOW)
1756                         break;
1757         
1758         if(!ar)
1759                 return 0;
1760         
1761         // bad context switch ..
1762         CTX_wm_area_set(C, sa);
1763         CTX_wm_region_set(C, ar);
1764
1765         return 1;
1766 }
1767
1768 static int game_engine_exec(bContext *C, wmOperator *op)
1769 {
1770 #ifdef WITH_GAMEENGINE
1771         Scene *startscene = CTX_data_scene(C);
1772         ScrArea *sa, *prevsa= CTX_wm_area(C);
1773         ARegion *ar, *prevar= CTX_wm_region(C);
1774         wmWindow *prevwin= CTX_wm_window(C);
1775         RegionView3D *rv3d;
1776         rcti cam_frame;
1777
1778         (void)op; /* unused */
1779         
1780         // bad context switch ..
1781         if(!ED_view3d_context_activate(C))
1782                 return OPERATOR_CANCELLED;
1783         
1784         rv3d= CTX_wm_region_view3d(C);
1785         sa= CTX_wm_area(C);
1786         ar= CTX_wm_region(C);
1787
1788         view3d_operator_needs_opengl(C);
1789         
1790         game_set_commmandline_options(&startscene->gm);
1791
1792         if(rv3d->persp==RV3D_CAMOB && startscene->gm.framing.type == SCE_GAMEFRAMING_BARS && startscene->gm.stereoflag != STEREO_DOME) { /* Letterbox */
1793                 rctf cam_framef;
1794                 ED_view3d_calc_camera_border(startscene, ar, CTX_wm_view3d(C), rv3d, &cam_framef, FALSE);
1795                 cam_frame.xmin = cam_framef.xmin + ar->winrct.xmin;
1796                 cam_frame.xmax = cam_framef.xmax + ar->winrct.xmin;
1797                 cam_frame.ymin = cam_framef.ymin + ar->winrct.ymin;
1798                 cam_frame.ymax = cam_framef.ymax + ar->winrct.ymin;
1799                 BLI_isect_rcti(&ar->winrct, &cam_frame, &cam_frame);
1800         }
1801         else {
1802                 cam_frame.xmin = ar->winrct.xmin;
1803                 cam_frame.xmax = ar->winrct.xmax;
1804                 cam_frame.ymin = ar->winrct.ymin;
1805                 cam_frame.ymax = ar->winrct.ymax;
1806         }
1807
1808
1809         SaveState(C, prevwin);
1810
1811         StartKetsjiShell(C, ar, &cam_frame, 1);
1812
1813         /* window wasnt closed while the BGE was running */
1814         if(BLI_findindex(&CTX_wm_manager(C)->windows, prevwin) == -1) {
1815                 prevwin= NULL;
1816                 CTX_wm_window_set(C, NULL);
1817         }
1818         
1819         if(prevwin) {
1820                 /* restore context, in case it changed in the meantime, for
1821                    example by working in another window or closing it */
1822                 CTX_wm_region_set(C, prevar);
1823                 CTX_wm_window_set(C, prevwin);
1824                 CTX_wm_area_set(C, prevsa);
1825         }
1826
1827         RestoreState(C, prevwin);
1828
1829         //XXX restore_all_scene_cfra(scene_cfra_store);
1830         set_scene_bg(CTX_data_main(C), startscene);
1831         //XXX scene_update_for_newframe(bmain, scene, scene->lay);
1832         
1833         ED_area_tag_redraw(CTX_wm_area(C));
1834
1835         return OPERATOR_FINISHED;
1836 #else
1837         (void)C; /* unused */
1838         BKE_report(op->reports, RPT_ERROR, "Game engine is disabled in this build.");
1839         return OPERATOR_CANCELLED;
1840 #endif
1841 }
1842
1843 void VIEW3D_OT_game_start(wmOperatorType *ot)
1844 {
1845         
1846         /* identifiers */
1847         ot->name= "Start Game Engine";
1848         ot->description= "Start game engine";
1849         ot->idname= "VIEW3D_OT_game_start";
1850         
1851         /* api callbacks */
1852         ot->exec= game_engine_exec;
1853         
1854         ot->poll= game_engine_poll;
1855 }
1856
1857 /* ************************************** */
1858
1859 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
1860 {
1861         float alignaxis[3] = {0.0, 0.0, 0.0};
1862         float norm[3], axis[3], angle, new_quat[4];
1863         
1864         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
1865         else alignaxis[-axisidx-1]= -1.0;
1866
1867         normalize_v3_v3(norm, vec);
1868
1869         angle= (float)acos(dot_v3v3(alignaxis, norm));
1870         cross_v3_v3v3(axis, alignaxis, norm);
1871         axis_angle_to_quat( new_quat,axis, -angle);
1872         
1873         rv3d->view= RV3D_VIEW_USER;
1874         
1875         if (rv3d->persp==RV3D_CAMOB && v3d->camera) {
1876                 /* switch out of camera view */
1877                 float orig_ofs[3];
1878                 float orig_dist= rv3d->dist;
1879                 float orig_lens= v3d->lens;
1880                 
1881                 copy_v3_v3(orig_ofs, rv3d->ofs);
1882                 rv3d->persp= RV3D_PERSP;
1883                 rv3d->dist= 0.0;
1884                 ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1885                 smooth_view(NULL, NULL, NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1886         } else {
1887                 if (rv3d->persp==RV3D_CAMOB) rv3d->persp= RV3D_PERSP; /* switch out of camera mode */
1888                 smooth_view(NULL, NULL, NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1889         }
1890 }
1891
1892 float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3])
1893 {
1894         return  (rv3d->persmat[3][3] + (
1895                                 rv3d->persmat[0][3]*co[0] +
1896                                 rv3d->persmat[1][3]*co[1] +
1897                                 rv3d->persmat[2][3]*co[2])
1898                         ) * rv3d->pixsize;
1899 }