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