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