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