[Bf-blender-cvs] [1501c3adad1] master: Add Object Tool: support placing objects in orthographic axis views

Campbell Barton noreply at git.blender.org
Fri Nov 20 07:20:33 CET 2020


Commit: 1501c3adad13c3b0ae4f02a84f1be2c84f6f1db2
Author: Campbell Barton
Date:   Fri Nov 20 17:07:04 2020 +1100
Branches: master
https://developer.blender.org/rB1501c3adad13c3b0ae4f02a84f1be2c84f6f1db2

Add Object Tool: support placing objects in orthographic axis views

When an projecting onto a plane that is orthogonal to the views Z axis,
project onto a view aligned axis then map it back to the original plane.
see: ED_view3d_win_to_3d_on_plane_with_fallback

Since the depth can't be properly visualized in 3D, display a 2D so
it's possible to to tell the depth from the cursor motion.

===================================================================

M	source/blender/editors/include/ED_view3d.h
M	source/blender/editors/space_view3d/view3d_placement.c
M	source/blender/editors/space_view3d/view3d_project.c

===================================================================

diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index e8aa312f444..596533406c3 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -364,6 +364,12 @@ bool ED_view3d_win_to_3d_on_plane(const struct ARegion *region,
                                   const float mval[2],
                                   const bool do_clip,
                                   float r_out[3]);
+bool ED_view3d_win_to_3d_on_plane_with_fallback(const struct ARegion *region,
+                                                const float plane[4],
+                                                const float mval[2],
+                                                const bool do_clip,
+                                                const float plane_fallback[4],
+                                                float r_out[3]);
 bool ED_view3d_win_to_3d_on_plane_int(const struct ARegion *region,
                                       const float plane[4],
                                       const int mval[2],
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index bdf795a33a1..b8512e42dcf 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -23,7 +23,6 @@
  * including library assets & non-mesh types.
  */
 
-#include "BLI_math_vector.h"
 #include "MEM_guardedalloc.h"
 
 #include "DNA_collection_types.h"
@@ -68,6 +67,13 @@ static const char *view3d_gzgt_placement_id = "VIEW3D_GGT_placement";
 static void preview_plane_cursor_setup(wmGizmoGroup *gzgroup);
 static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, bool do_draw);
 
+/**
+ * Dot products below this will be considered view aligned.
+ * In this case we can't usefully project the mouse cursor onto the plane,
+ * so use a fall-back plane instead.
+ */
+const float eps_view_align = 1e-2f;
+
 /* -------------------------------------------------------------------- */
 /** \name Local Types
  * \{ */
@@ -114,8 +120,42 @@ struct InteractivePlaceData {
     bool is_fixed_aspect;
     float plane[4];
     float co_dst[3];
+
+    /**
+     * We can't project the mouse cursor onto `plane`,
+     * in this case #view3d_win_to_3d_on_plane_maybe_fallback is used.
+     *
+     * - For #STEP_BASE we're drawing from the side, where the X/Y axis can't be projected.
+     * - For #STEP_DEPTH we're drawing from the top (2D), where the depth can't be projected.
+     */
+    bool is_degenerate_view_align;
+    /**
+     * When view aligned, use a diagonal offset (cavalier projection)
+     * to give user feedback about the depth being set.
+     *
+     * Currently this is only used for orthogonal views since perspective views
+     * nearly always show some depth, even when view aligned.
+     *
+     * - Drag to the bottom-left to move away from the view.
+     * - Drag to the top-right to move towards the view.
+     */
+    float degenerate_diagonal[3];
+    /**
+     * Corrected for display, so what's shown on-screen doesn't loop to be reversed
+     * in relation to cursor-motion.
+     */
+    float degenerate_diagonal_display[3];
+
+    /**
+     * Index into `matrix_orient` which is degenerate.
+     */
+    int degenerate_axis;
+
   } step[2];
 
+  /** When we can't project onto the real plane, use this in it's place. */
+  float view_plane[4];
+
   float matrix_orient[3][3];
   int orient_axis;
 
