Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_view3d / view3d_project.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_view3d/view3d_project.c
28  *  \ingroup spview3d
29  */
30
31 #include "DNA_camera_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_view3d_types.h"
36
37 #include "BLI_sys_types.h"  /* int64_t */
38
39 #include "BLI_math_vector.h"
40
41 #include "BKE_camera.h"
42 #include "BKE_screen.h"
43
44 #include "GPU_matrix.h"
45
46 #include "ED_view3d.h"  /* own include */
47
48 #define BL_NEAR_CLIP 0.001
49 #define BL_ZERO_CLIP 0.001
50
51 /* Non Clipping Projection Functions
52  * ********************************* */
53
54 /**
55  * \note use #ED_view3d_ob_project_mat_get to get the projection matrix
56  */
57 void ED_view3d_project_float_v2_m4(const ARegion *ar, const float co[3], float r_co[2], float mat[4][4])
58 {
59         float vec4[4];
60
61         copy_v3_v3(vec4, co);
62         vec4[3] = 1.0;
63         /* r_co[0] = IS_CLIPPED; */ /* always overwritten */
64
65         mul_m4_v4(mat, vec4);
66
67         if (vec4[3] > FLT_EPSILON) {
68                 r_co[0] = (float)(ar->winx / 2.0f) + (ar->winx / 2.0f) * vec4[0] / vec4[3];
69                 r_co[1] = (float)(ar->winy / 2.0f) + (ar->winy / 2.0f) * vec4[1] / vec4[3];
70         }
71         else {
72                 zero_v2(r_co);
73         }
74 }
75
76 /**
77  * \note use #ED_view3d_ob_project_mat_get to get projecting mat
78  */
79 void ED_view3d_project_float_v3_m4(const ARegion *ar, const float vec[3], float r_co[3], float mat[4][4])
80 {
81         float vec4[4];
82
83         copy_v3_v3(vec4, vec);
84         vec4[3] = 1.0;
85         /* r_co[0] = IS_CLIPPED; */ /* always overwritten */
86
87         mul_m4_v4(mat, vec4);
88
89         if (vec4[3] > FLT_EPSILON) {
90                 r_co[0] = (float)(ar->winx / 2.0f) + (ar->winx / 2.0f) * vec4[0] / vec4[3];
91                 r_co[1] = (float)(ar->winy / 2.0f) + (ar->winy / 2.0f) * vec4[1] / vec4[3];
92                 r_co[2] = vec4[2] / vec4[3];
93         }
94         else {
95                 zero_v3(r_co);
96         }
97 }
98
99
100 /* Clipping Projection Functions
101  * ***************************** */
102
103 eV3DProjStatus ED_view3d_project_base(const struct ARegion *ar, struct Base *base)
104 {
105         eV3DProjStatus ret = ED_view3d_project_short_global(ar, base->object->obmat[3], &base->sx, V3D_PROJ_TEST_CLIP_DEFAULT);
106
107         if (ret != V3D_PROJ_RET_OK) {
108                 base->sx = IS_CLIPPED;
109                 base->sy = 0;
110         }
111
112         return ret;
113 }
114
115 /* perspmat is typically...
116  * - 'rv3d->perspmat',   is_local == false
117  * - 'rv3d->persmatob', is_local == true
118  */
119 static eV3DProjStatus ed_view3d_project__internal(const ARegion *ar,
120                                                   float perspmat[4][4], const bool is_local,  /* normally hidden */
121                                                   const float co[3], float r_co[2], const eV3DProjTest flag)
122 {
123         float vec4[4];
124
125         /* check for bad flags */
126         BLI_assert((flag & V3D_PROJ_TEST_ALL) == flag);
127
128         if (flag & V3D_PROJ_TEST_CLIP_BB) {
129                 RegionView3D *rv3d = ar->regiondata;
130                 if (rv3d->rflag & RV3D_CLIPPING) {
131                         if (ED_view3d_clipping_test(rv3d, co, is_local)) {
132                                 return V3D_PROJ_RET_CLIP_BB;
133                         }
134                 }
135         }
136
137         copy_v3_v3(vec4, co);
138         vec4[3] = 1.0;
139         mul_m4_v4(perspmat, vec4);
140
141
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)ar->winx / 2.0f) * (1.0f + (vec4[0] * scalar));
147                         if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0.0f && fx < (float)ar->winx)) {
148                                 const float fy = ((float)ar->winy / 2.0f) * (1.0f + (vec4[1] * scalar));
149                                 if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)ar->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
177 eV3DProjStatus ED_view3d_project_short_ex(const ARegion *ar, float perspmat[4][4], const bool is_local,
178                                           const float co[3], short r_co[2], const eV3DProjTest flag)
179 {
180         float tvec[2];
181         eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag);
182         if (ret == V3D_PROJ_RET_OK) {
183                 if ((tvec[0] > -32700.0f && tvec[0] < 32700.0f) &&
184                     (tvec[1] > -32700.0f && tvec[1] < 32700.0f))
185                 {
186                         r_co[0] = (short)floorf(tvec[0]);
187                         r_co[1] = (short)floorf(tvec[1]);
188                 }
189                 else {
190                         ret = V3D_PROJ_RET_OVERFLOW;
191                 }
192         }
193         return ret;
194 }
195
196 eV3DProjStatus ED_view3d_project_int_ex(const ARegion *ar, float perspmat[4][4], const bool is_local,
197                                         const float co[3], int r_co[2], const eV3DProjTest flag)
198 {
199         float tvec[2];
200         eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag);
201         if (ret == V3D_PROJ_RET_OK) {
202                 if ((tvec[0] > -2140000000.0f && tvec[0] < 2140000000.0f) &&
203                     (tvec[1] > -2140000000.0f && tvec[1] < 2140000000.0f))
204                 {
205                         r_co[0] = (int)floorf(tvec[0]);
206                         r_co[1] = (int)floorf(tvec[1]);
207                 }
208                 else {
209                         ret = V3D_PROJ_RET_OVERFLOW;
210                 }
211         }
212         return ret;
213 }
214
215 eV3DProjStatus ED_view3d_project_float_ex(const ARegion *ar, float perspmat[4][4], const bool is_local,
216                                         const float co[3], float r_co[2], const eV3DProjTest flag)
217 {
218         float tvec[2];
219         eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag);
220         if (ret == V3D_PROJ_RET_OK) {
221                 if (isfinite(tvec[0]) &&
222                     isfinite(tvec[1]))
223                 {
224                         copy_v2_v2(r_co, tvec);
225                 }
226                 else {
227                         ret = V3D_PROJ_RET_OVERFLOW;
228                 }
229         }
230         return ret;
231 }
232
233 /* --- short --- */
234 eV3DProjStatus ED_view3d_project_short_global(const ARegion *ar, const float co[3], short r_co[2], const eV3DProjTest flag)
235 {
236         RegionView3D *rv3d = ar->regiondata;
237         return ED_view3d_project_short_ex(ar, rv3d->persmat, false, co, r_co, flag);
238 }
239 /* object space, use ED_view3d_init_mats_rv3d before calling */
240 eV3DProjStatus ED_view3d_project_short_object(const ARegion *ar, const float co[3], short r_co[2], const eV3DProjTest flag)
241 {
242         RegionView3D *rv3d = ar->regiondata;
243         ED_view3d_check_mats_rv3d(rv3d);
244         return ED_view3d_project_short_ex(ar, rv3d->persmatob, true, co, r_co, flag);
245 }
246
247 /* --- int --- */
248 eV3DProjStatus ED_view3d_project_int_global(const ARegion *ar, const float co[3], int r_co[2], const eV3DProjTest flag)
249 {
250         RegionView3D *rv3d = ar->regiondata;
251         return ED_view3d_project_int_ex(ar, rv3d->persmat, false, co, r_co, flag);
252 }
253 /* object space, use ED_view3d_init_mats_rv3d before calling */
254 eV3DProjStatus ED_view3d_project_int_object(const ARegion *ar, const float co[3], int r_co[2], const eV3DProjTest flag)
255 {
256         RegionView3D *rv3d = ar->regiondata;
257         ED_view3d_check_mats_rv3d(rv3d);
258         return ED_view3d_project_int_ex(ar, rv3d->persmatob, true, co, r_co, flag);
259 }
260
261 /* --- float --- */
262 eV3DProjStatus ED_view3d_project_float_global(const ARegion *ar, const float co[3], float r_co[2], const eV3DProjTest flag)
263 {
264         RegionView3D *rv3d = ar->regiondata;
265         return ED_view3d_project_float_ex(ar, rv3d->persmat, false, co, r_co, flag);
266 }
267 /* object space, use ED_view3d_init_mats_rv3d before calling */
268 eV3DProjStatus ED_view3d_project_float_object(const ARegion *ar, const float co[3], float r_co[2], const eV3DProjTest flag)
269 {
270         RegionView3D *rv3d = ar->regiondata;
271         ED_view3d_check_mats_rv3d(rv3d);
272         return ED_view3d_project_float_ex(ar, rv3d->persmatob, true, co, r_co, flag);
273 }
274
275
276
277 /* More Generic Window/Ray/Vector projection functions
278  * *************************************************** */
279
280 float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
281 {
282         return mul_project_m4_v3_zfac((float(*)[4])rv3d->persmat, co) * rv3d->pixsize * U.pixelsize;
283 }
284
285 float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
286 {
287         return mul_project_m4_v3_zfac((float(*)[4])rv3d->persmat, co) * rv3d->pixsize;
288 }
289
290 /**
291  * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta
292  */
293 float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_flip)
294 {
295         float zfac = mul_project_m4_v3_zfac((float (*)[4])rv3d->persmat, co);
296
297         if (r_flip) {
298                 *r_flip = (zfac < 0.0f);
299         }
300
301         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that
302          * (accounting for near zero values) */
303         if (zfac < 1.e-6f && zfac > -1.e-6f) {
304                 zfac = 1.0f;
305         }
306
307         /* Negative zfac means x, y, z was behind the camera (in perspective).
308          * This gives flipped directions, so revert back to ok default case. */
309         if (zfac < 0.0f) {
310                 zfac = -zfac;
311         }
312
313         return zfac;
314 }
315
316 static void view3d_win_to_ray_segment(
317         struct Depsgraph *depsgraph,
318         const ARegion *ar, const View3D *v3d, const float mval[2],
319         float r_ray_co[3], float r_ray_dir[3], float r_ray_start[3], float r_ray_end[3])
320 {
321         RegionView3D *rv3d = ar->regiondata;
322         float _ray_co[3], _ray_dir[3], start_offset, end_offset;
323
324         if (!r_ray_co) r_ray_co = _ray_co;
325         if (!r_ray_dir) r_ray_dir = _ray_dir;
326
327         ED_view3d_win_to_origin(ar, mval, r_ray_co);
328         ED_view3d_win_to_vector(ar, mval, r_ray_dir);
329
330         if ((rv3d->is_persp == false) && (rv3d->persp != RV3D_CAMOB)) {
331                 end_offset = v3d->far / 2.0f;
332                 start_offset = -end_offset;
333         }
334         else {
335                 ED_view3d_clip_range_get(depsgraph, v3d, rv3d, &start_offset, &end_offset, false);
336         }
337
338         if (r_ray_start) {
339                 madd_v3_v3v3fl(r_ray_start, r_ray_co, r_ray_dir, start_offset);
340         }
341         if (r_ray_end) {
342                 madd_v3_v3v3fl(r_ray_end, r_ray_co, r_ray_dir, end_offset);
343         }
344 }
345
346 bool ED_view3d_clip_segment(const RegionView3D *rv3d, float ray_start[3], float ray_end[3])
347 {
348         if ((rv3d->rflag & RV3D_CLIPPING) &&
349             (clip_segment_v3_plane_n(ray_start, ray_end, rv3d->clip, 6,
350                                      ray_start, ray_end) == false))
351         {
352                 return false;
353         }
354         return true;
355 }
356
357 /**
358  * Calculate a 3d viewpoint and direction vector from 2d window coordinates.
359  * This ray_start is located at the viewpoint, ray_normal is the direction towards mval.
360  * ray_start is clipped by the view near limit so points in front of it are always in view.
361  * In orthographic view the resulting ray_normal will match the view vector.
362  * This version also returns the ray_co point of the ray on window plane, useful to fix precision
363  * issues esp. with ortho view, where default ray_start is set rather far away.
364  * \param ar The region (used for the window width and height).
365  * \param v3d The 3d viewport (used for near clipping value).
366  * \param mval The area relative 2d location (such as event->mval, converted into float[2]).
367  * \param r_ray_co The world-space point where the ray intersects the window plane.
368  * \param r_ray_normal The normalized world-space direction of towards mval.
369  * \param r_ray_start The world-space starting point of the ray.
370  * \param do_clip Optionally clip the start of the ray by the view clipping planes.
371  * \return success, false if the ray is totally clipped.
372  */
373 bool ED_view3d_win_to_ray_ex(
374         struct Depsgraph *depsgraph,
375         const ARegion *ar, const View3D *v3d, const float mval[2],
376         float r_ray_co[3], float r_ray_normal[3], float r_ray_start[3], bool do_clip)
377 {
378         float ray_end[3];
379
380         view3d_win_to_ray_segment(depsgraph, ar, v3d, mval, r_ray_co, r_ray_normal, r_ray_start, ray_end);
381
382         /* bounds clipping */
383         if (do_clip) {
384                 return ED_view3d_clip_segment(ar->regiondata, r_ray_start, ray_end);
385         }
386
387         return true;
388 }
389
390 /**
391  * Calculate a 3d viewpoint and direction vector from 2d window coordinates.
392  * This ray_start is located at the viewpoint, ray_normal is the direction towards mval.
393  * ray_start is clipped by the view near limit so points in front of it are always in view.
394  * In orthographic view the resulting ray_normal will match the view vector.
395  * \param ar The region (used for the window width and height).
396  * \param v3d The 3d viewport (used for near clipping value).
397  * \param mval The area relative 2d location (such as event->mval, converted into float[2]).
398  * \param r_ray_start The world-space point where the ray intersects the window plane.
399  * \param r_ray_normal The normalized world-space direction of towards mval.
400  * \param do_clip Optionally clip the start of the ray by the view clipping planes.
401  * \return success, false if the ray is totally clipped.
402  */
403 bool ED_view3d_win_to_ray(
404         struct Depsgraph *depsgraph,
405         const ARegion *ar, const View3D *v3d, const float mval[2],
406         float r_ray_start[3], float r_ray_normal[3], const bool do_clip)
407 {
408         return ED_view3d_win_to_ray_ex(depsgraph, ar, v3d, mval, NULL, r_ray_normal, r_ray_start, do_clip);
409 }
410
411 /**
412  * Calculate a normalized 3d direction vector from the viewpoint towards a global location.
413  * In orthographic view the resulting vector will match the view vector.
414  * \param rv3d The region (used for the window width and height).
415  * \param coord The world-space location.
416  * \param vec The resulting normalized vector.
417  */
418 void ED_view3d_global_to_vector(const RegionView3D *rv3d, const float coord[3], float vec[3])
419 {
420         if (rv3d->is_persp) {
421                 float p1[4], p2[4];
422
423                 copy_v3_v3(p1, coord);
424                 p1[3] = 1.0f;
425                 copy_v3_v3(p2, p1);
426                 p2[3] = 1.0f;
427                 mul_m4_v4((float (*)[4])rv3d->viewmat, p2);
428
429                 mul_v3_fl(p2, 2.0f);
430
431                 mul_m4_v4((float (*)[4])rv3d->viewinv, p2);
432
433                 sub_v3_v3v3(vec, p1, p2);
434         }
435         else {
436                 copy_v3_v3(vec, rv3d->viewinv[2]);
437         }
438         normalize_v3(vec);
439 }
440
441 /* very similar to ED_view3d_win_to_3d() but has no advantage, de-duplicating */
442 #if 0
443 bool view3d_get_view_aligned_coordinate(ARegion *ar, float fp[3], const int mval[2], const bool do_fallback)
444 {
445         RegionView3D *rv3d = ar->regiondata;
446         float dvec[3];
447         int mval_cpy[2];
448         eV3DProjStatus ret;
449
450         ret = ED_view3d_project_int_global(ar, fp, mval_cpy, V3D_PROJ_TEST_NOP);
451
452         if (ret == V3D_PROJ_RET_OK) {
453                 const float mval_f[2] = {(float)(mval_cpy[0] - mval[0]),
454                                          (float)(mval_cpy[1] - mval[1])};
455                 const float zfac = ED_view3d_calc_zfac(rv3d, fp, NULL);
456                 ED_view3d_win_to_delta(ar, mval_f, dvec, zfac);
457                 sub_v3_v3(fp, dvec);
458
459                 return true;
460         }
461         else {
462                 /* fallback to the view center */
463                 if (do_fallback) {
464                         negate_v3_v3(fp, rv3d->ofs);
465                         return view3d_get_view_aligned_coordinate(ar, fp, mval, false);
466                 }
467                 else {
468                         return false;
469                 }
470         }
471 }
472 #endif
473
474 /**
475  * Calculate a 3d location from 2d window coordinates.
476  * \param ar The region (used for the window width and height).
477  * \param depth_pt The reference location used to calculate the Z depth.
478  * \param mval The area relative location (such as event->mval converted to floats).
479  * \param r_out The resulting world-space location.
480  */
481 void ED_view3d_win_to_3d(
482         const View3D *v3d, const ARegion *ar,
483         const float depth_pt[3], const float mval[2],
484         float r_out[3])
485 {
486         RegionView3D *rv3d = ar->regiondata;
487
488         float ray_origin[3];
489         float ray_direction[3];
490         float lambda;
491
492         if (rv3d->is_persp) {
493                 float plane[4];
494
495                 copy_v3_v3(ray_origin, rv3d->viewinv[3]);
496                 ED_view3d_win_to_vector(ar, mval, ray_direction);
497
498                 /* note, we could use isect_line_plane_v3() however we want the intersection to be infront of the
499                  * view no matter what, so apply the unsigned factor instead */
500                 plane_from_point_normal_v3(plane, depth_pt, rv3d->viewinv[2]);
501
502                 isect_ray_plane_v3(ray_origin, ray_direction, plane, &lambda, false);
503                 lambda = fabsf(lambda);
504         }
505         else {
506                 float dx = (2.0f * mval[0] / (float)ar->winx) - 1.0f;
507                 float dy = (2.0f * mval[1] / (float)ar->winy) - 1.0f;
508
509                 if (rv3d->persp == RV3D_CAMOB) {
510                         /* ortho camera needs offset applied */
511                         const Camera *cam = v3d->camera->data;
512                         const int sensor_fit = BKE_camera_sensor_fit(cam->sensor_fit, ar->winx, ar->winy);
513                         const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 4.0f;
514                         const float aspx = ar->winx / (float)ar->winy;
515                         const float aspy = ar->winy / (float)ar->winx;
516                         const float shiftx = cam->shiftx * 0.5f * (sensor_fit == CAMERA_SENSOR_FIT_HOR ? 1.0f : aspy);
517                         const float shifty = cam->shifty * 0.5f * (sensor_fit == CAMERA_SENSOR_FIT_HOR ? aspx : 1.0f);
518
519                         dx += (rv3d->camdx + shiftx) * zoomfac;
520                         dy += (rv3d->camdy + shifty) * zoomfac;
521                 }
522                 ray_origin[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
523                 ray_origin[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
524                 ray_origin[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
525
526                 copy_v3_v3(ray_direction, rv3d->viewinv[2]);
527                 lambda = ray_point_factor_v3(depth_pt, ray_origin, ray_direction);
528         }
529
530         madd_v3_v3v3fl(r_out, ray_origin, ray_direction, lambda);
531 }
532
533 void ED_view3d_win_to_3d_int(
534         const View3D *v3d, const ARegion *ar,
535         const float depth_pt[3], const int mval[2],
536         float r_out[3])
537 {
538         const float mval_fl[2] = {mval[0], mval[1]};
539         ED_view3d_win_to_3d(v3d, ar, depth_pt, mval_fl, r_out);
540 }
541
542 /**
543  * Calculate a 3d difference vector from 2d window offset.
544  * note that ED_view3d_calc_zfac() must be called first to determine
545  * the depth used to calculate the delta.
546  * \param ar The region (used for the window width and height).
547  * \param mval The area relative 2d difference (such as event->mval[0] - other_x).
548  * \param out The resulting world-space delta.
549  */
550 void ED_view3d_win_to_delta(const ARegion *ar, const float mval[2], float out[3], const float zfac)
551 {
552         RegionView3D *rv3d = ar->regiondata;
553         float dx, dy;
554
555         dx = 2.0f * mval[0] * zfac / ar->winx;
556         dy = 2.0f * mval[1] * zfac / ar->winy;
557
558         out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy);
559         out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy);
560         out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy);
561 }
562
563 /**
564  * Calculate a 3d origin from 2d window coordinates.
565  * \note Orthographic views have a less obvious origin,
566  * Since far clip can be a very large value resulting in numeric precision issues,
567  * the origin in this case is close to zero coordinate.
568  *
569  * \param ar The region (used for the window width and height).
570  * \param mval The area relative 2d location (such as event->mval converted to floats).
571  * \param out The resulting normalized world-space direction vector.
572  */
573 void ED_view3d_win_to_origin(const ARegion *ar, const float mval[2], float out[3])
574 {
575         RegionView3D *rv3d = ar->regiondata;
576         if (rv3d->is_persp) {
577                 copy_v3_v3(out, rv3d->viewinv[3]);
578         }
579         else {
580                 out[0] = 2.0f * mval[0] / ar->winx - 1.0f;
581                 out[1] = 2.0f * mval[1] / ar->winy - 1.0f;
582
583                 if (rv3d->persp == RV3D_CAMOB) {
584                         out[2] = -1.0f;
585                 }
586                 else {
587                         out[2] = 0.0f;
588                 }
589
590                 mul_project_m4_v3(rv3d->persinv, out);
591         }
592 }
593
594 /**
595  * Calculate a 3d direction vector from 2d window coordinates.
596  * This direction vector starts and the view in the direction of the 2d window coordinates.
597  * In orthographic view all window coordinates yield the same vector.
598  *
599  * \note doesn't rely on ED_view3d_calc_zfac
600  * for perspective view, get the vector direction to
601  * the mouse cursor as a normalized vector.
602  *
603  * \param ar The region (used for the window width and height).
604  * \param mval The area relative 2d location (such as event->mval converted to floats).
605  * \param out The resulting normalized world-space direction vector.
606  */
607 void ED_view3d_win_to_vector(const ARegion *ar, const float mval[2], float out[3])
608 {
609         RegionView3D *rv3d = ar->regiondata;
610
611         if (rv3d->is_persp) {
612                 out[0] = 2.0f * (mval[0] / ar->winx) - 1.0f;
613                 out[1] = 2.0f * (mval[1] / ar->winy) - 1.0f;
614                 out[2] = -0.5f;
615                 mul_project_m4_v3(rv3d->persinv, out);
616                 sub_v3_v3(out, rv3d->viewinv[3]);
617         }
618         else {
619                 negate_v3_v3(out, rv3d->viewinv[2]);
620         }
621         normalize_v3(out);
622 }
623
624 /**
625  * Calculate a 3d segment from 2d window coordinates.
626  * This ray_start is located at the viewpoint, ray_end is a far point.
627  * ray_start and ray_end are clipped by the view near and far limits
628  * so points along this line are always in view.
629  * In orthographic view all resulting segments will be parallel.
630  * \param ar The region (used for the window width and height).
631  * \param v3d The 3d viewport (used for near and far clipping range).
632  * \param mval The area relative 2d location (such as event->mval, converted into float[2]).
633  * \param r_ray_start The world-space starting point of the segment.
634  * \param r_ray_end The world-space end point of the segment.
635  * \param do_clip Optionally clip the ray by the view clipping planes.
636  * \return success, false if the segment is totally clipped.
637  */
638 bool ED_view3d_win_to_segment(struct Depsgraph *depsgraph,
639                               const ARegion *ar, View3D *v3d, const float mval[2],
640                               float r_ray_start[3], float r_ray_end[3], const bool do_clip)
641 {
642         view3d_win_to_ray_segment(depsgraph, ar, v3d, mval, NULL, NULL, r_ray_start, r_ray_end);
643
644         /* bounds clipping */
645         if (do_clip) {
646                 return ED_view3d_clip_segment((RegionView3D *)ar->regiondata, r_ray_start, r_ray_end);
647         }
648
649         return true;
650 }
651
652 /* Utility functions for projection
653  * ******************************** */
654
655 void ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, Object *ob, float pmat[4][4])
656 {
657         float vmat[4][4];
658
659         mul_m4_m4m4(vmat, (float (*)[4])rv3d->viewmat, ob->obmat);
660         mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat);
661 }
662
663 void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, float obmat[4][4], float pmat[4][4])
664 {
665         float vmat[4][4];
666
667         mul_m4_m4m4(vmat, (float (*)[4])rv3d->viewmat, obmat);
668         mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat);
669 }
670
671 /**
672  * Convert between region relative coordinates (x,y) and depth component z and
673  * a point in world space. */
674 void ED_view3d_project(const struct ARegion *ar, const float world[3], float region[3])
675 {
676         // viewport is set up to make coordinates relative to the region, not window
677         RegionView3D *rv3d = ar->regiondata;
678         int viewport[4] = {0, 0, ar->winx, ar->winy};
679
680         gpuProject(world, rv3d->viewmat, rv3d->winmat, viewport, region);
681 }
682
683 bool ED_view3d_unproject(const struct ARegion *ar, float regionx, float regiony, float regionz, float world[3])
684 {
685         RegionView3D *rv3d = ar->regiondata;
686         int viewport[4] = {0, 0, ar->winx, ar->winy};
687         float region[3] = {regionx, regiony, regionz};
688
689         return gpuUnProject(region, rv3d->viewmat, rv3d->winmat, viewport, world);
690 }