Cleanup: move 3D view utilities into own file
[blender.git] / source / blender / editors / space_view3d / view3d_utils.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  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 /** \file blender/editors/space_view3d/view3d_utils.c
25  *  \ingroup spview3d
26  *
27  * 3D View checks and manipulation (no operators).
28  */
29
30 #include <string.h>
31 #include <stdio.h>
32 #include <math.h>
33 #include <float.h>
34
35 #include "DNA_camera_types.h"
36 #include "DNA_curve_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39
40 #include "MEM_guardedalloc.h"
41
42 #include "BLI_bitmap_draw_2d.h"
43 #include "BLI_blenlib.h"
44 #include "BLI_math.h"
45 #include "BLI_utildefines.h"
46
47 #include "BKE_camera.h"
48 #include "BKE_context.h"
49 #include "BKE_object.h"
50 #include "BKE_screen.h"
51 #include "BKE_depsgraph.h" /* for ED_view3d_camera_lock_sync */
52
53 #include "BIF_gl.h"
54 #include "BIF_glutil.h"
55
56 #include "WM_api.h"
57 #include "WM_types.h"
58
59 #include "ED_keyframing.h"
60 #include "ED_screen.h"
61 #include "ED_view3d.h"
62
63 #include "view3d_intern.h"  /* own include */
64
65 /* -------------------------------------------------------------------- */
66 /** \name View Data Access Utilities
67  *
68  * \{ */
69
70 float *ED_view3d_cursor3d_get(Scene *scene, View3D *v3d)
71 {
72         if (v3d && v3d->localvd) return v3d->cursor;
73         else return scene->cursor;
74 }
75
76 Camera *ED_view3d_camera_data_get(View3D *v3d, RegionView3D *rv3d)
77 {
78         /* establish the camera object, so we can default to view mapping if anything is wrong with it */
79         if ((rv3d->persp == RV3D_CAMOB) && v3d->camera && (v3d->camera->type == OB_CAMERA)) {
80                 return v3d->camera->data;
81         }
82         else {
83                 return NULL;
84         }
85 }
86
87 void ED_view3d_dist_range_get(
88         const View3D *v3d,
89         float r_dist_range[2])
90 {
91         r_dist_range[0] = v3d->grid * 0.001f;
92         r_dist_range[1] = v3d->far * 10.0f;
93 }
94
95 /**
96  * \note copies logic of #ED_view3d_viewplane_get(), keep in sync.
97  */
98 bool ED_view3d_clip_range_get(
99         const View3D *v3d, const RegionView3D *rv3d,
100         float *r_clipsta, float *r_clipend,
101         const bool use_ortho_factor)
102 {
103         CameraParams params;
104
105         BKE_camera_params_init(&params);
106         BKE_camera_params_from_view3d(&params, v3d, rv3d);
107
108         if (use_ortho_factor && params.is_ortho) {
109                 const float fac = 2.0f / (params.clipend - params.clipsta);
110                 params.clipsta *= fac;
111                 params.clipend *= fac;
112         }
113
114         if (r_clipsta) *r_clipsta = params.clipsta;
115         if (r_clipend) *r_clipend = params.clipend;
116
117         return params.is_ortho;
118 }
119
120 bool ED_view3d_viewplane_get(
121         const View3D *v3d, const RegionView3D *rv3d, int winx, int winy,
122         rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize)
123 {
124         CameraParams params;
125
126         BKE_camera_params_init(&params);
127         BKE_camera_params_from_view3d(&params, v3d, rv3d);
128         BKE_camera_params_compute_viewplane(&params, winx, winy, 1.0f, 1.0f);
129
130         if (r_viewplane) *r_viewplane = params.viewplane;
131         if (r_clipsta) *r_clipsta = params.clipsta;
132         if (r_clipend) *r_clipend = params.clipend;
133         if (r_pixsize) *r_pixsize = params.viewdx;
134
135         return params.is_ortho;
136 }
137
138 /** \} */
139
140
141 /* -------------------------------------------------------------------- */
142 /** \name View State/Context Utilities
143  *
144  * \{ */
145
146 /**
147  * Use instead of: ``bglPolygonOffset(rv3d->dist, ...)`` see bug [#37727]
148  */
149 void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist)
150 {
151         float viewdist;
152
153         if (rv3d->rflag & RV3D_ZOFFSET_DISABLED) {
154                 return;
155         }
156
157         viewdist = rv3d->dist;
158
159         /* special exception for ortho camera (viewdist isnt used for perspective cameras) */
160         if (dist != 0.0f) {
161                 if (rv3d->persp == RV3D_CAMOB) {
162                         if (rv3d->is_persp == false) {
163                                 viewdist = 1.0f / max_ff(fabsf(rv3d->winmat[0][0]), fabsf(rv3d->winmat[1][1]));
164                         }
165                 }
166         }
167
168         bglPolygonOffset(viewdist, dist);
169 }
170
171 bool ED_view3d_context_activate(bContext *C)
172 {
173         bScreen *sc = CTX_wm_screen(C);
174         ScrArea *sa = CTX_wm_area(C);
175         ARegion *ar;
176
177         /* sa can be NULL when called from python */
178         if (sa == NULL || sa->spacetype != SPACE_VIEW3D) {
179                 sa = BKE_screen_find_big_area(sc, SPACE_VIEW3D, 0);
180         }
181
182         if (sa == NULL) {
183                 return false;
184         }
185
186         ar = BKE_area_find_region_active_win(sa);
187         if (ar == NULL) {
188                 return false;
189         }
190
191         /* bad context switch .. */
192         CTX_wm_area_set(C, sa);
193         CTX_wm_region_set(C, ar);
194
195         return true;
196 }
197
198 /** \} */
199
200 /* -------------------------------------------------------------------- */
201 /** \name View Clipping Utilities
202  *
203  * \{ */
204 void ED_view3d_clipping_calc_from_boundbox(float clip[4][4], const BoundBox *bb, const bool is_flip)
205 {
206         int val;
207
208         for (val = 0; val < 4; val++) {
209                 normal_tri_v3(clip[val], bb->vec[val], bb->vec[val == 3 ? 0 : val + 1], bb->vec[val + 4]);
210                 if (UNLIKELY(is_flip)) {
211                         negate_v3(clip[val]);
212                 }
213
214                 clip[val][3] = -dot_v3v3(clip[val], bb->vec[val]);
215         }
216 }
217
218 void ED_view3d_clipping_calc(BoundBox *bb, float planes[4][4], bglMats *mats, const rcti *rect)
219 {
220         float modelview[4][4];
221         double xs, ys, p[3];
222         int val, flip_sign, a;
223
224         /* near zero floating point values can give issues with gluUnProject
225          * in side view on some implementations */
226         if (fabs(mats->modelview[0]) < 1e-6) mats->modelview[0] = 0.0;
227         if (fabs(mats->modelview[5]) < 1e-6) mats->modelview[5] = 0.0;
228
229         /* Set up viewport so that gluUnProject will give correct values */
230         mats->viewport[0] = 0;
231         mats->viewport[1] = 0;
232
233         /* four clipping planes and bounding volume */
234         /* first do the bounding volume */
235         for (val = 0; val < 4; val++) {
236                 xs = (val == 0 || val == 3) ? rect->xmin : rect->xmax;
237                 ys = (val == 0 || val == 1) ? rect->ymin : rect->ymax;
238
239                 gluUnProject(xs, ys, 0.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
240                 copy_v3fl_v3db(bb->vec[val], p);
241
242                 gluUnProject(xs, ys, 1.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]);
243                 copy_v3fl_v3db(bb->vec[4 + val], p);
244         }
245
246         /* verify if we have negative scale. doing the transform before cross
247          * product flips the sign of the vector compared to doing cross product
248          * before transform then, so we correct for that. */
249         for (a = 0; a < 16; a++)
250                 ((float *)modelview)[a] = mats->modelview[a];
251         flip_sign = is_negative_m4(modelview);
252
253         ED_view3d_clipping_calc_from_boundbox(planes, bb, flip_sign);
254 }
255
256 /** \} */
257
258 /* -------------------------------------------------------------------- */
259 /** \name View Bound-Box Utilities
260  *
261  * \{ */
262
263 static bool view3d_boundbox_clip_m4(const BoundBox *bb, float persmatob[4][4])
264 {
265         int a, flag = -1, fl;
266
267         for (a = 0; a < 8; a++) {
268                 float vec[4], min, max;
269                 copy_v3_v3(vec, bb->vec[a]);
270                 vec[3] = 1.0;
271                 mul_m4_v4(persmatob, vec);
272                 max = vec[3];
273                 min = -vec[3];
274
275                 fl = 0;
276                 if (vec[0] < min) fl += 1;
277                 if (vec[0] > max) fl += 2;
278                 if (vec[1] < min) fl += 4;
279                 if (vec[1] > max) fl += 8;
280                 if (vec[2] < min) fl += 16;
281                 if (vec[2] > max) fl += 32;
282
283                 flag &= fl;
284                 if (flag == 0) return true;
285         }
286
287         return false;
288 }
289
290 bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d, const BoundBox *bb, float obmat[4][4])
291 {
292         /* return 1: draw */
293
294         float persmatob[4][4];
295
296         if (bb == NULL) return true;
297         if (bb->flag & BOUNDBOX_DISABLED) return true;
298
299         mul_m4_m4m4(persmatob, (float(*)[4])rv3d->persmat, obmat);
300
301         return view3d_boundbox_clip_m4(bb, persmatob);
302 }
303
304 bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const BoundBox *bb)
305 {
306         if (bb == NULL) return true;
307         if (bb->flag & BOUNDBOX_DISABLED) return true;
308
309         return view3d_boundbox_clip_m4(bb, rv3d->persmatob);
310 }
311
312 /** \} */
313
314 /* -------------------------------------------------------------------- */
315 /** \name View Perspective & Mode Switching
316  *
317  * Misc view utility functions.
318  * \{ */
319
320 bool ED_view3d_offset_lock_check(const  View3D *v3d, const  RegionView3D *rv3d)
321 {
322         return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre);
323 }
324
325 /**
326  * Use to store the last view, before entering camera view.
327  */
328 void ED_view3d_lastview_store(RegionView3D *rv3d)
329 {
330         copy_qt_qt(rv3d->lviewquat, rv3d->viewquat);
331         rv3d->lview = rv3d->view;
332         if (rv3d->persp != RV3D_CAMOB) {
333                 rv3d->lpersp = rv3d->persp;
334         }
335 }
336
337 void ED_view3d_lock_clear(View3D *v3d)
338 {
339         v3d->ob_centre = NULL;
340         v3d->ob_centre_bone[0] = '\0';
341         v3d->ob_centre_cursor = false;
342         v3d->flag2 &= ~V3D_LOCK_CAMERA;
343 }
344
345 /**
346  * For viewport operators that exit camera perspective.
347  *
348  * \note This differs from simply setting ``rv3d->persp = persp`` because it
349  * sets the ``ofs`` and ``dist`` values of the viewport so it matches the camera,
350  * otherwise switching out of camera view may jump to a different part of the scene.
351  */
352 void ED_view3d_persp_switch_from_camera(View3D *v3d, RegionView3D *rv3d, const char persp)
353 {
354         BLI_assert(rv3d->persp == RV3D_CAMOB);
355         BLI_assert(persp != RV3D_CAMOB);
356
357         if (v3d->camera) {
358                 rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
359                 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
360         }
361
362         if (!ED_view3d_camera_lock_check(v3d, rv3d)) {
363                 rv3d->persp = persp;
364         }
365 }
366 /**
367  * Action to take when rotating the view,
368  * handle auto-persp and logic for switching out of views.
369  *
370  * shared with NDOF.
371  */
372 bool ED_view3d_persp_ensure(struct View3D *v3d, ARegion *ar)
373 {
374         RegionView3D *rv3d = ar->regiondata;
375         const bool autopersp = (U.uiflag & USER_AUTOPERSP) != 0;
376
377         BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0);
378
379         if (ED_view3d_camera_lock_check(v3d, rv3d))
380                 return false;
381
382         if (rv3d->persp != RV3D_PERSP) {
383                 if (rv3d->persp == RV3D_CAMOB) {
384                         /* If autopersp and previous view was an axis one, switch back to PERSP mode, else reuse previous mode. */
385                         char persp = (autopersp && RV3D_VIEW_IS_AXIS(rv3d->lview)) ? RV3D_PERSP : rv3d->lpersp;
386                         ED_view3d_persp_switch_from_camera(v3d, rv3d, persp);
387                 }
388                 else if (autopersp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
389                         rv3d->persp = RV3D_PERSP;
390                 }
391                 return true;
392         }
393
394         return false;
395 }
396
397 /** \} */
398
399 /* -------------------------------------------------------------------- */
400 /** \name Camera Lock API
401  *
402  * Lock the camera to the view-port, allowing view manipulation to transform the camera.
403  * \{ */
404
405 /**
406  * \return true when the view-port is locked to its camera.
407  */
408 bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
409 {
410         return ((v3d->camera) &&
411                 (!ID_IS_LINKED(v3d->camera)) &&
412                 (v3d->flag2 & V3D_LOCK_CAMERA) &&
413                 (rv3d->persp == RV3D_CAMOB));
414 }
415
416 /**
417  * Apply the camera object transformation to the view-port.
418  * (needed so we can use regular view-port manipulation operators, that sync back to the camera).
419  */
420 void ED_view3d_camera_lock_init_ex(View3D *v3d, RegionView3D *rv3d, const bool calc_dist)
421 {
422         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
423                 if (calc_dist) {
424                         /* using a fallback dist is OK here since ED_view3d_from_object() compensates for it */
425                         rv3d->dist = ED_view3d_offset_distance(v3d->camera->obmat, rv3d->ofs, VIEW3D_DIST_FALLBACK);
426                 }
427                 ED_view3d_from_object(v3d->camera, rv3d->ofs, rv3d->viewquat, &rv3d->dist, NULL);
428         }
429 }
430
431 void ED_view3d_camera_lock_init(View3D *v3d, RegionView3D *rv3d)
432 {
433         ED_view3d_camera_lock_init_ex(v3d, rv3d, true);
434 }
435
436 /**
437  * Apply the view-port transformation back to the camera object.
438  *
439  * \return true if the camera is moved.
440  */
441 bool ED_view3d_camera_lock_sync(View3D *v3d, RegionView3D *rv3d)
442 {
443         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
444                 ObjectTfmProtectedChannels obtfm;
445                 Object *root_parent;
446
447                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
448                         Object *ob_update;
449                         float tmat[4][4];
450                         float imat[4][4];
451                         float view_mat[4][4];
452                         float diff_mat[4][4];
453                         float parent_mat[4][4];
454
455                         while (root_parent->parent) {
456                                 root_parent = root_parent->parent;
457                         }
458
459                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
460
461                         normalize_m4_m4(tmat, v3d->camera->obmat);
462
463                         invert_m4_m4(imat, tmat);
464                         mul_m4_m4m4(diff_mat, view_mat, imat);
465
466                         mul_m4_m4m4(parent_mat, diff_mat, root_parent->obmat);
467
468                         BKE_object_tfm_protected_backup(root_parent, &obtfm);
469                         BKE_object_apply_mat4(root_parent, parent_mat, true, false);
470                         BKE_object_tfm_protected_restore(root_parent, &obtfm, root_parent->protectflag);
471
472                         ob_update = v3d->camera;
473                         while (ob_update) {
474                                 DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
475                                 WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, ob_update);
476                                 ob_update = ob_update->parent;
477                         }
478                 }
479                 else {
480                         /* always maintain the same scale */
481                         const short protect_scale_all = (OB_LOCK_SCALEX | OB_LOCK_SCALEY | OB_LOCK_SCALEZ);
482                         BKE_object_tfm_protected_backup(v3d->camera, &obtfm);
483                         ED_view3d_to_object(v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
484                         BKE_object_tfm_protected_restore(v3d->camera, &obtfm, v3d->camera->protectflag | protect_scale_all);
485
486                         DAG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
487                         WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, v3d->camera);
488                 }
489
490                 return true;
491         }
492         else {
493                 return false;
494         }
495 }
496
497 bool ED_view3d_camera_autokey(
498         Scene *scene, ID *id_key,
499         struct bContext *C, const bool do_rotate, const bool do_translate)
500 {
501         if (autokeyframe_cfra_can_key(scene, id_key)) {
502                 const float cfra = (float)CFRA;
503                 ListBase dsources = {NULL, NULL};
504
505                 /* add data-source override for the camera object */
506                 ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL);
507
508                 /* insert keyframes
509                  * 1) on the first frame
510                  * 2) on each subsequent frame
511                  *    TODO: need to check in future that frame changed before doing this
512                  */
513                 if (do_rotate) {
514                         struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_ROTATION_ID);
515                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
516                 }
517                 if (do_translate) {
518                         struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
519                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
520                 }
521
522                 /* free temp data */
523                 BLI_freelistN(&dsources);
524
525                 return true;
526         }
527         else {
528                 return false;
529         }
530 }
531
532 /**
533  * Call after modifying a locked view.
534  *
535  * \note Not every view edit currently auto-keys (numpad for eg),
536  * this is complicated because of smoothview.
537  */
538 bool ED_view3d_camera_lock_autokey(
539         View3D *v3d, RegionView3D *rv3d,
540         struct bContext *C, const bool do_rotate, const bool do_translate)
541 {
542         /* similar to ED_view3d_cameracontrol_update */
543         if (ED_view3d_camera_lock_check(v3d, rv3d)) {
544                 Scene *scene = CTX_data_scene(C);
545                 ID *id_key;
546                 Object *root_parent;
547                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) {
548                         while (root_parent->parent) {
549                                 root_parent = root_parent->parent;
550                         }
551                         id_key = &root_parent->id;
552                 }
553                 else {
554                         id_key = &v3d->camera->id;
555                 }
556
557                 return ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate);
558         }
559         else {
560                 return false;
561         }
562 }
563
564 /** \} */
565
566
567 /* -------------------------------------------------------------------- */
568 /** \name Box View Support
569  *
570  * Use with quad-split so each view is clipped by the bounds of each view axis.
571  * \{ */
572
573 static void view3d_boxview_clip(ScrArea *sa)
574 {
575         ARegion *ar;
576         BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb");
577         float clip[6][4];
578         float x1 = 0.0f, y1 = 0.0f, z1 = 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f};
579         int val;
580
581         /* create bounding box */
582         for (ar = sa->regionbase.first; ar; ar = ar->next) {
583                 if (ar->regiontype == RGN_TYPE_WINDOW) {
584                         RegionView3D *rv3d = ar->regiondata;
585
586                         if (rv3d->viewlock & RV3D_BOXCLIP) {
587                                 if (ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
588                                         if (ar->winx > ar->winy) x1 = rv3d->dist;
589                                         else x1 = ar->winx * rv3d->dist / ar->winy;
590
591                                         if (ar->winx > ar->winy) y1 = ar->winy * rv3d->dist / ar->winx;
592                                         else y1 = rv3d->dist;
593                                         copy_v2_v2(ofs, rv3d->ofs);
594                                 }
595                                 else if (ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
596                                         ofs[2] = rv3d->ofs[2];
597
598                                         if (ar->winx > ar->winy) z1 = ar->winy * rv3d->dist / ar->winx;
599                                         else z1 = rv3d->dist;
600                                 }
601                         }
602                 }
603         }
604
605         for (val = 0; val < 8; val++) {
606                 if (ELEM(val, 0, 3, 4, 7))
607                         bb->vec[val][0] = -x1 - ofs[0];
608                 else
609                         bb->vec[val][0] =  x1 - ofs[0];
610
611                 if (ELEM(val, 0, 1, 4, 5))
612                         bb->vec[val][1] = -y1 - ofs[1];
613                 else
614                         bb->vec[val][1] =  y1 - ofs[1];
615
616                 if (val > 3)
617                         bb->vec[val][2] = -z1 - ofs[2];
618                 else
619                         bb->vec[val][2] =  z1 - ofs[2];
620         }
621
622         /* normals for plane equations */
623         normal_tri_v3(clip[0], bb->vec[0], bb->vec[1], bb->vec[4]);
624         normal_tri_v3(clip[1], bb->vec[1], bb->vec[2], bb->vec[5]);
625         normal_tri_v3(clip[2], bb->vec[2], bb->vec[3], bb->vec[6]);
626         normal_tri_v3(clip[3], bb->vec[3], bb->vec[0], bb->vec[7]);
627         normal_tri_v3(clip[4], bb->vec[4], bb->vec[5], bb->vec[6]);
628         normal_tri_v3(clip[5], bb->vec[0], bb->vec[2], bb->vec[1]);
629
630         /* then plane equations */
631         for (val = 0; val < 6; val++) {
632                 clip[val][3] = -dot_v3v3(clip[val], bb->vec[val % 5]);
633         }
634
635         /* create bounding box */
636         for (ar = sa->regionbase.first; ar; ar = ar->next) {
637                 if (ar->regiontype == RGN_TYPE_WINDOW) {
638                         RegionView3D *rv3d = ar->regiondata;
639
640                         if (rv3d->viewlock & RV3D_BOXCLIP) {
641                                 rv3d->rflag |= RV3D_CLIPPING;
642                                 memcpy(rv3d->clip, clip, sizeof(clip));
643                                 if (rv3d->clipbb) MEM_freeN(rv3d->clipbb);
644                                 rv3d->clipbb = MEM_dupallocN(bb);
645                         }
646                 }
647         }
648         MEM_freeN(bb);
649 }
650
651 /**
652  * Find which axis values are shared between both views and copy to \a rv3d_dst
653  * taking axis flipping into account.
654  */
655 static void view3d_boxview_sync_axis(RegionView3D *rv3d_dst, RegionView3D *rv3d_src)
656 {
657         /* absolute axis values above this are considered to be set (will be ~1.0f) */
658         const float axis_eps = 0.5f;
659         float viewinv[4];
660
661         /* use the view rotation to identify which axis to sync on */
662         float view_axis_all[4][3] = {
663             {1.0f, 0.0f, 0.0f},
664             {0.0f, 1.0f, 0.0f},
665             {1.0f, 0.0f, 0.0f},
666             {0.0f, 1.0f, 0.0f}};
667
668         float *view_src_x = &view_axis_all[0][0];
669         float *view_src_y = &view_axis_all[1][0];
670
671         float *view_dst_x = &view_axis_all[2][0];
672         float *view_dst_y = &view_axis_all[3][0];
673         int i;
674
675
676         /* we could use rv3d->viewinv, but better not depend on view matrix being updated */
677         if (UNLIKELY(ED_view3d_quat_from_axis_view(rv3d_src->view, viewinv) == false)) {
678                 return;
679         }
680         invert_qt_normalized(viewinv);
681         mul_qt_v3(viewinv, view_src_x);
682         mul_qt_v3(viewinv, view_src_y);
683
684         if (UNLIKELY(ED_view3d_quat_from_axis_view(rv3d_dst->view, viewinv) == false)) {
685                 return;
686         }
687         invert_qt_normalized(viewinv);
688         mul_qt_v3(viewinv, view_dst_x);
689         mul_qt_v3(viewinv, view_dst_y);
690
691         /* check source and dest have a matching axis */
692         for (i = 0; i < 3; i++) {
693                 if (((fabsf(view_src_x[i]) > axis_eps) || (fabsf(view_src_y[i]) > axis_eps)) &&
694                     ((fabsf(view_dst_x[i]) > axis_eps) || (fabsf(view_dst_y[i]) > axis_eps)))
695                 {
696                         rv3d_dst->ofs[i] = rv3d_src->ofs[i];
697                 }
698         }
699 }
700
701 /* sync center/zoom view of region to others, for view transforms */
702 void view3d_boxview_sync(ScrArea *sa, ARegion *ar)
703 {
704         ARegion *artest;
705         RegionView3D *rv3d = ar->regiondata;
706         short clip = 0;
707
708         for (artest = sa->regionbase.first; artest; artest = artest->next) {
709                 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
710                         RegionView3D *rv3dtest = artest->regiondata;
711
712                         if (rv3dtest->viewlock & RV3D_LOCKED) {
713                                 rv3dtest->dist = rv3d->dist;
714                                 view3d_boxview_sync_axis(rv3dtest, rv3d);
715                                 clip |= rv3dtest->viewlock & RV3D_BOXCLIP;
716
717                                 ED_region_tag_redraw(artest);
718                         }
719                 }
720         }
721
722         if (clip) {
723                 view3d_boxview_clip(sa);
724         }
725 }
726
727 /* for home, center etc */
728 void view3d_boxview_copy(ScrArea *sa, ARegion *ar)
729 {
730         ARegion *artest;
731         RegionView3D *rv3d = ar->regiondata;
732         bool clip = false;
733
734         for (artest = sa->regionbase.first; artest; artest = artest->next) {
735                 if (artest != ar && artest->regiontype == RGN_TYPE_WINDOW) {
736                         RegionView3D *rv3dtest = artest->regiondata;
737
738                         if (rv3dtest->viewlock) {
739                                 rv3dtest->dist = rv3d->dist;
740                                 copy_v3_v3(rv3dtest->ofs, rv3d->ofs);
741                                 ED_region_tag_redraw(artest);
742
743                                 clip |= ((rv3dtest->viewlock & RV3D_BOXCLIP) != 0);
744                         }
745                 }
746         }
747
748         if (clip) {
749                 view3d_boxview_clip(sa);
750         }
751 }
752
753 /* 'clip' is used to know if our clip setting has changed */
754 void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, bool do_clip)
755 {
756         ARegion *ar_sync = NULL;
757         RegionView3D *rv3d = ar->regiondata;
758         short viewlock;
759         /* this function copies flags from the first of the 3 other quadview
760          * regions to the 2 other, so it assumes this is the region whose
761          * properties are always being edited, weak */
762         viewlock = rv3d->viewlock;
763
764         if ((viewlock & RV3D_LOCKED) == 0) {
765                 do_clip = (viewlock & RV3D_BOXCLIP) != 0;
766                 viewlock = 0;
767         }
768         else if ((viewlock & RV3D_BOXVIEW) == 0 && (viewlock & RV3D_BOXCLIP) != 0) {
769                 do_clip = true;
770                 viewlock &= ~RV3D_BOXCLIP;
771         }
772
773         for (; ar; ar = ar->prev) {
774                 if (ar->alignment == RGN_ALIGN_QSPLIT) {
775                         rv3d = ar->regiondata;
776                         rv3d->viewlock = viewlock;
777
778                         if (do_clip && (viewlock & RV3D_BOXCLIP) == 0) {
779                                 rv3d->rflag &= ~RV3D_BOXCLIP;
780                         }
781
782                         /* use ar_sync so we sync with one of the aligned views below
783                          * else the view jumps on changing view settings like 'clip'
784                          * since it copies from the perspective view */
785                         ar_sync = ar;
786                 }
787         }
788
789         if (rv3d->viewlock & RV3D_BOXVIEW) {
790                 view3d_boxview_sync(sa, ar_sync ? ar_sync : sa->regionbase.last);
791         }
792
793         /* ensure locked regions have an axis, locked user views don't make much sense */
794         if (viewlock & RV3D_LOCKED) {
795                 int index_qsplit = 0;
796                 for (ar = sa->regionbase.first; ar; ar = ar->next) {
797                         if (ar->alignment == RGN_ALIGN_QSPLIT) {
798                                 rv3d = ar->regiondata;
799                                 if (rv3d->viewlock) {
800                                         if (!RV3D_VIEW_IS_AXIS(rv3d->view)) {
801                                                 rv3d->view = ED_view3d_lock_view_from_index(index_qsplit);
802                                                 rv3d->persp = RV3D_ORTHO;
803                                                 ED_view3d_lock(rv3d);
804                                         }
805                                 }
806                                 index_qsplit++;
807                         }
808                 }
809         }
810
811         ED_area_tag_redraw(sa);
812 }
813
814 /** \} */
815
816 /* -------------------------------------------------------------------- */
817 /** \name View Auto-Depth Utilities
818  * \{ */
819
820 static float view_autodist_depth_margin(ARegion *ar, const int mval[2], int margin)
821 {
822         ViewDepths depth_temp = {0};
823         rcti rect;
824         float depth_close;
825
826         if (margin == 0) {
827                 /* Get Z Depths, needed for perspective, nice for ortho */
828                 rect.xmin = mval[0];
829                 rect.ymin = mval[1];
830                 rect.xmax = mval[0] + 1;
831                 rect.ymax = mval[1] + 1;
832         }
833         else {
834                 BLI_rcti_init_pt_radius(&rect, mval, margin);
835         }
836
837         view3d_update_depths_rect(ar, &depth_temp, &rect);
838         depth_close = view3d_depth_near(&depth_temp);
839         MEM_SAFE_FREE(depth_temp.depths);
840         return depth_close;
841 }
842
843 /**
844  * Get the world-space 3d location from a screen-space 2d point.
845  *
846  * \param mval: Input screen-space pixel location.
847  * \param mouse_worldloc: Output world-space location.
848  * \param fallback_depth_pt: Use this points depth when no depth can be found.
849  */
850 bool ED_view3d_autodist(
851         Scene *scene, ARegion *ar, View3D *v3d,
852         const int mval[2], float mouse_worldloc[3],
853         const bool alphaoverride, const float fallback_depth_pt[3])
854 {
855         bglMats mats; /* ZBuffer depth vars */
856         float depth_close;
857         double cent[2],  p[3];
858         int margin_arr[] = {0, 2, 4};
859         int i;
860         bool depth_ok = false;
861
862         /* Get Z Depths, needed for perspective, nice for ortho */
863         ED_view3d_draw_depth(scene, ar, v3d, alphaoverride);
864
865         /* call after in case settings have been modified since last drawing, see: T47089 */
866         bgl_get_mats(&mats);
867
868         /* Attempt with low margin's first */
869         i = 0;
870         do {
871                 depth_close = view_autodist_depth_margin(ar, mval, margin_arr[i++] * U.pixelsize);
872                 depth_ok = (depth_close != FLT_MAX);
873         } while ((depth_ok == false) && (i < ARRAY_SIZE(margin_arr)));
874
875         if (depth_ok) {
876                 cent[0] = (double)mval[0] + 0.5;
877                 cent[1] = (double)mval[1] + 0.5;
878
879                 if (gluUnProject(cent[0], cent[1], depth_close,
880                                  mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
881                 {
882                         mouse_worldloc[0] = (float)p[0];
883                         mouse_worldloc[1] = (float)p[1];
884                         mouse_worldloc[2] = (float)p[2];
885                         return true;
886                 }
887         }
888
889         if (fallback_depth_pt) {
890                 ED_view3d_win_to_3d_int(v3d, ar, fallback_depth_pt, mval, mouse_worldloc);
891                 return true;
892         }
893         else {
894                 return false;
895         }
896 }
897
898 void ED_view3d_autodist_init(Scene *scene, ARegion *ar, View3D *v3d, int mode)
899 {
900         /* Get Z Depths, needed for perspective, nice for ortho */
901         switch (mode) {
902                 case 0:
903                         ED_view3d_draw_depth(scene, ar, v3d, true);
904                         break;
905                 case 1:
906                         ED_view3d_draw_depth_gpencil(scene, ar, v3d);
907                         break;
908         }
909 }
910
911 /* no 4x4 sampling, run #ED_view3d_autodist_init first */
912 bool ED_view3d_autodist_simple(
913         ARegion *ar, const int mval[2], float mouse_worldloc[3],
914         int margin, float *force_depth)
915 {
916         bglMats mats; /* ZBuffer depth vars, could cache? */
917         float depth;
918         double cent[2],  p[3];
919
920         /* Get Z Depths, needed for perspective, nice for ortho */
921         if (force_depth)
922                 depth = *force_depth;
923         else
924                 depth = view_autodist_depth_margin(ar, mval, margin);
925
926         if (depth == FLT_MAX)
927                 return false;
928
929         cent[0] = (double)mval[0] + 0.5;
930         cent[1] = (double)mval[1] + 0.5;
931
932         bgl_get_mats(&mats);
933
934         if (!gluUnProject(cent[0], cent[1], depth,
935                           mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
936         {
937                 return false;
938         }
939
940         mouse_worldloc[0] = (float)p[0];
941         mouse_worldloc[1] = (float)p[1];
942         mouse_worldloc[2] = (float)p[2];
943         return true;
944 }
945
946 bool ED_view3d_autodist_depth(ARegion *ar, const int mval[2], int margin, float *depth)
947 {
948         *depth = view_autodist_depth_margin(ar, mval, margin);
949
950         return (*depth != FLT_MAX);
951 }
952
953 static bool depth_segment_cb(int x, int y, void *userData)
954 {
955         struct { ARegion *ar; int margin; float depth; } *data = userData;
956         int mval[2];
957         float depth;
958
959         mval[0] = x;
960         mval[1] = y;
961
962         depth = view_autodist_depth_margin(data->ar, mval, data->margin);
963
964         if (depth != FLT_MAX) {
965                 data->depth = depth;
966                 return 0;
967         }
968         else {
969                 return 1;
970         }
971 }
972
973 bool ED_view3d_autodist_depth_seg(
974         ARegion *ar, const int mval_sta[2], const int mval_end[2],
975         int margin, float *depth)
976 {
977         struct { ARegion *ar; int margin; float depth; } data = {NULL};
978         int p1[2];
979         int p2[2];
980
981         data.ar = ar;
982         data.margin = margin;
983         data.depth = FLT_MAX;
984
985         copy_v2_v2_int(p1, mval_sta);
986         copy_v2_v2_int(p2, mval_end);
987
988         BLI_bitmap_draw_2d_line_v2v2i(p1, p2, depth_segment_cb, &data);
989
990         *depth = data.depth;
991
992         return (*depth != FLT_MAX);
993 }
994
995 /** \} */
996
997 /* -------------------------------------------------------------------- */
998 /** \name View Radius/Distance Utilities
999  *
1000  * Use to calculate a distance to a point based on it's radius.
1001  * \{ */
1002
1003 float ED_view3d_radius_to_dist_persp(const float angle, const float radius)
1004 {
1005         return radius * (1.0f / tanf(angle / 2.0f));
1006 }
1007
1008 float ED_view3d_radius_to_dist_ortho(const float lens, const float radius)
1009 {
1010         return radius / (DEFAULT_SENSOR_WIDTH / lens);
1011 }
1012
1013 /**
1014  * Return a new RegionView3D.dist value to fit the \a radius.
1015  *
1016  * \note Depth isn't taken into account, this will fit a flat plane exactly,
1017  * but points towards the view (with a perspective projection),
1018  * may be within the radius but outside the view. eg:
1019  *
1020  * <pre>
1021  *           +
1022  * pt --> + /^ radius
1023  *         / |
1024  *        /  |
1025  * view  +   +
1026  *        \  |
1027  *         \ |
1028  *          \|
1029  *           +
1030  * </pre>
1031  *
1032  * \param ar  Can be NULL if \a use_aspect is false.
1033  * \param persp  Allow the caller to tell what kind of perspective to use (ortho/view/camera)
1034  * \param use_aspect  Increase the distance to account for non 1:1 view aspect.
1035  * \param radius  The radius will be fitted exactly, typically pre-scaled by a margin (#VIEW3D_MARGIN).
1036  */
1037 float ED_view3d_radius_to_dist(
1038         const View3D *v3d, const ARegion *ar,
1039         const char persp, const bool use_aspect,
1040         const float radius)
1041 {
1042         float dist;
1043
1044         BLI_assert(ELEM(persp, RV3D_ORTHO, RV3D_PERSP, RV3D_CAMOB));
1045         BLI_assert((persp != RV3D_CAMOB) || v3d->camera);
1046
1047         if (persp == RV3D_ORTHO) {
1048                 dist = ED_view3d_radius_to_dist_ortho(v3d->lens, radius);
1049         }
1050         else {
1051                 float lens, sensor_size, zoom;
1052                 float angle;
1053
1054                 if (persp == RV3D_CAMOB) {
1055                         CameraParams params;
1056                         BKE_camera_params_init(&params);
1057                         params.clipsta = v3d->near;
1058                         params.clipend = v3d->far;
1059                         BKE_camera_params_from_object(&params, v3d->camera);
1060
1061                         lens = params.lens;
1062                         sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y);
1063
1064                         /* ignore 'rv3d->camzoom' because we want to fit to the cameras frame */
1065                         zoom = CAMERA_PARAM_ZOOM_INIT_CAMOB;
1066                 }
1067                 else {
1068                         lens = v3d->lens;
1069                         sensor_size = DEFAULT_SENSOR_WIDTH;
1070                         zoom = CAMERA_PARAM_ZOOM_INIT_PERSP;
1071                 }
1072
1073                 angle = focallength_to_fov(lens, sensor_size);
1074
1075                 /* zoom influences lens, correct this by scaling the angle as a distance (by the zoom-level) */
1076                 angle = atanf(tanf(angle / 2.0f) * zoom) * 2.0f;
1077
1078                 dist = ED_view3d_radius_to_dist_persp(angle, radius);
1079         }
1080
1081         if (use_aspect) {
1082                 const RegionView3D *rv3d = ar->regiondata;
1083
1084                 float winx, winy;
1085
1086                 if (persp == RV3D_CAMOB) {
1087                         /* camera frame x/y in pixels */
1088                         winx = ar->winx / rv3d->viewcamtexcofac[0];
1089                         winy = ar->winy / rv3d->viewcamtexcofac[1];
1090                 }
1091                 else {
1092                         winx = ar->winx;
1093                         winy = ar->winy;
1094                 }
1095
1096                 if (winx && winy) {
1097                         float aspect = winx / winy;
1098                         if (aspect < 1.0f) {
1099                                 aspect = 1.0f / aspect;
1100                         }
1101                         dist *= aspect;
1102                 }
1103         }
1104
1105         return dist;
1106 }
1107
1108 /** \} */
1109
1110 /* -------------------------------------------------------------------- */
1111 /** \name View Distance Utilities
1112  * \{ */
1113
1114 /* problem - ofs[3] can be on same location as camera itself.
1115  * Blender needs proper dist value for zoom.
1116  * use fallback_dist to override small values
1117  */
1118 float ED_view3d_offset_distance(float mat[4][4], const float ofs[3], const float fallback_dist)
1119 {
1120         float pos[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1121         float dir[4] = {0.0f, 0.0f, 1.0f, 0.0f};
1122         float dist;
1123
1124         mul_m4_v4(mat, pos);
1125         add_v3_v3(pos, ofs);
1126         mul_m4_v4(mat, dir);
1127         normalize_v3(dir);
1128
1129         dist = dot_v3v3(pos, dir);
1130
1131         if ((dist < FLT_EPSILON) && (fallback_dist != 0.0f)) {
1132                 dist = fallback_dist;
1133         }
1134
1135         return dist;
1136 }
1137
1138 /**
1139  * Set the dist without moving the view (compensate with #RegionView3D.ofs)
1140  *
1141  * \note take care that viewinv is up to date, #ED_view3d_update_viewmat first.
1142  */
1143 void ED_view3d_distance_set(RegionView3D *rv3d, const float dist)
1144 {
1145         float viewinv[4];
1146         float tvec[3];
1147
1148         BLI_assert(dist >= 0.0f);
1149
1150         copy_v3_fl3(tvec, 0.0f, 0.0f, rv3d->dist - dist);
1151         /* rv3d->viewinv isn't always valid */
1152 #if 0
1153         mul_mat3_m4_v3(rv3d->viewinv, tvec);
1154 #else
1155         invert_qt_qt_normalized(viewinv, rv3d->viewquat);
1156         mul_qt_v3(viewinv, tvec);
1157 #endif
1158         sub_v3_v3(rv3d->ofs, tvec);
1159
1160         rv3d->dist = dist;
1161 }
1162
1163 /** \} */
1164
1165 /* -------------------------------------------------------------------- */
1166 /** \name View Axis Utilities
1167  * \{ */
1168 static float view3d_quat_axis[6][4] = {
1169         {M_SQRT1_2, -M_SQRT1_2, 0.0f, 0.0f},    /* RV3D_VIEW_FRONT */
1170         {0.0f, 0.0f, -M_SQRT1_2, -M_SQRT1_2},   /* RV3D_VIEW_BACK */
1171         {0.5f, -0.5f, 0.5f, 0.5f},              /* RV3D_VIEW_LEFT */
1172         {0.5f, -0.5f, -0.5f, -0.5f},            /* RV3D_VIEW_RIGHT */
1173         {1.0f, 0.0f, 0.0f, 0.0f},               /* RV3D_VIEW_TOP */
1174         {0.0f, -1.0f, 0.0f, 0.0f},              /* RV3D_VIEW_BOTTOM */
1175 };
1176
1177
1178 bool ED_view3d_quat_from_axis_view(const char view, float quat[4])
1179 {
1180         if (RV3D_VIEW_IS_AXIS(view)) {
1181                 copy_qt_qt(quat, view3d_quat_axis[view - RV3D_VIEW_FRONT]);
1182                 return true;
1183         }
1184         else {
1185                 return false;
1186         }
1187 }
1188
1189 char ED_view3d_quat_to_axis_view(const float quat[4], const float epsilon)
1190 {
1191         /* quat values are all unit length */
1192
1193         char view;
1194
1195         for (view = RV3D_VIEW_FRONT; view <= RV3D_VIEW_BOTTOM; view++) {
1196                 if (fabsf(angle_signed_qtqt(quat, view3d_quat_axis[view - RV3D_VIEW_FRONT])) < epsilon) {
1197                         return view;
1198                 }
1199         }
1200
1201         return RV3D_VIEW_USER;
1202 }
1203
1204 char ED_view3d_lock_view_from_index(int index)
1205 {
1206         switch (index) {
1207                 case 0:  return RV3D_VIEW_FRONT;
1208                 case 1:  return RV3D_VIEW_TOP;
1209                 case 2:  return RV3D_VIEW_RIGHT;
1210                 default: return RV3D_VIEW_USER;
1211         }
1212
1213 }
1214
1215 char ED_view3d_axis_view_opposite(char view)
1216 {
1217         switch (view) {
1218                 case RV3D_VIEW_FRONT:   return RV3D_VIEW_BACK;
1219                 case RV3D_VIEW_BACK:    return RV3D_VIEW_FRONT;
1220                 case RV3D_VIEW_LEFT:    return RV3D_VIEW_RIGHT;
1221                 case RV3D_VIEW_RIGHT:   return RV3D_VIEW_LEFT;
1222                 case RV3D_VIEW_TOP:     return RV3D_VIEW_BOTTOM;
1223                 case RV3D_VIEW_BOTTOM:  return RV3D_VIEW_TOP;
1224         }
1225
1226         return RV3D_VIEW_USER;
1227 }
1228
1229
1230 bool ED_view3d_lock(RegionView3D *rv3d)
1231 {
1232         return ED_view3d_quat_from_axis_view(rv3d->view, rv3d->viewquat);
1233 }
1234
1235 /** \} */
1236
1237 /* -------------------------------------------------------------------- */
1238 /** \name View Transform Utilities
1239  * \{ */
1240
1241 /**
1242  * Set the view transformation from a 4x4 matrix.
1243  *
1244  * \param mat The view 4x4 transformation matrix to assign.
1245  * \param ofs The view offset, normally from RegionView3D.ofs.
1246  * \param quat The view rotation, quaternion normally from RegionView3D.viewquat.
1247  * \param dist The view distance from ofs, normally from RegionView3D.dist.
1248  */
1249 void ED_view3d_from_m4(float mat[4][4], float ofs[3], float quat[4], float *dist)
1250 {
1251         float nmat[3][3];
1252
1253         /* dist depends on offset */
1254         BLI_assert(dist == NULL || ofs != NULL);
1255
1256         copy_m3_m4(nmat, mat);
1257         normalize_m3(nmat);
1258
1259         /* Offset */
1260         if (ofs)
1261                 negate_v3_v3(ofs, mat[3]);
1262
1263         /* Quat */
1264         if (quat) {
1265                 mat3_normalized_to_quat(quat, nmat);
1266                 invert_qt_normalized(quat);
1267         }
1268
1269         if (ofs && dist) {
1270                 madd_v3_v3fl(ofs, nmat[2], *dist);
1271         }
1272 }
1273
1274 /**
1275  * Calculate the view transformation matrix from RegionView3D input.
1276  * The resulting matrix is equivalent to RegionView3D.viewinv
1277  * \param mat The view 4x4 transformation matrix to calculate.
1278  * \param ofs The view offset, normally from RegionView3D.ofs.
1279  * \param quat The view rotation, quaternion normally from RegionView3D.viewquat.
1280  * \param dist The view distance from ofs, normally from RegionView3D.dist.
1281  */
1282 void ED_view3d_to_m4(float mat[4][4], const float ofs[3], const float quat[4], const float dist)
1283 {
1284         float iviewquat[4] = {-quat[0], quat[1], quat[2], quat[3]};
1285         float dvec[3] = {0.0f, 0.0f, dist};
1286
1287         quat_to_mat4(mat, iviewquat);
1288         mul_mat3_m4_v3(mat, dvec);
1289         sub_v3_v3v3(mat[3], dvec, ofs);
1290 }
1291
1292 /**
1293  * Set the RegionView3D members from an objects transformation and optionally lens.
1294  * \param ob The object to set the view to.
1295  * \param ofs The view offset to be set, normally from RegionView3D.ofs.
1296  * \param quat The view rotation to be set, quaternion normally from RegionView3D.viewquat.
1297  * \param dist The view distance from ofs to be set, normally from RegionView3D.dist.
1298  * \param lens The view lens angle set for cameras and lamps, normally from View3D.lens.
1299  */
1300 void ED_view3d_from_object(Object *ob, float ofs[3], float quat[4], float *dist, float *lens)
1301 {
1302         ED_view3d_from_m4(ob->obmat, ofs, quat, dist);
1303
1304         if (lens) {
1305                 CameraParams params;
1306
1307                 BKE_camera_params_init(&params);
1308                 BKE_camera_params_from_object(&params, ob);
1309                 *lens = params.lens;
1310         }
1311 }
1312
1313 /**
1314  * Set the object transformation from RegionView3D members.
1315  * \param ob The object which has the transformation assigned.
1316  * \param ofs The view offset, normally from RegionView3D.ofs.
1317  * \param quat The view rotation, quaternion normally from RegionView3D.viewquat.
1318  * \param dist The view distance from ofs, normally from RegionView3D.dist.
1319  */
1320 void ED_view3d_to_object(Object *ob, const float ofs[3], const float quat[4], const float dist)
1321 {
1322         float mat[4][4];
1323         ED_view3d_to_m4(mat, ofs, quat, dist);
1324         BKE_object_apply_mat4(ob, mat, true, true);
1325 }
1326
1327 /** \} */
1328
1329 /* -------------------------------------------------------------------- */
1330 /** \name Depth Buffer Utilities
1331  * \{ */
1332
1333 float ED_view3d_depth_read_cached(const ViewContext *vc, const int mval[2])
1334 {
1335         ViewDepths *vd = vc->rv3d->depths;
1336
1337         int x = mval[0];
1338         int y = mval[1];
1339
1340         if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h) {
1341                 return vd->depths[y * vd->w + x];
1342         }
1343         else {
1344                 BLI_assert(1.0 <= vd->depth_range[1]);
1345                 return 1.0f;
1346         }
1347 }
1348
1349 bool ED_view3d_depth_read_cached_normal(
1350         const ViewContext *vc, const bglMats *mats, const int mval[2],
1351         float r_normal[3])
1352 {
1353         /* Note: we could support passing in a radius.
1354          * For now just read 9 pixels. */
1355
1356         /* pixels surrounding */
1357         bool  depths_valid[9] = {false};
1358         float coords[9][3] = {{0}};
1359
1360         ARegion *ar = vc->ar;
1361         const ViewDepths *depths = vc->rv3d->depths;
1362
1363         for (int x = 0, i = 0; x < 2; x++) {
1364                 for (int y = 0; y < 2; y++) {
1365                         const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)};
1366
1367                         const double depth = (double)ED_view3d_depth_read_cached(vc, mval_ofs);
1368                         if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
1369                                 if (ED_view3d_depth_unproject(ar, mats, mval_ofs, depth, coords[i])) {
1370                                         depths_valid[i] = true;
1371                                 }
1372                         }
1373                         i++;
1374                 }
1375         }
1376
1377         const int edges[2][6][2] = {
1378             /* x edges */
1379             {{0, 1}, {1, 2},
1380              {3, 4}, {4, 5},
1381              {6, 7}, {7, 8}},
1382             /* y edges */
1383             {{0, 3}, {3, 6},
1384              {1, 4}, {4, 7},
1385              {2, 5}, {5, 8}},
1386         };
1387
1388         float cross[2][3] = {{0.0f}};
1389
1390         for (int i = 0; i < 6; i++) {
1391                 for (int axis = 0; axis < 2; axis++) {
1392                         if (depths_valid[edges[axis][i][0]] && depths_valid[edges[axis][i][1]]) {
1393                                 float delta[3];
1394                                 sub_v3_v3v3(delta, coords[edges[axis][i][0]], coords[edges[axis][i][1]]);
1395                                 add_v3_v3(cross[axis], delta);
1396                         }
1397                 }
1398         }
1399
1400         cross_v3_v3v3(r_normal, cross[0], cross[1]);
1401
1402         if (normalize_v3(r_normal) != 0.0f) {
1403                 return true;
1404         }
1405         else {
1406                 return false;
1407         }
1408 }
1409
1410 bool ED_view3d_depth_unproject(
1411         const ARegion *ar, const bglMats *mats,
1412         const int mval[2], const double depth,
1413         float r_location_world[3])
1414 {
1415         double p[3];
1416         if (gluUnProject(
1417                 (double)ar->winrct.xmin + mval[0] + 0.5,
1418                 (double)ar->winrct.ymin + mval[1] + 0.5,
1419                 depth, mats->modelview, mats->projection, (const GLint *)mats->viewport,
1420                 &p[0], &p[1], &p[2]))
1421         {
1422                 copy_v3fl_v3db(r_location_world, p);
1423                 return true;
1424         }
1425         return false;
1426 }
1427
1428 void ED_view3d_depth_tag_update(RegionView3D *rv3d)
1429 {
1430         if (rv3d->depths)
1431                 rv3d->depths->damaged = true;
1432 }
1433
1434 /** \} */
1435
1436 /* -------------------------------------------------------------------- */
1437 /** \name Background Image Utilities
1438  * \{ */
1439
1440 BGpic *ED_view3d_background_image_new(View3D *v3d)
1441 {
1442         BGpic *bgpic = MEM_callocN(sizeof(BGpic), "Background Image");
1443
1444         bgpic->rotation = 0.0f;
1445         bgpic->size = 5.0f;
1446         bgpic->blend = 0.5f;
1447         bgpic->iuser.fie_ima = 2;
1448         bgpic->iuser.ok = 1;
1449         bgpic->view = 0; /* 0 for all */
1450         bgpic->flag |= V3D_BGPIC_EXPANDED;
1451
1452         BLI_addtail(&v3d->bgpicbase, bgpic);
1453
1454         return bgpic;
1455 }
1456
1457 void ED_view3d_background_image_remove(View3D *v3d, BGpic *bgpic)
1458 {
1459         BLI_remlink(&v3d->bgpicbase, bgpic);
1460
1461         MEM_freeN(bgpic);
1462 }
1463
1464 void ED_view3d_background_image_clear(View3D *v3d)
1465 {
1466         BGpic *bgpic = v3d->bgpicbase.first;
1467
1468         while (bgpic) {
1469                 BGpic *next_bgpic = bgpic->next;
1470
1471                 ED_view3d_background_image_remove(v3d, bgpic);
1472
1473                 bgpic = next_bgpic;
1474         }
1475 }
1476
1477 /** \} */