[Bf-blender-cvs] [927456491aa] master: Fix edit-mesh partial update regression when snapping to normals

Campbell Barton noreply at git.blender.org
Thu Jul 1 06:54:19 CEST 2021


Commit: 927456491aa4368e8fadcff1319587a24a73009b
Author: Campbell Barton
Date:   Thu Jul 1 14:39:53 2021 +1000
Branches: master
https://developer.blender.org/rB927456491aa4368e8fadcff1319587a24a73009b

Fix edit-mesh partial update regression when snapping to normals

Edit-mesh partial update logic assumed translate didn't need normals
to be recalculated (for faces with all vertices being transformed).

However translate can optionally rotate which requires
all transformed normals to be updated.

Check for this case and use the previous partial-update method
when it modified extra geometry, so the normals are properly reset.
Further updates need not recalculate them.

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

M	source/blender/editors/transform/transform_convert_mesh.c

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

diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 58cf82c72d3..de2f96c0f97 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -69,13 +69,13 @@ struct TransCustomData_PartialUpdate {
   float prop_size_prev;
 };
 
+/**
+ * \note It's important to order from least to greatest (which updates more data),
+ * since the larger values are used when values change between updates
+ * (which can happen when rotation is enabled with snapping).
+ */
 enum ePartialType {
   PARTIAL_NONE = -1,
-  /**
-   * Update for all tagged vertices (any kind of deformation).
-   * Use as a default since it can be used with any kind of deformation.
-   */
-  PARTIAL_TYPE_ALL = 0,
   /**
    * Update only faces between tagged and non-tagged faces (affine transformations).
    * Use when transforming is guaranteed not to change the relative locations of vertices.
@@ -83,14 +83,29 @@ enum ePartialType {
    * This has the advantage that selecting the entire mesh or only isolated elements,
    * can skip normal/tessellation updates entirely, so it's worth using when possible.
    */
-  PARTIAL_TYPE_GROUP = 1,
-
+  PARTIAL_TYPE_GROUP = 0,
+  /**
+   * Update for all tagged vertices (any kind of deformation).
+   * Use as a default since it can be used with any kind of deformation.
+   */
+  PARTIAL_TYPE_ALL = 1,
 };
+
 #define PARTIAL_TYPE_MAX 2
 
+/**
+ * Settings used for a single update,
+ * use for comparison with previous updates.
+ */
+struct PartialTypeState {
+  enum ePartialType for_looptri;
+  enum ePartialType for_normals;
+};
+
 struct TransCustomDataMesh {
   struct TransCustomDataLayer *cd_layer_correct;
   struct TransCustomData_PartialUpdate partial_update[PARTIAL_TYPE_MAX];
+  struct PartialTypeState partial_update_state_prev;
 };
 
 static struct TransCustomDataMesh *tc_mesh_customdata_ensure(TransDataContainer *tc)
@@ -102,6 +117,8 @@ static struct TransCustomDataMesh *tc_mesh_customdata_ensure(TransDataContainer
     tc->custom.type.data = MEM_callocN(sizeof(struct TransCustomDataMesh), __func__);
     tc->custom.type.free_cb = tc_mesh_customdata_free_fn;
     tcmd = tc->custom.type.data;
+    tcmd->partial_update_state_prev.for_looptri = PARTIAL_NONE;
+    tcmd->partial_update_state_prev.for_normals = PARTIAL_NONE;
   }
   return tcmd;
 }
@@ -1895,9 +1912,7 @@ static BMPartialUpdate *tc_mesh_partial_ensure(TransInfo *t,
   return pupdate->cache;
 }
 
