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 
24 #include "DNA_camera_types.h"
25 #include "DNA_object_types.h"
26 #include "DNA_scene_types.h"
27 #include "DNA_screen_types.h"
28 #include "DNA_view3d_types.h"
29 
30 #include "BLI_sys_types.h" /* int64_t */
31 
32 #include "BLI_math_vector.h"
33 
34 #include "BKE_camera.h"
35 #include "BKE_screen.h"
36 
37 #include "GPU_matrix.h"
38 
39 #include "ED_view3d.h" /* own include */
40 
41 #define BL_NEAR_CLIP 0.001
42 #define BL_ZERO_CLIP 0.001
43 
44 /* Non Clipping Projection Functions
45  * ********************************* */
46 
47 /**
48  * \note use #ED_view3d_ob_project_mat_get to get the projection matrix
49  */
ED_view3d_project_float_v2_m4(const ARegion * region,const float co[3],float r_co[2],float mat[4][4])50 void ED_view3d_project_float_v2_m4(const ARegion *region,
51                                    const float co[3],
52                                    float r_co[2],
53                                    float mat[4][4])
54 {
55   float vec4[4];
56 
57   copy_v3_v3(vec4, co);
58   vec4[3] = 1.0;
59   /* r_co[0] = IS_CLIPPED; */ /* always overwritten */
60 
61   mul_m4_v4(mat, vec4);
62 
63   if (vec4[3] > FLT_EPSILON) {
64     r_co[0] = (float)(region->winx / 2.0f) + (region->winx / 2.0f) * vec4[0] / vec4[3];
65     r_co[1] = (float)(region->winy / 2.0f) + (region->winy / 2.0f) * vec4[1] / vec4[3];
66   }
67   else {
68     zero_v2(r_co);
69   }
70 }
71 
72 /**
73  * \note use #ED_view3d_ob_project_mat_get to get projecting mat
74  */
ED_view3d_project_float_v3_m4(const ARegion * region,const float co[3],float r_co[3],float mat[4][4])75 void ED_view3d_project_float_v3_m4(const ARegion *region,
76                                    const float co[3],
77                                    float r_co[3],
78                                    float mat[4][4])
79 {
80   float vec4[4];
81 
82   copy_v3_v3(vec4, co);
83   vec4[3] = 1.0;
84   /* r_co[0] = IS_CLIPPED; */ /* always overwritten */
85 
86   mul_m4_v4(mat, vec4);
87 
88   if (vec4[3] > FLT_EPSILON) {
89     r_co[0] = (float)(region->winx / 2.0f) + (region->winx / 2.0f) * vec4[0] / vec4[3];
90     r_co[1] = (float)(region->winy / 2.0f) + (region->winy / 2.0f) * vec4[1] / vec4[3];
91     r_co[2] = vec4[2] / vec4[3];
92   }
93   else {
94     zero_v3(r_co);
95   }
96 }
97 
98 /* Clipping Projection Functions
99  * ***************************** */
100 
ED_view3d_project_base(const struct ARegion * region,struct Base * base)101 eV3DProjStatus ED_view3d_project_base(const struct ARegion *region, struct Base *base)
102 {
103   eV3DProjStatus ret = ED_view3d_project_short_global(
104       region, base->object->obmat[3], &base->sx, V3D_PROJ_TEST_CLIP_DEFAULT);
105 
106   if (ret != V3D_PROJ_RET_OK) {
107     base->sx = IS_CLIPPED;
108     base->sy = 0;
109   }
110 
111   return ret;
112 }
113 
114 /* perspmat is typically...
115  * - 'rv3d->perspmat',   is_local == false
116  * - 'rv3d->persmatob', is_local == true
117  */
ed_view3d_project__internal(const ARegion * region,const float perspmat[4][4],const bool is_local,const float co[3],float r_co[2],const eV3DProjTest flag)118 static eV3DProjStatus ed_view3d_project__internal(const ARegion *region,
119                                                   const float perspmat[4][4],
120                                                   const bool is_local, /* normally hidden */
121                                                   const float co[3],
122                                                   float r_co[2],
123                                                   const eV3DProjTest flag)
124 {
125   float vec4[4];
126 
127   /* check for bad flags */
128   BLI_assert((flag & V3D_PROJ_TEST_ALL) == flag);
129 
130   if (flag & V3D_PROJ_TEST_CLIP_BB) {
131     RegionView3D *rv3d = region->regiondata;
132     if (rv3d->rflag & RV3D_CLIPPING) {
133       if (ED_view3d_clipping_test(rv3d, co, is_local)) {
134         return V3D_PROJ_RET_CLIP_BB;
135       }
136     }
137   }
138 
139   copy_v3_v3(vec4, co);
140   vec4[3] = 1.0;
141   mul_m4_v4(perspmat, vec4);
142 
143   if (((flag & V3D_PROJ_TEST_CLIP_ZERO) == 0) || (fabsf(vec4[3]) > (float)BL_ZERO_CLIP)) {
144     if (((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) || (vec4[3] > (float)BL_NEAR_CLIP)) {
145       const float scalar = (vec4[3] != 0.0f) ? (1.0f / vec4[3]) : 0.0f;
146       const float fx = ((float)region->winx / 2.0f) * (1.0f + (vec4[0] * scalar));
147       if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0.0f && fx < (float)region->winx)) {
148         const float fy = ((float)region->winy / 2.0f) * (1.0f + (vec4[1] * scalar));
149         if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)region->winy)) {
150           r_co[0] = fx;
151           r_co[1] = fy;
152 
153           /* check if the point is behind the view, we need to flip in this case */
154           if (UNLIKELY((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) && (vec4[3] < 0.0f)) {
155             negate_v2(r_co);
156           }
157         }
158         else {
159           return V3D_PROJ_RET_CLIP_WIN;
160         }
161       }
162       else {
163         return V3D_PROJ_RET_CLIP_WIN;
164       }
165     }
166     else {
167       return V3D_PROJ_RET_CLIP_NEAR;
168     }
169   }
170   else {
171     return V3D_PROJ_RET_CLIP_ZERO;
172   }
173 
174   return V3D_PROJ_RET_OK;
175 }
176 
ED_view3d_project_short_ex(const ARegion * region,float perspmat[4][4],const bool is_local,const float co[3],short r_co[2],const eV3DProjTest flag)177 eV3DProjStatus ED_view3d_project_short_ex(const ARegion *region,
178                                           float perspmat[4][4],
179                                           const bool is_local,
180                                           const float co[3],
181                                           short r_co[2],
182                                           const eV3DProjTest flag)
183 {
184   float tvec[2];
185   eV3DProjStatus ret = ed_view3d_project__internal(region, perspmat, is_local, co, tvec, flag);
186   if (ret == V3D_PROJ_RET_OK) {
187     if ((tvec[0] > -32700.0f && tvec[0] < 32700.0f) &&
188         (tvec[1] > -32700.0f && tvec[1] < 32700.0f)) {
189       r_co[0] = (short)floorf(tvec[0]);
190       r_co[1] = (short)floorf(tvec[1]);
191     }
192     else {
193       ret = V3D_PROJ_RET_OVERFLOW;
194     }
195   }
196   return ret;
197 }
198 
ED_view3d_project_int_ex(const ARegion * region,float perspmat[4][4],const bool is_local,const float co[3],int r_co[2],const eV3DProjTest flag)199 eV3DProjStatus ED_view3d_project_int_ex(const ARegion *region,
200                                         float perspmat[4][4],
201                                         const bool is_local,
202                                         const float co[3],
203                                         int r_co[2],
204                                         const eV3DProjTest flag)
205 {
206   float tvec[2];
207   eV3DProjStatus ret = ed_view3d_project__internal(region, perspmat, is_local, co, tvec, flag);
208   if (ret == V3D_PROJ_RET_OK) {
209     if ((tvec[0] > -2140000000.0f && tvec[0] < 2140000000.0f) &&
210         (tvec[1] > -2140000000.0f && tvec[1] < 2140000000.0f)) {
211       r_co[0] = (int)floorf(tvec[0]);
212       r_co[1] = (int)floorf(tvec[1]);
213     }
214     else {
215       ret = V3D_PROJ_RET_OVERFLOW;
216     }
217   }
218   return ret;
219 }
220 
ED_view3d_project_float_ex(const ARegion * region,float perspmat[4][4],const bool is_local,const float co[3],float r_co[2],const eV3DProjTest flag)221 eV3DProjStatus ED_view3d_project_float_ex(const ARegion *region,
222                                           float perspmat[4][4],
223                                           const bool is_local,
224                                           const float co[3],
225                                           float r_co[2],
226                                           const eV3DProjTest flag)
227 {
228   float tvec[2];
229   eV3DProjStatus ret = ed_view3d_project__internal(region, perspmat, is_local, co, tvec, flag);
230   if (ret == V3D_PROJ_RET_OK) {
231     if (isfinite(tvec[0]) && isfinite(tvec[1])) {
232       copy_v2_v2(r_co, tvec);
233     }
234     else {
235       ret = V3D_PROJ_RET_OVERFLOW;
236     }
237   }
238   return ret;
239 }
240 
241 /* --- short --- */
ED_view3d_project_short_global(const ARegion * region,const float co[3],short r_co[2],const eV3DProjTest flag)242 eV3DProjStatus ED_view3d_project_short_global(const ARegion *region,
243                                               const float co[3],
244                                               short r_co[2],
245                                               const eV3DProjTest flag)
246 {
247   RegionView3D *rv3d = region->regiondata;
248   return ED_view3d_project_short_ex(region, rv3d->persmat, false, co, r_co, flag);
249 }
250 /* object space, use ED_view3d_init_mats_rv3d before calling */
ED_view3d_project_short_object(const ARegion * region,const float co[3],short r_co[2],const eV3DProjTest flag)251 eV3DProjStatus ED_view3d_project_short_object(const ARegion *region,
252                                               const float co[3],
253                                               short r_co[2],
254                                               const eV3DProjTest flag)
255 {
256   RegionView3D *rv3d = region->regiondata;
257   ED_view3d_check_mats_rv3d(rv3d);
258   return ED_view3d_project_short_ex(region, rv3d->persmatob, true, co, r_co, flag);
259 }
260 
261 /* --- int --- */
ED_view3d_project_int_global(const ARegion * region,const float co[3],int r_co[2],const eV3DProjTest flag)262 eV3DProjStatus ED_view3d_project_int_global(const ARegion *region,
263                                             const float co[3],
264                                             int r_co[2],
265                                             const eV3DProjTest flag)
266 {
267   RegionView3D *rv3d = region->regiondata;
268   return ED_view3d_project_int_ex(region, rv3d->persmat, false, co, r_co, flag);
269 }
270 /* object space, use ED_view3d_init_mats_rv3d before calling */
ED_view3d_project_int_object(const ARegion * region,const float co[3],int r_co[2],const eV3DProjTest flag)271 eV3DProjStatus ED_view3d_project_int_object(const ARegion *region,
272                                             const float co[3],
273                                             int r_co[2],
274                                             const eV3DProjTest flag)
275 {
276   RegionView3D *rv3d = region->regiondata;
277   ED_view3d_check_mats_rv3d(rv3d);
278   return ED_view3d_project_int_ex(region, rv3d->persmatob, true, co, r_co, flag);
279 }
280 
281 /* --- float --- */
ED_view3d_project_float_global(const ARegion * region,const float co[3],float r_co[2],const eV3DProjTest flag)282 eV3DProjStatus ED_view3d_project_float_global(const ARegion *region,
283                                               const float co[3],
284                                               float r_co[2],
285                                               const eV3DProjTest flag)
286 {
287   RegionView3D *rv3d = region->regiondata;
288   return ED_view3d_project_float_ex(region, rv3d->persmat, false, co, r_co, flag);
289 }
290 /* object space, use ED_view3d_init_mats_rv3d before calling */
ED_view3d_project_float_object(const ARegion * region,const float co[3],float r_co[2],const eV3DProjTest flag)291 eV3DProjStatus ED_view3d_project_float_object(const ARegion *region,
292                                               const float co[3],
293                                               float r_co[2],
294                                               const eV3DProjTest flag)
295 {
296   RegionView3D *rv3d = region->regiondata;
297   ED_view3d_check_mats_rv3d(rv3d);
298   return ED_view3d_project_float_ex(region, rv3d->persmatob, true, co, r_co, flag);
299 }
300 
301 /* More Generic Window/Ray/Vector projection functions
302  * *************************************************** */
303 
ED_view3d_pixel_size(const RegionView3D * rv3d,const float co[3])304 float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
305 {
306   return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize * U.pixelsize;
307 }
308 
ED_view3d_pixel_size_no_ui_scale(const RegionView3D * rv3d,const float co[3])309 float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
310 {
311   return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize;
312 }
313 
314 /**
315  * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta
316  */
ED_view3d_calc_zfac(const RegionView3D * rv3d,const float co[3],bool * r_flip)317 float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_flip)
318 {
319   float zfac = mul_project_m4_v3_zfac(rv3d->persmat, co);
320 
321   if (r_flip) {
322     *r_flip = (zfac < 0.0f);
323   }
324 
325   /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that
326    * (accounting for near zero values) */
327   if (zfac < 1.e-6f && zfac > -1.e-6f) {
328     zfac = 1.0f;
329   }
330 
331   /* Negative zfac means x, y, z was behind the camera (in perspective).
332    * This gives flipped directions, so revert back to ok default case. */
333   if (zfac < 0.0f) {
334     zfac = -zfac;
335   }
336 
337   return zfac;
338 }
339 
view3d_win_to_ray_segment(struct Depsgraph * depsgraph,const ARegion * region,const View3D * v3d,const float mval[2],float r_ray_co[3],float r_ray_dir[3],float r_ray_start[3],float r_ray_end[3])340 static void view3d_win_to_ray_segment(struct Depsgraph *depsgraph,
341                                       const ARegion *region,
342                                       const View3D *v3d,
343                                       const float mval[2],
344                                       float r_ray_co[3],
345                                       float r_ray_dir[3],
346                                       float r_ray_start[3],
347                                       float r_ray_end[3])
348 {
349   RegionView3D *rv3d = region->regiondata;
350   float _ray_co[3], _ray_dir[3], start_offset, end_offset;
351 
352   if (!r_ray_co) {
353     r_ray_co = _ray_co;
354   }
355   if (!r_ray_dir) {
356     r_ray_dir = _ray_dir;
357   }
358 
359   ED_view3d_win_to_origin(region, mval, r_ray_co);
360   ED_view3d_win_to_vector(region, mval, r_ray_dir);
361 
362   if ((rv3d->is_persp == false) && (rv3d->persp != RV3D_CAMOB)) {
363     end_offset = v3d->clip_end / 2.0f;
364     start_offset = -end_offset;
365   }
366   else {
367     ED_view3d_clip_range_get(depsgraph, v3d, rv3d, &start_offset, &end_offset, false);
368   }
369 
370   if (r_ray_start) {
371     madd_v3_v3v3fl(r_ray_start, r_ray_co, r_ray_dir, start_offset);
372   }
373   if (r_ray_end) {
374     madd_v3_v3v3fl(r_ray_end, r_ray_co, r_ray_dir, end_offset);
375   }
376 }
377 
ED_view3d_clip_segment(const RegionView3D * rv3d,float ray_start[3],float ray_end[3])378 bool ED_view3d_clip_segment(const RegionView3D *rv3d, float ray_start[3], float ray_end[3])
379 {
380   if ((rv3d->rflag & RV3D_CLIPPING) &&
381       (clip_segment_v3_plane_n(ray_start, ray_end, rv3d->clip, 6, ray_start, ray_end) == false)) {
382     return false;
383   }
384   return true;
385 }
386 
387 /**
388  * Calculate a 3d viewpoint and direction vector from 2d window coordinates.
389  * This ray_start is located at the viewpoint, ray_normal is the direction towards mval.
390  * ray_start is clipped by the view near limit so points in front of it are always in view.
391  * In orthographic view the resulting ray_normal will match the view vector.
392  * This version also returns the ray_co point of the ray on window plane, useful to fix precision
393  * issues esp. with ortho view, where default ray_start is set rather far away.
394  * \param region: The region (used for the window width and height).
395  * \param v3d: The 3d viewport (used for near clipping value).
396  * \param mval: The area relative 2d location (such as event->mval, converted into float[2]).
397  * \param r_ray_co: The world-space point where the ray intersects the window plane.
398  * \param r_ray_normal: The normalized world-space direction of towards mval.
399  * \param r_ray_start: The world-space starting point of the ray.
400  * \param do_clip_planes: Optionally clip the start of the ray by the view clipping planes.
401  * \return success, false if the ray is totally clipped.
402  */
ED_view3d_win_to_ray_clipped_ex(struct Depsgraph * depsgraph,const ARegion * region,const View3D * v3d,const float mval[2],float r_ray_co[3],float r_ray_normal[3],float r_ray_start[3],bool do_clip_planes)403 bool ED_view3d_win_to_ray_clipped_ex(struct Depsgraph *depsgraph,
404                                      const ARegion *region,
405                                      const View3D *v3d,
406                                      const float mval[2],
407                                      float r_ray_co[3],
408                                      float r_ray_normal[3],
409                                      float r_ray_start[3],
410                                      bool do_clip_planes)
411 {
412   float ray_end[3];
413 
414   view3d_win_to_ray_segment(
415       depsgraph, region, v3d, mval, r_ray_co, r_ray_normal, r_ray_start, ray_end);
416 
417   /* bounds clipping */
418   if (do_clip_planes) {
419     return ED_view3d_clip_segment(region->regiondata, r_ray_start, ray_end);
420   }
421 
422   return true;
423 }
424 
425 /**
426  * Calculate a 3d viewpoint and direction vector from 2d window coordinates.
427  * This ray_start is located at the viewpoint, ray_normal is the direction towards mval.
428  * ray_start is clipped by the view near limit so points in front of it are always in view.
429  * In orthographic view the resulting ray_normal will match the view vector.
430  * \param region: The region (used for the window width and height).
431  * \param v3d: The 3d viewport (used for near clipping value).
432  * \param mval: The area relative 2d location (such as event->mval, converted into float[2]).
433  * \param r_ray_start: The world-space point where the ray intersects the window plane.
434  * \param r_ray_normal: The normalized world-space direction of towards mval.
435  * \param do_clip_planes: Optionally clip the start of the ray by the view clipping planes.
436  * \return success, false if the ray is totally clipped.
437  */
ED_view3d_win_to_ray_clipped(struct Depsgraph * depsgraph,const ARegion * region,const View3D * v3d,const float mval[2],float r_ray_start[3],float r_ray_normal[3],const bool do_clip_planes)438 bool ED_view3d_win_to_ray_clipped(struct Depsgraph *depsgraph,
439                                   const ARegion *region,
440                                   const View3D *v3d,
441                                   const float mval[2],
442                                   float r_ray_start[3],
443                                   float r_ray_normal[3],
444                                   const bool do_clip_planes)
445 {
446   return ED_view3d_win_to_ray_clipped_ex(
447       depsgraph, region, v3d, mval, NULL, r_ray_normal, r_ray_start, do_clip_planes);
448 }
449 
450 /**
451  * Calculate a 3d viewpoint and direction vector from 2d window coordinates.
452  * This ray_start is located at the viewpoint, ray_normal is the direction towards mval.
453  * \param region: The region (used for the window width and height).
454  * \param mval: The area relative 2d location (such as event->mval, converted into float[2]).
455  * \param r_ray_start: The world-space point where the ray intersects the window plane.
456  * \param r_ray_normal: The normalized world-space direction of towards mval.
457  *
458  * \note Ignores view near/far clipping,
459  * to take this into account use #ED_view3d_win_to_ray_clipped.
460  */
ED_view3d_win_to_ray(const ARegion * region,const float mval[2],float r_ray_start[3],float r_ray_normal[3])461 void ED_view3d_win_to_ray(const ARegion *region,
462                           const float mval[2],
463                           float r_ray_start[3],
464                           float r_ray_normal[3])
465 {
466   ED_view3d_win_to_origin(region, mval, r_ray_start);
467   ED_view3d_win_to_vector(region, mval, r_ray_normal);
468 }
469 
470 /**
471  * Calculate a normalized 3d direction vector from the viewpoint towards a global location.
472  * In orthographic view the resulting vector will match the view vector.
473  * \param rv3d: The region (used for the window width and height).
474  * \param coord: The world-space location.
475  * \param vec: The resulting normalized vector.
476  */
ED_view3d_global_to_vector(const RegionView3D * rv3d,const float coord[3],float vec[3])477 void ED_view3d_global_to_vector(const RegionView3D *rv3d, const float coord[3], float vec[3])
478 {
479   if (rv3d->is_persp) {
480     float p1[4], p2[4];
481 
482     copy_v3_v3(p1, coord);
483     p1[3] = 1.0f;
484     copy_v3_v3(p2, p1);
485     p2[3] = 1.0f;
486     mul_m4_v4(rv3d->viewmat, p2);
487 
488     mul_v3_fl(p2, 2.0f);
489 
490     mul_m4_v4(rv3d->viewinv, p2);
491 
492     sub_v3_v3v3(vec, p1, p2);
493   }
494   else {
495     copy_v3_v3(vec, rv3d->viewinv[2]);
496   }
497   normalize_v3(vec);
498 }
499 
500 /* very similar to ED_view3d_win_to_3d() but has no advantage, de-duplicating */
501 #if 0
502 bool view3d_get_view_aligned_coordinate(ARegion *region,
503                                         float fp[3],
504                                         const int mval[2],
505                                         const bool do_fallback)
506 {
507   RegionView3D *rv3d = region->regiondata;
508   float dvec[3];
509   int mval_cpy[2];
510   eV3DProjStatus ret;
511 
512   ret = ED_view3d_project_int_global(region, fp, mval_cpy, V3D_PROJ_TEST_NOP);
513 
514   if (ret == V3D_PROJ_RET_OK) {
515     const float mval_f[2] = {(float)(mval_cpy[0] - mval[0]), (float)(mval_cpy[1] - mval[1])};
516     const float zfac = ED_view3d_calc_zfac(rv3d, fp, NULL);
517     ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
518     sub_v3_v3(fp, dvec);
519 
520     return true;
521   }
522   else {
523     /* fallback to the view center */
524     if (do_fallback) {
525       negate_v3_v3(fp, rv3d->ofs);
526       return view3d_get_view_aligned_coordinate(region, fp, mval, false);
527     }
528     else {
529       return false;
530     }
531   }
532 }
533 #endif
534 
535 /**
536  * Calculate a 3d location from 2d window coordinates.
537  * \param region: The region (used for the window width and height).
538  * \param depth_pt: The reference location used to calculate the Z depth.
539  * \param mval: The area relative location (such as event->mval converted to floats).
540  * \param r_out: The resulting world-space location.
541  */
ED_view3d_win_to_3d(const View3D * v3d,const ARegion * region,const float depth_pt[3],const float mval[2],float r_out[3])542 void ED_view3d_win_to_3d(const View3D *v3d,
543                          const ARegion *region,
544                          const float depth_pt[3],
545                          const float mval[2],
546                          float r_out[3])
547 {
548   RegionView3D *rv3d = region->regiondata;
549 
550   float ray_origin[3];
551   float ray_direction[3];
552   float lambda;
553 
554   if (rv3d->is_persp) {
555     float plane[4];
556 
557     copy_v3_v3(ray_origin, rv3d->viewinv[3]);
558     ED_view3d_win_to_vector(region, mval, ray_direction);
559 
560     /* Note: we could use #isect_line_plane_v3()
561      * however we want the intersection to be in front of the view no matter what,
562      * so apply the unsigned factor instead. */
563     plane_from_point_normal_v3(plane, depth_pt, rv3d->viewinv[2]);
564 
565     isect_ray_plane_v3(ray_origin, ray_direction, plane, &lambda, false);
566     lambda = fabsf(lambda);
567   }
568   else {
569     float dx = (2.0f * mval[0] / (float)region->winx) - 1.0f;
570     float dy = (2.0f * mval[1] / (float)region->winy) - 1.0f;
571 
572     if (rv3d->persp == RV3D_CAMOB) {
573       /* ortho camera needs offset applied */
574       const Camera *cam = v3d->camera->data;
575       const int sensor_fit = BKE_camera_sensor_fit(cam->sensor_fit, region->winx, region->winy);
576       const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 4.0f;
577       const float aspx = region->winx / (float)region->winy;
578       const float aspy = region->winy / (float)region->winx;
579       const float shiftx = cam->shiftx * 0.5f *
580                            (sensor_fit == CAMERA_SENSOR_FIT_HOR ? 1.0f : aspy);
581       const float shifty = cam->shifty * 0.5f *
582                            (sensor_fit == CAMERA_SENSOR_FIT_HOR ? aspx : 1.0f);
583 
584       dx += (rv3d->camdx + shiftx) * zoomfac;
585       dy += (rv3d->camdy + shifty) * zoomfac;
586     }
587     ray_origin[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
588     ray_origin[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
589     ray_origin[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
590 
591     copy_v3_v3(ray_direction, rv3d->viewinv[2]);
592     lambda = ray_point_factor_v3(depth_pt, ray_origin, ray_direction);
593   }
594 
595   madd_v3_v3v3fl(r_out, ray_origin, ray_direction, lambda);
596 }
597 
ED_view3d_win_to_3d_int(const View3D * v3d,const ARegion * region,const float depth_pt[3],const int mval[2],float r_out[3])598 void ED_view3d_win_to_3d_int(const View3D *v3d,
599                              const ARegion *region,
600                              const float depth_pt[3],
601                              const int mval[2],
602                              float r_out[3])
603 {
604   const float mval_fl[2] = {mval[0], mval[1]};
605   ED_view3d_win_to_3d(v3d, region, depth_pt, mval_fl, r_out);
606 }
607 
ED_view3d_win_to_3d_on_plane(const ARegion * region,const float plane[4],const float mval[2],const bool do_clip,float r_out[3])608 bool ED_view3d_win_to_3d_on_plane(const ARegion *region,
609                                   const float plane[4],
610                                   const float mval[2],
611                                   const bool do_clip,
612                                   float r_out[3])
613 {
614   float ray_co[3], ray_no[3];
615   ED_view3d_win_to_origin(region, mval, ray_co);
616   ED_view3d_win_to_vector(region, mval, ray_no);
617   float lambda;
618   if (isect_ray_plane_v3(ray_co, ray_no, plane, &lambda, do_clip)) {
619     madd_v3_v3v3fl(r_out, ray_co, ray_no, lambda);
620     return true;
621   }
622   return false;
623 }
624 
ED_view3d_win_to_3d_on_plane_int(const ARegion * region,const float plane[4],const int mval[2],const bool do_clip,float r_out[3])625 bool ED_view3d_win_to_3d_on_plane_int(const ARegion *region,
626                                       const float plane[4],
627                                       const int mval[2],
628                                       const bool do_clip,
629                                       float r_out[3])
630 {
631   const float mval_fl[2] = {mval[0], mval[1]};
632   return ED_view3d_win_to_3d_on_plane(region, plane, mval_fl, do_clip, r_out);
633 }
634 
635 /**
636  * Calculate a 3d difference vector from 2d window offset.
637  * note that #ED_view3d_calc_zfac() must be called first to determine
638  * the depth used to calculate the delta.
639  * \param region: The region (used for the window width and height).
640  * \param mval: The area relative 2d difference (such as event->mval[0] - other_x).
641  * \param out: The resulting world-space delta.
642  */
ED_view3d_win_to_delta(const ARegion * region,const float mval[2],float out[3],const float zfac)643 void ED_view3d_win_to_delta(const ARegion *region,
644                             const float mval[2],
645                             float out[3],
646                             const float zfac)
647 {
648   RegionView3D *rv3d = region->regiondata;
649   float dx, dy;
650 
651   dx = 2.0f * mval[0] * zfac / region->winx;
652   dy = 2.0f * mval[1] * zfac / region->winy;
653 
654   out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy);
655   out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy);
656   out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy);
657 }
658 
659 /**
660  * Calculate a 3d origin from 2d window coordinates.
661  * \note Orthographic views have a less obvious origin,
662  * Since far clip can be a very large value resulting in numeric precision issues,
663  * the origin in this case is close to zero coordinate.
664  *
665  * \param region: The region (used for the window width and height).
666  * \param mval: The area relative 2d location (such as event->mval converted to floats).
667  * \param out: The resulting normalized world-space direction vector.
668  */
ED_view3d_win_to_origin(const ARegion * region,const float mval[2],float out[3])669 void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float out[3])
670 {
671   RegionView3D *rv3d = region->regiondata;
672   if (rv3d->is_persp) {
673     copy_v3_v3(out, rv3d->viewinv[3]);
674   }
675   else {
676     out[0] = 2.0f * mval[0] / region->winx - 1.0f;
677     out[1] = 2.0f * mval[1] / region->winy - 1.0f;
678 
679     if (rv3d->persp == RV3D_CAMOB) {
680       out[2] = -1.0f;
681     }
682     else {
683       out[2] = 0.0f;
684     }
685 
686     mul_project_m4_v3(rv3d->persinv, out);
687   }
688 }
689 
690 /**
691  * Calculate a 3d direction vector from 2d window coordinates.
692  * This direction vector starts and the view in the direction of the 2d window coordinates.
693  * In orthographic view all window coordinates yield the same vector.
694  *
695  * \note doesn't rely on ED_view3d_calc_zfac
696  * for perspective view, get the vector direction to
697  * the mouse cursor as a normalized vector.
698  *
699  * \param region: The region (used for the window width and height).
700  * \param mval: The area relative 2d location (such as event->mval converted to floats).
701  * \param out: The resulting normalized world-space direction vector.
702  */
ED_view3d_win_to_vector(const ARegion * region,const float mval[2],float out[3])703 void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float out[3])
704 {
705   RegionView3D *rv3d = region->regiondata;
706 
707   if (rv3d->is_persp) {
708     out[0] = 2.0f * (mval[0] / region->winx) - 1.0f;
709     out[1] = 2.0f * (mval[1] / region->winy) - 1.0f;
710     out[2] = -0.5f;
711     mul_project_m4_v3(rv3d->persinv, out);
712     sub_v3_v3(out, rv3d->viewinv[3]);
713   }
714   else {
715     negate_v3_v3(out, rv3d->viewinv[2]);
716   }
717   normalize_v3(out);
718 }
719 
720 /**
721  * Calculate a 3d segment from 2d window coordinates.
722  * This ray_start is located at the viewpoint, ray_end is a far point.
723  * ray_start and ray_end are clipped by the view near and far limits
724  * so points along this line are always in view.
725  * In orthographic view all resulting segments will be parallel.
726  * \param region: The region (used for the window width and height).
727  * \param v3d: The 3d viewport (used for near and far clipping range).
728  * \param mval: The area relative 2d location (such as event->mval, converted into float[2]).
729  * \param r_ray_start: The world-space starting point of the segment.
730  * \param r_ray_end: The world-space end point of the segment.
731  * \param do_clip_planes: Optionally clip the ray by the view clipping planes.
732  * \return success, false if the segment is totally clipped.
733  */
ED_view3d_win_to_segment_clipped(struct Depsgraph * depsgraph,const ARegion * region,View3D * v3d,const float mval[2],float r_ray_start[3],float r_ray_end[3],const bool do_clip_planes)734 bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph,
735                                       const ARegion *region,
736                                       View3D *v3d,
737                                       const float mval[2],
738                                       float r_ray_start[3],
739                                       float r_ray_end[3],
740                                       const bool do_clip_planes)
741 {
742   view3d_win_to_ray_segment(depsgraph, region, v3d, mval, NULL, NULL, r_ray_start, r_ray_end);
743 
744   /* bounds clipping */
745   if (do_clip_planes) {
746     return ED_view3d_clip_segment((RegionView3D *)region->regiondata, r_ray_start, r_ray_end);
747   }
748 
749   return true;
750 }
751 
752 /* -------------------------------------------------------------------- */
753 /** \name Utility functions for projection
754  * \{ */
755 
ED_view3d_ob_project_mat_get(const RegionView3D * rv3d,Object * ob,float r_pmat[4][4])756 void ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, Object *ob, float r_pmat[4][4])
757 {
758   float vmat[4][4];
759 
760   mul_m4_m4m4(vmat, rv3d->viewmat, ob->obmat);
761   mul_m4_m4m4(r_pmat, rv3d->winmat, vmat);
762 }
763 
ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D * rv3d,const float obmat[4][4],float r_pmat[4][4])764 void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d,
765                                              const float obmat[4][4],
766                                              float r_pmat[4][4])
767 {
768   float vmat[4][4];
769 
770   mul_m4_m4m4(vmat, rv3d->viewmat, obmat);
771   mul_m4_m4m4(r_pmat, rv3d->winmat, vmat);
772 }
773 
774 /**
775  * Convert between region relative coordinates (x,y) and depth component z and
776  * a point in world space. */
ED_view3d_project(const struct ARegion * region,const float world[3],float r_region_co[3])777 void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3])
778 {
779   /* Viewport is set up to make coordinates relative to the region, not window. */
780   RegionView3D *rv3d = region->regiondata;
781   const int viewport[4] = {0, 0, region->winx, region->winy};
782 
783   GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
784 }
785 
ED_view3d_unproject(const struct ARegion * region,float regionx,float regiony,float regionz,float world[3])786 bool ED_view3d_unproject(
787     const struct ARegion *region, float regionx, float regiony, float regionz, float world[3])
788 {
789   RegionView3D *rv3d = region->regiondata;
790   const int viewport[4] = {0, 0, region->winx, region->winy};
791   const float region_co[3] = {regionx, regiony, regionz};
792 
793   return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world);
794 }
795 
796 /** \} */
797