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