-static void tc_mesh_partial_types_calc(TransInfo *t,
-                                       enum ePartialType *r_partial_for_looptri,
-                                       enum ePartialType *r_partial_for_normals)
+static void tc_mesh_partial_types_calc(TransInfo *t, struct PartialTypeState *r_partial_state)
 {
   /* Calculate the kind of partial updates which can be performed. */
   enum ePartialType partial_for_normals = PARTIAL_NONE;
@@ -1909,6 +1924,10 @@ static void tc_mesh_partial_types_calc(TransInfo *t,
     case TFM_TRANSLATION: {
       partial_for_looptri = PARTIAL_TYPE_GROUP;
       partial_for_normals = PARTIAL_TYPE_GROUP;
+      /* Translation can rotate when snapping to normal. */
+      if (activeSnap(t) && usingSnappingNormal(t) && validSnappingNormal(t)) {
+        partial_for_normals = PARTIAL_TYPE_ALL;
+      }
       break;
     }
     case TFM_ROTATION: {
@@ -1946,53 +1965,65 @@ static void tc_mesh_partial_types_calc(TransInfo *t,
     }
   }
 
-  *r_partial_for_looptri = partial_for_looptri;
-  *r_partial_for_normals = partial_for_normals;
+  r_partial_state->for_looptri = partial_for_looptri;
+  r_partial_state->for_normals = partial_for_normals;
 }
 
 static void tc_mesh_partial_update(TransInfo *t,
                                    TransDataContainer *tc,
-                                   enum ePartialType partial_for_looptri,
-                                   enum ePartialType partial_for_normals)
+                                   const struct PartialTypeState *partial_state)
 {
   BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 
-  /* Matching. */
-  if ((partial_for_looptri == PARTIAL_TYPE_ALL) && (partial_for_normals == PARTIAL_TYPE_ALL)) {
+  struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc);
+
+  const struct PartialTypeState *partial_state_prev = &tcmd->partial_update_state_prev;
+
+  /* Promote the partial update types based on the previous state
+   * so the values that no longer modified are reset before being left as-is.
+   * Needed for translation which can toggle snap-to-normal during transform.  */
+  const enum ePartialType partial_for_looptri = MAX2(partial_state->for_looptri,
+                                                     partial_state_prev->for_looptri);
+  const enum ePartialType partial_for_normals = MAX2(partial_state->for_normals,
+                                                     partial_state_prev->for_normals);
+
+  if ((partial_for_looptri == PARTIAL_TYPE_ALL) && (partial_for_normals == PARTIAL_TYPE_ALL) &&
+      (em->bm->totvert == em->bm->totvertsel)) {
     /* The additional cost of generating the partial connectivity data isn't justified
      * when all data needs to be updated.
      *
      * While proportional editing can cause all geometry to need updating with a partial
      * selection. It's impractical to calculate this ahead of time. Further, the down side of
      * using partial updates when their not needed is negligible. */
-    if (em->bm->totvert == em->bm->totvertsel) {
-      BKE_editmesh_looptri_and_normals_calc(em);
-      return;
-    }
-  }
-
-  /* Not matching. */
-  if (partial_for_looptri != PARTIAL_NONE) {
-    BMPartialUpdate *bmpinfo = tc_mesh_partial_ensure(t, tc, partial_for_looptri);
-    BKE_editmesh_looptri_calc_with_partial_ex(em,
-                                              bmpinfo,
-                                              &(const struct BMeshCalcTessellation_Params){
-                                                  .face_normals = true,
-                                              });
-  }
-
-  if (partial_for_normals != PARTIAL_NONE) {
-    BMPartialUpdate *bmpinfo = tc_mesh_partial_ensure(t, tc, partial_for_normals);
-    /* While not a large difference, take advantage of existing normals where possible. */
-    const bool face_normals = !((partial_for_looptri == PARTIAL_TYPE_ALL) ||
-                                ((partial_for_looptri == PARTIAL_TYPE_GROUP) &&
-                                 (partial_for_normals == PARTIAL_TYPE_GROUP)));
-    BM_mesh_normals_update_with_partial_ex(em->bm,
-                                           bmpinfo,
-                                           &(const struct BMeshNormalsUpdate_Params){
-                                               .face_normals = face_normals,
-                                           });
+    BKE_editmesh_looptri_and_normals_calc(em);
   }
+  else {
+    if (partial_for_looptri != PARTIAL_NONE) {
+      BMPartialUpdate *bmpinfo = tc_mesh_partial_ensure(t, tc, partial_for_looptri);
+      BKE_editmesh_looptri_calc_with_partial_ex(em,
+                                                bmpinfo,
+                                                &(const struct BMeshCalcTessellation_Params){
+                                                    .face_normals = true,
+                                                });
+    }
+
+    if (partial_for_normals != PARTIAL_NONE) {
+      BMPartialUpdate *bmpinfo = tc_mesh_partial_ensure(t, tc, partial_for_normals);
+      /* While not a large difference, take advantage of existing normals where possible. */
+      const bool face_normals = !((partial_for_looptri == PARTIAL_TYPE_ALL) ||
+                                  ((partial_for_looptri == PARTIAL_TYPE_GROUP) &&
+                                   (partial_for_normals == PARTIAL_TYPE_GROUP)));
+      BM_mesh_normals_update_with_partial_ex(em->bm,
+                                             bmpinfo,
+                                             &(const struct BMeshNormalsUpdate_Params){
+                                                 .face_normals = face_normals,
+                                             });
+    }
+  }
+
+  /* Store the previous requested (not the previous used),
+   * since the values used may have been promoted based on the previous types. */
+  tcmd->partial_update_state_prev = *partial_state;
 }
 
 /** \} */
@@ -2059,13 +2090,13 @@ void recalcData_mesh(TransInfo *t)
     tc_mesh_customdatacorrect_restore(t);
   }
 
-  enum ePartialType partial_for_looptri, partial_for_normals;
-  tc_mesh_partial_types_calc(t, &partial_for_looptri, &partial_for_normals);
+  struct PartialTypeState partial_state;
+  tc_mesh_partial_types_calc(t, &partial_state);
 
   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
     DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
 
-    tc_mesh_partial_update(t, tc, partial_for_looptri, partial_for_normals);
+    tc_mesh_partial_update(t, tc, &partial_state);
   }
 }
 /** \} */



More information about the Bf-blender-cvs mailing list