Mesh Tool,
authorCampbell Barton <ideasman42@gmail.com>
Sun, 7 Apr 2013 11:41:37 +0000 (11:41 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sun, 7 Apr 2013 11:41:37 +0000 (11:41 +0000)
inset interpolation now works for 'inset_region', enabled by default.

Example:
http://www.graphicall.org/ftp/ideasman42/inset_interpolation_new.png

source/blender/bmesh/intern/bmesh_interp.c
source/blender/bmesh/intern/bmesh_interp.h
source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/operators/bmo_inset.c
source/blender/editors/mesh/editmesh_inset.c

index 120618a..b658581 100644 (file)
@@ -167,6 +167,29 @@ void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BM
  *
  * \note Only handles loop customdata. multires is handled.
  */
+void BM_face_interp_from_face_ex(BMesh *bm, BMFace *target, BMFace *source,
+                                 void **blocks, float (*cos_2d)[2], float axis_mat[3][3])
+{
+       BMLoop *l_iter;
+       BMLoop *l_first;
+
+       float *w = BLI_array_alloca(w, source->len);
+       float co[2];
+       int i;
+
+       if (source != target)
+               BM_elem_attrs_copy(bm, bm, source, target);
+
+       /* interpolate */
+       i = 0;
+       l_iter = l_first = BM_FACE_FIRST_LOOP(target);
+       do {
+               mul_v2_m3v3(co, axis_mat, l_iter->v->co);
+               interp_weights_poly_v2(w, cos_2d, source->len, co);
+               CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, l_iter->head.data);
+       } while (i++, (l_iter = l_iter->next) != l_first);
+}
+
 void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source)
 {
        BMLoop *l_iter;
@@ -176,14 +199,11 @@ void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source)
        float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len);
        float *w        = BLI_array_alloca(w,      source->len);
        float axis_mat[3][3];  /* use normal to transform into 2d xy coords */
-       float co[2];
        int i;
 
        /* convert the 3d coords into 2d for projection */
        axis_dominant_v3_to_m3(axis_mat, source->no);
 
-       BM_elem_attrs_copy(bm, bm, source, target);
-
        i = 0;
        l_iter = l_first = BM_FACE_FIRST_LOOP(source);
        do {
@@ -191,14 +211,8 @@ void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source)
                blocks[i] = l_iter->head.data;
        } while (i++, (l_iter = l_iter->next) != l_first);
 
-       /* interpolate */
-       i = 0;
-       l_iter = l_first = BM_FACE_FIRST_LOOP(target);
-       do {
-               mul_v2_m3v3(co, axis_mat, l_iter->v->co);
-               interp_weights_poly_v2(w, cos_2d, source->len, co);
-               CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, l_iter->head.data);
-       } while (i++, (l_iter = l_iter->next) != l_first);
+       BM_face_interp_from_face_ex(bm, target, source,
+                                   blocks, cos_2d, axis_mat);
 }
 
 /**
index 3563ed1..1a1ca24 100644 (file)
@@ -42,6 +42,8 @@ void  BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int d
 float BM_elem_float_data_get(CustomData *cd, void *element, int type);
 void  BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val);
 
+void BM_face_interp_from_face_ex(BMesh *bm, BMFace *target, BMFace *source,
+                                 void **blocks, float (*cos_2d)[2], float axis_mat[3][3]);
 void  BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source);
 void  BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
                               const bool do_vertex, const bool do_multires);
index 41e1875..9006f38 100644 (file)
@@ -1533,6 +1533,7 @@ static BMOpDefine bmo_inset_region_def = {
        {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}},    /* input faces */
         {"use_boundary", BMO_OP_SLOT_BOOL},
         {"use_even_offset", BMO_OP_SLOT_BOOL},
+        {"use_interpolate", BMO_OP_SLOT_BOOL},
         {"use_relative_offset", BMO_OP_SLOT_BOOL},
         {"thickness", BMO_OP_SLOT_FLT},
         {"depth", BMO_OP_SLOT_FLT},
index 26574aa..d58894d 100644 (file)
@@ -32,6 +32,8 @@
 
 #include "BLI_math.h"
 #include "BLI_array.h"
+#include "BLI_memarena.h"
+#include "BKE_customdata.h"
 
 #include "bmesh.h"
 
@@ -243,6 +245,59 @@ typedef struct SplitEdgeInfo {
        BMLoop *l;
 } SplitEdgeInfo;
 
