code cleanup:
[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_object_types.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_view3d_types.h"
35
36 #include "BLO_sys_types.h"  /* int64_t */
37
38 #include "BIF_gl.h"  /* bglMats */
39 #include "BIF_glutil.h"  /* bglMats */
40
41 #include "BLI_math_vector.h"
42
43 #include "ED_view3d.h"  /* own include */
44
45 #define BL_NEAR_CLIP 0.001
46
47 /* Non Clipping Projection Functions
48  * ********************************* */
49
50 /**
51  * \note use #ED_view3d_ob_project_mat_get to get the projection matrix
52  */
53 void ED_view3d_project_float_v2_m4(const ARegion *ar, const float co[3], float r_co[2], 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)(ar->winx / 2.0f) + (ar->winx / 2.0f) * vec4[0] / vec4[3];
65                 r_co[1] = (float)(ar->winy / 2.0f) + (ar->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  */
75 void ED_view3d_project_float_v3_m4(ARegion *ar, const float vec[3], float r_co[3], float mat[4][4])
76 {
77         float vec4[4];
78         
79         copy_v3_v3(vec4, vec);
80         vec4[3] = 1.0;
81         /* r_co[0] = IS_CLIPPED; */ /* always overwritten */
82         
83         mul_m4_v4(mat, vec4);
84         
85         if (vec4[3] > FLT_EPSILON) {
86                 r_co[0] = (float)(ar->winx / 2.0f) + (ar->winx / 2.0f) * vec4[0] / vec4[3];
87                 r_co[1] = (float)(ar->winy / 2.0f) + (ar->winy / 2.0f) * vec4[1] / vec4[3];
88                 r_co[2] = vec4[2] / vec4[3];
89         }
90         else {
91                 zero_v3(r_co);
92         }
93 }
94
95
96 /* Clipping Projection Functions
97  * ***************************** */
98
99 eV3DProjStatus ED_view3d_project_base(struct ARegion *ar, struct Base *base)
100 {
101         eV3DProjStatus ret = ED_view3d_project_short_global(ar, base->object->obmat[3], &base->sx, V3D_PROJ_TEST_CLIP_DEFAULT);
102
103         if (ret != V3D_PROJ_RET_OK) {
104                 base->sx = IS_CLIPPED;
105                 base->sy = 0;
106         }
107
108         return ret;
109 }
110
111 /* perspmat is typically...
112  * - 'rv3d->perspmat',   is_local == FALSE
113  * - 'rv3d->perspmatob', is_local == TRUE
114  */
115 static eV3DProjStatus ed_view3d_project__internal(ARegion *ar,
116                                                   float perspmat[4][4], const int is_local,  /* normally hidden */
117                                                   const float co[3], float r_co[2], const eV3DProjTest flag)
118 {
119         float fx, fy, vec4[4];
120
121         /* check for bad flags */
122         BLI_assert((flag & V3D_PROJ_TEST_ALL) == flag);
123
124         if (flag & V3D_PROJ_TEST_CLIP_BB) {
125                 RegionView3D *rv3d = ar->regiondata;
126                 if (rv3d->rflag & RV3D_CLIPPING) {
127                         if (ED_view3d_clipping_test(rv3d, co, is_local)) {
128                                 return V3D_PROJ_RET_CLIP_BB;
129                         }
130                 }
131         }
132
133         copy_v3_v3(vec4, co);
134         vec4[3] = 1.0;
135         mul_m4_v4(perspmat, vec4);
136
137         if (vec4[3] > (float)BL_NEAR_CLIP) {
138                 fx = ((float)ar->winx / 2.0f) * (1.0f + vec4[0] / vec4[3]);
139                 if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0 && fx < ar->winx)) {
140                         fy = ((float)ar->winy / 2.0f) * (1.0f + vec4[1] / vec4[3]);
141                         if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)ar->winy)) {
142                                 r_co[0] = (short)floor(fx);
143                                 r_co[1] = (short)floor(fy);
144                         }
145                         else {
146                                 return V3D_PROJ_RET_CLIP_WIN;
147                         }
148                 }
149                 else {
150                         return V3D_PROJ_RET_CLIP_WIN;
151                 }
152         }
153         else {
154                 return V3D_PROJ_RET_CLIP_NEAR;
155         }
156
157         return V3D_PROJ_RET_OK;
158 }
159
160 eV3DProjStatus ED_view3d_project_short_ex(ARegion *ar, float perspmat[4][4], const int is_local,
161                                           const float co[3], short r_co[2], const eV3DProjTest flag)
162 {
163         float tvec[2];
164         eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag);
165         if (ret == V3D_PROJ_RET_OK) {
166                 if ((tvec[0] > -32700.0 && tvec[0] < 32700.0f) &&
167                     (tvec[1] > -32700.0 && tvec[1] < 32700.0f))
168                 {
169                         r_co[0] = (short)floor(tvec[0]);
170                         r_co[1] = (short)floor(tvec[1]);
171                 }
172                 else {
173                         ret = V3D_PROJ_RET_OVERFLOW;
174                 }
175         }
176         return ret;
177 }
178
179 eV3DProjStatus ED_view3d_project_int_ex(ARegion *ar, float perspmat[4][4], const int is_local,
180                                         const float co[3], int r_co[2], const eV3DProjTest flag)
181 {
182         float tvec[2];
183         eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag);
184         if (ret == V3D_PROJ_RET_OK) {
185                 if ((tvec[0] > -2140000000.0 && tvec[0] < 2140000000.0f) &&
186                     (tvec[1] > -2140000000.0 && tvec[1] < 2140000000.0f))
187                 {
188                         r_co[0] = (int)floor(tvec[0]);
189                         r_co[1] = (int)floor(tvec[1]);
190                 }
191                 else {
192                         ret = V3D_PROJ_RET_OVERFLOW;
193                 }
194         }
195         return ret;
196 }
197
198 eV3DProjStatus ED_view3d_project_float_ex(ARegion *ar, float perspmat[4][4], const int is_local,
199                                         const float co[3], float r_co[2], const eV3DProjTest flag)
200 {
201         float tvec[2];
202         eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag);
203         if (ret == V3D_PROJ_RET_OK) {
204                 if (finite(tvec[0]) &&
205                     finite(tvec[1]))
206                 {
207                         copy_v2_v2(r_co, tvec);
208                 }
209                 else {
210                         ret = V3D_PROJ_RET_OVERFLOW;
211                 }
212         }
213         return ret;
214 }
215
216 /* --- short --- */
217 eV3DProjStatus ED_view3d_project_short_global(ARegion *ar, const float co[3], short r_co[2], const eV3DProjTest flag)
218 {
219         RegionView3D *rv3d = ar->regiondata;
220         return ED_view3d_project_short_ex(ar, rv3d->persmat, FALSE, co, r_co, flag);
221 }
222 /* object space, use ED_view3d_init_mats_rv3d before calling */
223 eV3DProjStatus ED_view3d_project_short_object(ARegion *ar, const float co[3], short r_co[2], const eV3DProjTest flag)
224 {
225         RegionView3D *rv3d = ar->regiondata;
226         return ED_view3d_project_short_ex(ar, rv3d->persmatob, TRUE, co, r_co, flag);
227 }
228
229 /* --- int --- */
230 eV3DProjStatus ED_view3d_project_int_global(ARegion *ar, const float co[3], int r_co[2], const eV3DProjTest flag)
231 {
232         RegionView3D *rv3d = ar->regiondata;
233         return ED_view3d_project_int_ex(ar, rv3d->persmat, FALSE, co, r_co, flag);
234 }
235 /* object space, use ED_view3d_init_mats_rv3d before calling */
236 eV3DProjStatus ED_view3d_project_int_object(ARegion *ar, const float co[3], int r_co[2], const eV3DProjTest flag)
237 {
238         RegionView3D *rv3d = ar->regiondata;
239         return ED_view3d_project_int_ex(ar, rv3d->persmatob, TRUE, co, r_co, flag);
240 }
241
242 /* --- float --- */
243 eV3DProjStatus ED_view3d_project_float_global(ARegion *ar, const float co[3], float r_co[2], const eV3DProjTest flag)
244 {
245         RegionView3D *rv3d = ar->regiondata;
246         return ED_view3d_project_float_ex(ar, rv3d->persmat, FALSE, co, r_co, flag);
247 }
248 /* object space, use ED_view3d_init_mats_rv3d before calling */
249 eV3DProjStatus ED_view3d_project_float_object(ARegion *ar, const float co[3], float r_co[2], const eV3DProjTest flag)
250 {
251         RegionView3D *rv3d = ar->regiondata;
252         return ED_view3d_project_float_ex(ar, rv3d->persmatob, TRUE, co, r_co, flag);
253 }
254
255
256
257 /* More Generic Window/Ray/Vector projection functions
258  * *************************************************** */
259
260 /* odd function, need to document better */
261 int initgrabz(RegionView3D *rv3d, float x, float y, float z)
262 {
263         int flip = FALSE;
264         if (rv3d == NULL) return flip;
265         rv3d->zfac = rv3d->persmat[0][3] * x + rv3d->persmat[1][3] * y + rv3d->persmat[2][3] * z + rv3d->persmat[3][3];
266         if (rv3d->zfac < 0.0f)
267                 flip = TRUE;
268         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that
269          * (accounting for near zero values)
270          */
271         if (rv3d->zfac < 1.e-6f && rv3d->zfac > -1.e-6f) rv3d->zfac = 1.0f;
272
273         /* Negative zfac means x, y, z was behind the camera (in perspective).
274          * This gives flipped directions, so revert back to ok default case.
275          */
276         /* NOTE: I've changed this to flip zfac to be positive again for now so that GPencil draws ok
277          * Aligorith, 2009Aug31 */
278         //if (rv3d->zfac < 0.0f) rv3d->zfac = 1.0f;
279         if (rv3d->zfac < 0.0f) rv3d->zfac = -rv3d->zfac;
280
281         return flip;
282 }
283
284 /**
285  * Calculate a 3d viewpoint and direction vector from 2d window coordinates.
286  * This ray_start is located at the viewpoint, ray_normal is the direction towards mval.
287  * ray_start is clipped by the view near limit so points in front of it are always in view.
288  * In orthographic view the resulting ray_normal will match the view vector.
289  * \param ar The region (used for the window width and height).
290  * \param v3d The 3d viewport (used for near clipping value).
291  * \param mval The area relative 2d location (such as event->mval, converted into float[2]).
292  * \param ray_start The world-space starting point of the segment.
293  * \param ray_normal The normalized world-space direction of towards mval.
294  */
295 void ED_view3d_win_to_ray(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_normal[3])
296 {
297         float ray_end[3];
298         
299         ED_view3d_win_to_segment_clip(ar, v3d, mval, ray_start, ray_end);
300         sub_v3_v3v3(ray_normal, ray_end, ray_start);
301         normalize_v3(ray_normal);
302 }
303
304 /**
305  * Calculate a normalized 3d direction vector from the viewpoint towards a global location.
306  * In orthographic view the resulting vector will match the view vector.
307  * \param rv3d The region (used for the window width and height).
308  * \param coord The world-space location.
309  * \param vec The resulting normalized vector.
310  */
311 void ED_view3d_global_to_vector(RegionView3D *rv3d, const float coord[3], float vec[3])
312 {
313         if (rv3d->is_persp) {
314                 float p1[4], p2[4];
315
316                 copy_v3_v3(p1, coord);
317                 p1[3] = 1.0f;
318                 copy_v3_v3(p2, p1);
319                 p2[3] = 1.0f;
320                 mul_m4_v4(rv3d->viewmat, p2);
321
322                 mul_v3_fl(p2, 2.0f);
323
324                 mul_m4_v4(rv3d->viewinv, p2);
325
326                 sub_v3_v3v3(vec, p1, p2);
327         }
328         else {
329                 copy_v3_v3(vec, rv3d->viewinv[2]);
330         }
331         normalize_v3(vec);
332 }
333
334 /**
335  * Calculate a 3d location from 2d window coordinates.
336  * \param ar The region (used for the window width and height).
337  * \param depth_pt The reference location used to calculate the Z depth.
338  * \param mval The area relative location (such as event->mval converted to floats).
339  * \param out The resulting world-space location.
340  */
341 void ED_view3d_win_to_3d(ARegion *ar, const float depth_pt[3], const float mval[2], float out[3])
342 {
343         RegionView3D *rv3d = ar->regiondata;
344         
345         float line_sta[3];
346         float line_end[3];
347
348         if (rv3d->is_persp) {
349                 float mousevec[3];
350                 copy_v3_v3(line_sta, rv3d->viewinv[3]);
351                 ED_view3d_win_to_vector(ar, mval, mousevec);
352                 add_v3_v3v3(line_end, line_sta, mousevec);
353
354                 if (isect_line_plane_v3(out, line_sta, line_end, depth_pt, rv3d->viewinv[2], TRUE) == 0) {
355                         /* highly unlikely to ever happen, mouse vec paralelle with view plane */
356                         zero_v3(out);
357                 }
358         }
359         else {
360                 const float dx = (2.0f * mval[0] / (float)ar->winx) - 1.0f;
361                 const float dy = (2.0f * mval[1] / (float)ar->winy) - 1.0f;
362                 line_sta[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0];
363                 line_sta[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1];
364                 line_sta[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2];
365
366                 add_v3_v3v3(line_end, line_sta, rv3d->viewinv[2]);
367                 closest_to_line_v3(out, depth_pt, line_sta, line_end);
368         }
369 }
370
371 /**
372  * Calculate a 3d difference vector from 2d window offset.
373  * note that initgrabz() must be called first to determine
374  * the depth used to calculate the delta.
375  * \param ar The region (used for the window width and height).
376  * \param mval The area relative 2d difference (such as event->mval[0] - other_x).
377  * \param out The resulting world-space delta.
378  */
379 void ED_view3d_win_to_delta(ARegion *ar, const float mval[2], float out[3])
380 {
381         RegionView3D *rv3d = ar->regiondata;
382         float dx, dy;
383         
384         dx = 2.0f * mval[0] * rv3d->zfac / ar->winx;
385         dy = 2.0f * mval[1] * rv3d->zfac / ar->winy;
386         
387         out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy);
388         out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy);
389         out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy);
390 }
391
392 /**
393  * Calculate a 3d direction vector from 2d window coordinates.
394  * This direction vector starts and the view in the direction of the 2d window coordinates.
395  * In orthographic view all window coordinates yield the same vector.
396  *
397  * \note doesn't rely on initgrabz
398  * for perspective view, get the vector direction to
399  * the mouse cursor as a normalized vector.
400  *
401  * \param ar The region (used for the window width and height).
402  * \param mval The area relative 2d location (such as event->mval converted to floats).
403  * \param out The resulting normalized world-space direction vector.
404  */
405 void ED_view3d_win_to_vector(ARegion *ar, const float mval[2], float out[3])
406 {
407         RegionView3D *rv3d = ar->regiondata;
408
409         if (rv3d->is_persp) {
410                 out[0] = 2.0f * (mval[0] / ar->winx) - 1.0f;
411                 out[1] = 2.0f * (mval[1] / ar->winy) - 1.0f;
412                 out[2] = -0.5f;
413                 mul_project_m4_v3(rv3d->persinv, out);
414                 sub_v3_v3(out, rv3d->viewinv[3]);
415         }
416         else {
417                 copy_v3_v3(out, rv3d->viewinv[2]);
418         }
419         normalize_v3(out);
420 }
421
422 /**
423  * Calculate a 3d segment from 2d window coordinates.
424  * This ray_start is located at the viewpoint, ray_end is a far point.
425  * ray_start and ray_end are clipped by the view near and far limits
426  * so points along this line are always in view.
427  * In orthographic view all resulting segments will be parallel.
428  * \param ar The region (used for the window width and height).
429  * \param v3d The 3d viewport (used for near and far clipping range).
430  * \param mval The area relative 2d location (such as event->mval, converted into float[2]).
431  * \param ray_start The world-space starting point of the segment.
432  * \param ray_end The world-space end point of the segment.
433  */
434 void ED_view3d_win_to_segment_clip(ARegion *ar, View3D *v3d, const float mval[2], float ray_start[3], float ray_end[3])
435 {
436         RegionView3D *rv3d = ar->regiondata;
437
438         if (rv3d->is_persp) {
439                 float vec[3];
440                 ED_view3d_win_to_vector(ar, mval, vec);
441
442                 copy_v3_v3(ray_start, rv3d->viewinv[3]);
443                 madd_v3_v3v3fl(ray_start, rv3d->viewinv[3], vec, v3d->near);
444                 madd_v3_v3v3fl(ray_end, rv3d->viewinv[3], vec, v3d->far);
445         }
446         else {
447                 float vec[4];
448                 vec[0] = 2.0f * mval[0] / ar->winx - 1;
449                 vec[1] = 2.0f * mval[1] / ar->winy - 1;
450                 vec[2] = 0.0f;
451                 vec[3] = 1.0f;
452
453                 mul_m4_v4(rv3d->persinv, vec);
454
455                 madd_v3_v3v3fl(ray_start, vec, rv3d->viewinv[2],  1000.0f);
456                 madd_v3_v3v3fl(ray_end, vec, rv3d->viewinv[2], -1000.0f);
457         }
458
459         /* clipping */
460         if (rv3d->rflag & RV3D_CLIPPING) {
461                 int a;
462                 for (a = 0; a < 4; a++) {
463                         clip_line_plane(ray_start, ray_end, rv3d->clip[a]);
464                 }
465         }
466 }
467
468
469 /* Utility functions for projection
470  * ******************************** */
471
472 void ED_view3d_ob_project_mat_get(RegionView3D *rv3d, Object *ob, float pmat[4][4])
473 {
474         float vmat[4][4];
475
476         mult_m4_m4m4(vmat, rv3d->viewmat, ob->obmat);
477         mult_m4_m4m4(pmat, rv3d->winmat, vmat);
478 }
479
480 /**
481  * Uses window coordinates (x,y) and depth component z to find a point in
482  * modelspace */
483 void ED_view3d_unproject(bglMats *mats, float out[3], const float x, const float y, const float z)
484 {
485         double ux, uy, uz;
486
487         gluUnProject(x, y, z, mats->modelview, mats->projection,
488                      (GLint *)mats->viewport, &ux, &uy, &uz);
489
490         out[0] = ux;
491         out[1] = uy;
492         out[2] = uz;
493 }