@@ -152,6 +192,47 @@ struct InteractivePlaceData {
 /** \name Internal Utilities
  * \{ */
 
+/**
+ * Convenience wrapper to avoid duplicating arguments.
+ */
+static bool view3d_win_to_3d_on_plane_maybe_fallback(const ARegion *region,
+                                                     const float plane[4],
+                                                     const float mval[2],
+                                                     const float *plane_fallback,
+                                                     float r_out[3])
+{
+  RegionView3D *rv3d = region->regiondata;
+  bool do_clip = rv3d->is_persp;
+  if (plane_fallback != NULL) {
+    return ED_view3d_win_to_3d_on_plane_with_fallback(
+        region, plane, mval, do_clip, plane_fallback, r_out);
+  }
+  return ED_view3d_win_to_3d_on_plane(region, plane, mval, do_clip, r_out);
+}
+
+/**
+ * Return the index of \a dirs with the largest dot product compared to \a dir_test.
+ */
+static int dot_v3_array_find_max_index(const float dirs[][3],
+                                       const int dirs_len,
+                                       const float dir_test[3],
+                                       bool is_signed)
+{
+  int index_found = -1;
+  float dot_best = -1.0f;
+  for (int i = 0; i < dirs_len; i++) {
+    float dot_test = dot_v3v3(dirs[i], dir_test);
+    if (is_signed == false) {
+      dot_test = fabsf(dot_test);
+    }
+    if ((index_found == -1) || (dot_test > dot_best)) {
+      dot_best = dot_test;
+      index_found = i;
+    }
+  }
+  return index_found;
+}
+
 /**
  * Re-order \a mat so \a axis_align uses it's own axis which is closest to \a v.
  */
@@ -556,12 +637,50 @@ static void draw_circle_in_quad(const float v1[2],
 
 static void draw_primitive_view_impl(const struct bContext *C,
                                      struct InteractivePlaceData *ipd,
-                                     const float color[4])
+                                     const float color[4],
+                                     int flatten_axis)
 {
   UNUSED_VARS(C);
 
   BoundBox bounds;
   calc_bbox(ipd, &bounds);
+
+  /* Use cavalier projection, since it maps the scale usefully to the cursor. */
+  if (flatten_axis == STEP_BASE) {
+    /* Calculate the plane that would be defined by the side of the cube vertices
+     * if the plane had any volume. */
+
+    float no[3];
+
+    cross_v3_v3v3(
+        no, ipd->matrix_orient[ipd->orient_axis], ipd->matrix_orient[(ipd->orient_axis + 1) % 3]);
+
+    RegionView3D *rv3d = ipd->region->regiondata;
+    copy_v3_v3(no, rv3d->viewinv[2]);
+    normalize_v3(no);
+
+    float base_plane[4];
+
+    plane_from_point_normal_v3(base_plane, bounds.vec[0], no);
+
+    /* Offset all vertices even though we only need to offset the half of them.
+     * This is harmless as `dist` will be zero for the `base_plane` aligned side of the cube. */
+    for (int i = 0; i < ARRAY_SIZE(bounds.vec); i++) {
+      const float dist = dist_signed_to_plane_v3(bounds.vec[i], base_plane);
+      madd_v3_v3fl(bounds.vec[i], base_plane, -dist);
+      madd_v3_v3fl(bounds.vec[i], ipd->step[STEP_BASE].degenerate_diagonal_display, dist);
+    }
+  }
+
+  if (flatten_axis == STEP_DEPTH) {
+    const float *base_plane = ipd->step[0].plane;
+    for (int i = 0; i < 4; i++) {
+      const float dist = dist_signed_to_plane_v3(bounds.vec[i + 4], base_plane);
+      madd_v3_v3fl(bounds.vec[i + 4], base_plane, -dist);
+      madd_v3_v3fl(bounds.vec[i + 4], ipd->step[STEP_DEPTH].degenerate_diagonal_display, dist);
+    }
+  }
+
   draw_line_bounds(&bounds, color);
 
   if (ipd->primitive_type == PLACE_PRIMITIVE_TYPE_CUBE) {
@@ -629,14 +748,22 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region
   if (use_depth) {
     GPU_depth_test(GPU_DEPTH_NONE);
     color[3] = 0.15f;
-    draw_primitive_view_impl(C, ipd, color);
+    draw_primitive_view_impl(C, ipd, color, -1);
+  }
+
+  /* Show a flattened projection if the current step is aligned to the view. */
+  if (ipd->step[ipd->step_index].is_degenerate_view_align) {
+    const RegionView3D *rv3d = ipd->region->regiondata;
+    if (!rv3d->is_persp) {
+      draw_primitive_view_impl(C, ipd, color, ipd->step_index);
+    }
   }
 
   if (use_depth) {
     GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
   }
   color[3] = 1.0f;
-  draw_primitive_view_impl(C, ipd, color);
+  draw_primitive_view_impl(C, ipd, color, -1);
 
   if (use_depth) {
     if (depth_test_enabled == false) {
@@ -744,15 +871,30 @@ static void view3d_interactive_add_calc_plane(bContext *C,
     if (use_depth_fallback || (plane_depth == PLACE_DEPTH_CURSOR_PLANE)) {
       /* Cursor plane. */
       float plane[4];
-      plane_from_point_normal_v3(plane, scene->cursor.location, r_matrix_orient[plane_axis]);
-      if (ED_view3d_win_to_3d_on_plane(region, plane, mval_fl, false, r_co_src)) {
+      const float *plane_normal = r_matrix_orient[plane_axis];
+
+      const float view_axis_dot = fabsf(dot_v3v3(rv3d->viewinv[2], r_matrix_orient[plane_axis]));
+      if (view_axis_dot < eps_view_align) {
+        /* In this case, just project onto the view plane as it's important the location
+         * is _always_ under the mouse cursor, even if it turns out that wont lie on
+         * the original 'plane' that's been calculated for us. */
+        plane_normal = rv3d->viewinv[2];
+      }
+
+      plane_from_point_normal_v3(plane, scene->cursor.location, plane_normal);
+
+      if (view3d_win_to_3d_on_plane_maybe_fallback(region, plane, mval_fl, NULL, r_co_src)) {
         use_depth_fallback = false;
       }
-      /* Even if the calculation works, it's possible the point found is behind the view. */
+
+      /* Even if the calculation works, it's possible the point found is behind the view,
+       * or very far away (past the far clipping).
+       * In either case creating objects wont be useful. */
       if (rv3d->is_persp) {
         float dir[3];
         sub_v3_v3v3(dir, rv3d->viewinv[3], r_co_src);
-        if (dot_v3v3(dir, rv3d->viewinv[2]) < v3d->clip_start) {
+        const float dot = dot_v3v3(dir, rv3d->viewinv[2]);
+        if (dot < v3d->clip_start || dot > v3d->clip_end) {
           use_depth_fallback = true;
         }
       }
@@ -828,6 +970,67 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
 
   copy_v3_v3(ipd->step[0].co_dst, ipd->co_src);
 
+  {
+    RegionView3D *rv3d = ipd->region->regiondata;
+    const float view_axis_dot = fabsf(dot_v3v3(rv3d->viewinv[2], ipd->matrix_orient[plane_axis]));
+    ipd->step[STEP_BASE].is_degenerate_view_align = view_axis_dot < eps_view_align;
+    ipd->step[STEP_DEPT

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list