+
+/**
+ * Interpolation, this is more complex for regions since we're not creating new faces
+ * and throwing away old ones, so instead, store face data needed for interpolation.
+ *
+ * \note This uses CustomData functions in quite a low-level way which should be
+ * avoided, but in this case its hard to do without storing a duplicate mesh. */
+
+/* just enough of a face to store interpolation data we can use once the inset is done */
+typedef struct InterpFace {
+       BMFace *f;
+       void **blocks;
+       float (*cos_2d)[2];
+       float axis_mat[3][3];
+} InterpFace;
+
+/* basically a clone of #BM_vert_interp_from_face */
+static void bm_interp_face_store(InterpFace *iface, BMesh *bm, BMFace *f, MemArena *interp_arena)
+{
+       BMLoop *l_iter, *l_first;
+       void **blocks      = iface->blocks = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks) * f->len);
+       float (*cos_2d)[2] = iface->cos_2d = BLI_memarena_alloc(interp_arena, sizeof(*iface->cos_2d) * f->len);
+       void *axis_mat     = iface->axis_mat;
+       int i;
+
+       axis_dominant_v3_to_m3(axis_mat, f->no);
+
+       iface->f = f;
+
+       i = 0;
+       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+       do {
+               mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
+               blocks[i] = NULL;
+               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_iter->head.data, &blocks[i]);
+               /* if we were not modifying the loops later we would do... */
+               // blocks[i] = l_iter->head.data;
+
+               /* use later for index lookups */
+               BM_elem_index_set(l_iter, i); /* set_ok */
+       } while (i++, (l_iter = l_iter->next) != l_first);
+}
+static void bm_interp_face_free(InterpFace *iface, BMesh *bm)
+{
+       void **blocks = iface->blocks;
+       int i;
+
+       for (i = 0; i < iface->f->len; i++) {
+               CustomData_bmesh_free_block(&bm->ldata, &blocks[i]);
+       }
+}
+
+
 /**
  * return the tag loop where there is...
  * - only 1 tagged face attached to this edge.
@@ -298,6 +353,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        const bool use_even_offset     = BMO_slot_bool_get(op->slots_in, "use_even_offset");
        const bool use_even_boundry    = use_even_offset; /* could make own option */
        const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
+       const bool use_interpolate     = BMO_slot_bool_get(op->slots_in, "use_interpolate");
        const float thickness          = BMO_slot_float_get(op->slots_in, "thickness");
        const float depth              = BMO_slot_float_get(op->slots_in, "depth");
 
@@ -307,11 +363,24 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        SplitEdgeInfo *edge_info;
        SplitEdgeInfo *es;
 
+       /* Interpolation Vars */
+       /* an array alligned with faces but only fill items which are used. */
+       InterpFace **iface_array = NULL;
+       int          iface_array_len;
+       MemArena *interp_arena = NULL;
+
        BMVert *v;
        BMEdge *e;
        BMFace *f;
        int i, j, k;
 
+       if (use_interpolate) {
+               interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+               /* warning, we could be more clever here and not over alloc */
+               iface_array = MEM_callocN(sizeof(*iface_array) * bm->totface, __func__);
+               iface_array_len = bm->totface;
+       }
+
        if (use_outset == false) {
                BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
                BMO_slot_buffer_hflag_enable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false);
@@ -392,9 +461,22 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                /* important to tag again here */
                BM_elem_flag_enable(es->e_new->v1, BM_ELEM_TAG);
                BM_elem_flag_enable(es->e_new->v2, BM_ELEM_TAG);
-       }
 
 
+               /* initialize interpolation vars */
+               /* this could go in its own loop,
+                * only use the 'es->l->f' so we don't store loops for faces which have no mixed selection */
+               if (use_interpolate) {
+                       const int j = BM_elem_index_get((f = es->l->f));
+                       if (iface_array[j] == NULL) {
+                               InterpFace *iface = BLI_memarena_alloc(interp_arena, sizeof(*iface));
+                               bm_interp_face_store(iface, bm, f, interp_arena);
+                               iface_array[j] = iface;
+                       }
+               }
+               /* done interpolation */
+       }
+
        /* show edge normals for debugging */
 #if 0
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
@@ -638,6 +720,16 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                }
        }
 
+       if (use_interpolate) {
+               for (i = 0; i < iface_array_len; i++) {
+                       if (iface_array[i]) {
+                               InterpFace *iface = iface_array[i];
+                               BM_face_interp_from_face_ex(bm, iface->f, iface->f,
+                                                           iface->blocks, iface->cos_2d, iface->axis_mat);
+                       }
+               }
+       }
+
        /* create faces */
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
                BMVert *varr[4] = {NULL};
@@ -704,10 +796,30 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                        l_b = l_a->next;
 
                        /* swap a<->b intentionally */
-                       BM_elem_attrs_copy(bm, bm, l_a_other, l_b);
-                       BM_elem_attrs_copy(bm, bm, l_b_other, l_a);
+                       if (use_interpolate) {
+                               InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)];
+                               const int i_a = BM_elem_index_get(l_a_other);
+                               const int i_b = BM_elem_index_get(l_b_other);
+                               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks[i_a], &l_b->head.data);
+                               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks[i_b], &l_a->head.data);
+                       }
+                       else {
+                               BM_elem_attrs_copy(bm, bm, l_a_other, l_b);
+                               BM_elem_attrs_copy(bm, bm, l_b_other, l_a);
+                       }
                }
+       }
 #endif
+
+       if (use_interpolate) {
+               for (i = 0; i < iface_array_len; i++) {
+                       if (iface_array[i]) {
+                               bm_interp_face_free(iface_array[i], bm);
+                       }
+               }
+
+               BLI_memarena_free(interp_arena);
+               MEM_freeN(iface_array);
        }
 
        /* we could flag new edges/verts too, is it useful? */
index c9a9601..7a879d3 100644 (file)
@@ -209,9 +209,9 @@ static int edbm_inset_calc(wmOperator *op)
        }
        else {
                EDBM_op_init(em, &bmop, op,
-                            "inset_region faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b "
-                            "thickness=%f depth=%f use_outset=%b",
-                            BM_ELEM_SELECT, use_boundary, use_even_offset, use_relative_offset,
+                            "inset_region faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b"
+                            " use_interpolate=%b thickness=%f depth=%f use_outset=%b",
+                            BM_ELEM_SELECT, use_boundary, use_even_offset, use_relative_offset, use_interpolate,
                             thickness, depth, use_outset);
        }
        BMO_op_exec(em->bm, &bmop);