Added method to remove vertices from basic meshes by remapping the v/e/l/p custom...
[blender.git] / source / blender / editors / sculpt_paint / sculpt.c
index 93bfef1834df4ed138901e7c54f200003d4ad16b..8f2d0242ea0112bafa524d7b9ff148d892ec01dc 100644 (file)
 #include "BLI_alloca.h"
 #include "BLI_array.h"
 
-#define SILHOUETTE_STROKE_STORE_CHUNK 512
-/*#define DEBUG_DRAW*/
+#define SIL_STROKE_STORE_CHUNK 512
+/* Store bias is used to bias a close estimate since resizing is more expensive than bigger array on first allocate*/
+#define STORE_ESTIMATE_BIAS 0.1f
+/* Fillet Blur determines the fuzziness wether a vert is intersecting or not.
+ * Important for example if two shapes with the same thickness intersect. */
+#define SIL_FILLET_BLUR_MAX 0.3f
+#define SIL_FILLET_BLUR_MIN 0.001f
+
+#define DEBUG_DRAW
 #ifdef DEBUG_DRAW
 /* static void bl_debug_draw(void);*/
 /* add these locally when using these functions for testing */
@@ -157,6 +164,66 @@ typedef struct SilhouetteStroke {
        BB bb;
 } SilhouetteStroke;
 
+#ifdef DEBUG_DRAW
+void bl_debug_draw_BB_add(BB *bb,const unsigned int col){
+       float v1[3],v2[3],v3[3],v4[3];
+       float xd[3], yd[3], zd[3];
+
+       bl_debug_color_set(col);
+
+       xd[0] = bb->bmax[0]-bb->bmin[0];
+       xd[1] = 0.0f;
+       xd[2] = 0.0f;
+
+       yd[0] = 0.0f;
+       yd[1] = bb->bmax[1]-bb->bmin[1];
+       yd[2] = 0.0f;
+
+       zd[0] = 0.0f;
+       zd[1] = 0.0f;
+       zd[2] = bb->bmax[2]-bb->bmin[2];
+
+       copy_v3_v3(v1,bb->bmin);
+       copy_v3_v3(v2,bb->bmin);
+       add_v3_v3(v2,xd);
+       add_v3_v3v3(v3,v1,yd);
+       add_v3_v3v3(v4,v2,yd);
+
+       bl_debug_draw_edge_add(v1,v2);
+       bl_debug_draw_edge_add(v1,v3);
+       bl_debug_draw_edge_add(v2,v4);
+
+       copy_v3_v3(v1,v3);
+       copy_v3_v3(v2,v4);
+       add_v3_v3v3(v3,v1,zd);
+       add_v3_v3v3(v4,v2,zd);
+
+       bl_debug_draw_edge_add(v1,v2);
+       bl_debug_draw_edge_add(v1,v3);
+       bl_debug_draw_edge_add(v2,v4);
+
+       copy_v3_v3(v1,v3);
+       copy_v3_v3(v2,v4);
+       sub_v3_v3v3(v3,v1,yd);
+       sub_v3_v3v3(v4,v2,yd);
+
+       bl_debug_draw_edge_add(v1,v2);
+       bl_debug_draw_edge_add(v1,v3);
+       bl_debug_draw_edge_add(v2,v4);
+
+       copy_v3_v3(v1,v3);
+       copy_v3_v3(v2,v4);
+       sub_v3_v3v3(v3,v1,zd);
+       sub_v3_v3v3(v4,v2,zd);
+
+       bl_debug_draw_edge_add(v1,v2);
+       bl_debug_draw_edge_add(v1,v3);
+       bl_debug_draw_edge_add(v2,v4);
+       
+}
+#endif
+
+
 typedef enum {
        SIL_INIT = 0,
        SIL_DRAWING = 1,
@@ -177,11 +244,21 @@ typedef struct SilhouetteData {
 
        SilhouetteState state;  /* Operator state */
 
-       float depth;                    /* Depth or thickness of the generated shape */
-       float smoothness;               /* Smoothness of the generated shape */
-       int resolution;                 /* Subdivision of the shape*/
-       float anchor[3];                /* Origin point of the reference plane */
-       float z_vec[3];                 /* Orientation of the reference plane */
+       float depth;                                    /* Depth or thickness of the generated shape */
+       float smoothness;                               /* Smoothness of the generated shape */
+       int resolution;                                 /* Subdivision of the shape*/
+       float anchor[3];                                /* Origin point of the reference plane */
+       float z_vec[3];                                 /* Orientation of the reference plane */
+       MeshElemMap *emap;                              /* Original Mesh vert -> edges map */
+       GHash *i_edges;                                 /* Edges crossing the both shapes. (only orig mesh)*/
+       int *fillet_ring_orig;                  /* ring_edges to connect to in the orig mesh */
+       int *fillet_ring_orig_start;    /* start positions to each individual ring */
+       int *fillet_ring_new;                   /* ring_edges to connect to in the new mesh */
+       int *fillet_ring_new_start;             /* start positions to each individual ring */
+       int num_rings, fillet_ring_tot;
+       int *inter_edges;                               /* edges crossing the two shapes */
+       int num_inter_edges;                    /* number of edges crossing */
+       BB *fillet_ring_bbs;                            /* every ring gets a Bounding box to check intersection with branches */
 } SilhouetteData;
 
 /** \name Tool Capabilities
@@ -591,6 +668,8 @@ typedef struct SculptThreadedTaskData {
        bool smooth_mask;
        bool has_bm_orco;
        SilhouetteData *sil;
+       int *v_to_rm; /* Shared array handle access with mutex! */
+       int num_v_to_rm;
 
        SculptProjectVector *spvc;
        float *offset;
@@ -4792,8 +4871,11 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
                                     const float mouse[2])
 {
        /* Don't start the stroke until mouse goes over the mesh.
-        * note: mouse will only be null when re-executing the saved stroke. */
-       if (!mouse || over_mesh(C, op, mouse[0], mouse[1])) {
+        * note: mouse will only be null when re-executing the saved stroke.
+        * We have exception for 'exec' strokes since they may not set 'mouse', only 'location', see: T52195. */
+       if (((op->flag & OP_IS_INVOKE) == 0) ||
+           (mouse == NULL) || over_mesh(C, op, mouse[0], mouse[1]))
+       {
                Object *ob = CTX_data_active_object(C);
                SculptSession *ss = ob->sculpt;
                Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -5085,6 +5167,19 @@ static void BB_reset(BB *bb)
        bb->bmax[0] = bb->bmax[1] = bb->bmax[2] = -FLT_MAX;
 }
 
+static bool bb_intersect(BB *bb1, BB *bb2) {
+       int i;
+
+       /* min is inclusive max is exclusive? BB*/
+       for (i = 0; i < 3; ++i) {
+               if(bb1->bmin[i] >= bb2->bmax[i] || bb1->bmax[i] < bb2->bmin[i]){
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 static void silhouette_stroke_free(SilhouetteStroke *stroke)
 {
        if (stroke) {
@@ -5098,10 +5193,10 @@ static void silhouette_stroke_free(SilhouetteStroke *stroke)
 static SilhouetteStroke *silhouette_stroke_new()
 {
        SilhouetteStroke *stroke = MEM_callocN(sizeof(SilhouetteStroke), "SilhouetteStroke");
-       stroke->points = MEM_callocN(sizeof(float) * 3 * SILHOUETTE_STROKE_STORE_CHUNK,"SilhouetteStrokePoints");
-       stroke->points_v2 = MEM_callocN(sizeof(float) * 2 * SILHOUETTE_STROKE_STORE_CHUNK,"SilhouetteStrokePoints");
+       stroke->points = MEM_callocN(sizeof(float) * 3 * SIL_STROKE_STORE_CHUNK,"SilhouetteStrokePoints");
+       stroke->points_v2 = MEM_callocN(sizeof(float) * 2 * SIL_STROKE_STORE_CHUNK,"SilhouetteStrokePoints");
        stroke->totvert = 0;
-       stroke->max_verts = SILHOUETTE_STROKE_STORE_CHUNK;
+       stroke->max_verts = SIL_STROKE_STORE_CHUNK;
        BB_reset(&stroke->bb);
        return stroke;
 }
@@ -5130,6 +5225,14 @@ static SilhouetteData *silhouette_data_new(bContext *C)
 
        copy_v3_v3(sil->anchor, fp);
 
+       /* Intersection variables */
+       sil->fillet_ring_orig = NULL;
+       sil->fillet_ring_orig_start = NULL;
+       sil->fillet_ring_new = NULL;
+       sil->fillet_ring_new_start = NULL;
+       sil->inter_edges = NULL;
+       sil->fillet_ring_bbs = NULL;
+
        sil->scene = scene;
        sil->ob = obedit;
        sil->state = SIL_INIT;
@@ -5142,6 +5245,25 @@ static void silhouette_data_free(struct wmOperator *op)
        data = op->customdata;
        if (data) {
                silhouette_stroke_free(data->current_stroke);
+
+               if (data->inter_edges) {
+                       MEM_freeN(data->inter_edges);
+               }
+               if (data->fillet_ring_orig) {
+                       MEM_freeN(data->fillet_ring_orig);
+               }
+               if (data->fillet_ring_orig_start) {
+                       MEM_freeN(data->fillet_ring_orig_start);
+               }
+               if (data->fillet_ring_new) {
+                       MEM_freeN(data->fillet_ring_new);
+               }
+               if (data->fillet_ring_new_start) {
+                       MEM_freeN(data->fillet_ring_new_start);
+               }
+               if (data->fillet_ring_bbs) {
+                       MEM_freeN(data->fillet_ring_bbs);
+               }
                MEM_SAFE_FREE(data);
        }
 }
@@ -5157,7 +5279,7 @@ static void silhoute_stroke_point_to_3d(SilhouetteData *sil, int point, float r_
 static void silhouette_stroke_add_3Dpoint(SilhouetteStroke *stroke, float point[3])
 {
        if (stroke->totvert >= stroke->max_verts) {
-               stroke->max_verts += SILHOUETTE_STROKE_STORE_CHUNK;
+               stroke->max_verts += SIL_STROKE_STORE_CHUNK;
                stroke->points = MEM_reallocN(stroke->points, sizeof(float) * 3 * stroke->max_verts);
                stroke->points_v2 = MEM_reallocN(stroke->points_v2, sizeof(float) * 2 * stroke->max_verts);
        }
@@ -5170,7 +5292,7 @@ static void silhouette_stroke_add_3Dpoint(SilhouetteStroke *stroke, float point[
 static void silhouette_stroke_add_point(SilhouetteData *sil, SilhouetteStroke *stroke, float point[2])
 {
        if (stroke->totvert >= stroke->max_verts) {
-               stroke->max_verts += SILHOUETTE_STROKE_STORE_CHUNK;
+               stroke->max_verts += SIL_STROKE_STORE_CHUNK;
                stroke->points = MEM_reallocN(stroke->points, sizeof(float) * 3 * stroke->max_verts);
                stroke->points_v2 = MEM_reallocN(stroke->points_v2, sizeof(float) * 2 * stroke->max_verts);
        }
@@ -5184,7 +5306,10 @@ static void silhouette_stroke_add_point(SilhouetteData *sil, SilhouetteStroke *s
 /* Set reference plane, 3D plane which is drawn on in 2D */
 static void silhouette_set_ref_plane(SilhouetteData *sil)
 {
-       ED_view3d_global_to_vector(sil->ar->regiondata, sil->anchor, sil->z_vec);
+       /*Get the view vector. Same as ED_view3d_global_to_vector from the center of screen or orthographic*/
+       negate_v3_v3(sil->z_vec, sil->vc.rv3d->viewinv[2]);
+       normalize_v3(sil->z_vec);
+       /*ED_view3d_global_to_vector(sil->ar->regiondata, sil->anchor, sil->z_vec);*/
 }
 
 static void sculpt_silhouette_stroke_update(float mouse[2], SilhouetteData *sil)
@@ -5230,6 +5355,7 @@ typedef struct SpineBranch{
        int *e_start_arr;               /* Edges on the ends are stored (used primarly for bridging) */
        int fs_bs_offset;               /* Frontside edge offset to backside*/
        int *e_flip_side_ends;  /* Front and backside connecting edges of each part*/
+       bool intersecting;
 }SpineBranch;
 
 /* Main Tree Container */
@@ -6978,21 +7104,21 @@ static void silhouette_create_shape_mesh(bContext *C, Mesh *me, SilhouetteData *
                                        add_ss_cap(sil, a_branch, me, z_vec, depth, v_steps, w_steps, smoothness, n_ori, false);
                                        add_ss_cap(sil, a_branch, me, inv_z_vec, depth, v_steps, w_steps, smoothness, !n_ori, true);
 #ifdef DEBUG_DRAW
-                                       debug_branch(a_branch, 0x00ff00);
+                                       /*debug_branch(a_branch, 0x00ff00);*/
 #endif
                                        break;
                                case 2:
                                        add_ss_tube(sil, a_branch, me, z_vec, depth, v_steps, w_steps, smoothness, n_ori, false);
                                        add_ss_tube(sil, a_branch, me, inv_z_vec, depth, v_steps, w_steps, smoothness, !n_ori, true);
 #ifdef DEBUG_DRAW
-                                       debug_branch(a_branch, 0xff0000);
+                                       /*debug_branch(a_branch, 0xff0000);*/
 #endif
                                        break;
                                case 3:
                                        add_ss_tinter(sil, spine, a_branch, me, z_vec, depth, v_steps, w_steps, smoothness, n_ori, false);
                                        add_ss_tinter(sil, spine, a_branch, me, inv_z_vec, depth, v_steps, w_steps, smoothness, !n_ori, true);
 #ifdef DEBUG_DRAW
-                                       debug_branch(a_branch, 0x0000ff);
+                                       /*debug_branch(a_branch, 0x0000ff);*/
 #endif
                                        break;
                        }
@@ -7028,52 +7154,697 @@ static void stroke_smooth_cap(SilhouetteData *sil, SilhouetteStroke *stroke, flo
        }
 }
 
+static void remove_connected_from_edgehash(MeshElemMap *emap, GHash *edge_hash, int v) {
+       for (int e = 0; e < emap[v].count; e++) {
+               BLI_ghash_remove(edge_hash, emap[v].indices[e], NULL, NULL);
+       }
+}
+
+static bool has_cross_border_neighbour(Mesh *me, GHash *vert_hash, GHash *edge_hash, MeshElemMap *emap, int edge, int l_v_edge, int depth) {
+       int v_edge;
+
+       v_edge = me->medge[edge].v1 == l_v_edge ? me->medge[edge].v2 : me->medge[edge].v1;
+
+       if (depth == 0) {
+               return BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(v_edge));
+       } else {
+               if(!BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(v_edge))){
+                       for (int e = 0; e < emap[v_edge].count; e++) {
+                               if(emap[v_edge].indices[e] != edge) {
+                                       if(has_cross_border_neighbour(me, vert_hash, edge_hash, emap, emap[v_edge].indices[e], v_edge, depth - 1)){
+                                               return true;
+                                       }
+                               }
+                       }
+               } else {
+                       BLI_ghash_remove(edge_hash, edge, NULL, NULL);
+               }
+       }
+       return false;
+}
+
+/* Get the adjacent edge which connects the edges within the edge_hash. Used to create multiple ordered loops
+ * v_edge is the endpoint off curr_edge from which to branch off
+ * TODO: One wide strips might get cutoff */
+static int get_adjacent_edge(Mesh *me, MeshElemMap *emap, int curr_edge, int v_edge, GHash *edge_hash, GHash *vert_hash)
+{
+       for (int e = 0; e < emap[v_edge].count; e++) {
+               if(emap[v_edge].indices[e] != curr_edge && has_cross_border_neighbour(me, vert_hash, edge_hash, emap, emap[v_edge].indices[e], v_edge, 1)) {
+                       return emap[v_edge].indices[e];
+               }
+       }
+       for (int e = 0; e < emap[v_edge].count; e++) {
+               if(emap[v_edge].indices[e] != curr_edge && has_cross_border_neighbour(me, vert_hash, edge_hash, emap, emap[v_edge].indices[e], v_edge, 2)) {
+                       return emap[v_edge].indices[e];
+               }
+       }
+       /*End Of Loop. Shouldn't happen with two manifold meshes*/
+       return -1;
+}
+
+/*TODO:Remove Temp debug function*/
+static char *cd_type_name(int t)
+{
+       char *name = "";
+       switch(t) {
+               case -1 : name = "CD_AUTO_FROM_NAME      ";break;
+               case  0 : name = "CD_MVERT               ";break;
+               case  1 : name = "CD_MSTICKY             ";break;  /* DEPRECATED */
+               case  2 : name = "CD_MDEFORMVERT         ";break;
+               case  3 : name = "CD_MEDGE               ";break;
+               case  4 : name = "CD_MFACE               ";break;
+               case  5 : name = "CD_MTFACE              ";break;
+               case  6 : name = "CD_MCOL                ";break;
+               case  7 : name = "CD_ORIGINDEX           ";break;
+               case  8 : name = "CD_NORMAL          ";break;
+                       /*      CD_POLYINDEX        = 9, */
+               case 10 : name = "CD_PROP_FLT        ";break;
+               case 11 : name = "CD_PROP_INT        ";break;
+               case 12 : name = "CD_PROP_STR        ";break;
+               case 13 : name = "CD_ORIGSPACE       ";break;  /* for modifier stack face location mapping */
+               case 14 : name = "CD_ORCO            ";break;
+               case 15 : name = "CD_MTEXPOLY        ";break;
+               case 16 : name = "CD_MLOOPUV         ";break;
+               case 17 : name = "CD_MLOOPCOL        ";break;
+               case 18 : name = "CD_TANGENT         ";break;
+               case 19 : name = "CD_MDISPS          ";break;
+               case 20 : name = "CD_PREVIEW_MCOL    ";break;  /* for displaying weightpaint colors */
+                       /*      CD_ID_MCOL          = 21, */
+               case 22 : name = "CD_TEXTURE_MLOOPCOL";break;
+               case 23 : name = "CD_CLOTH_ORCO      ";break;
+               case 24 : name = "CD_RECAST          ";break;
+
+                       /* BMESH ONLY START */
+               case 25 : name = "CD_MPOLY           ";break;
+               case 26 : name = "CD_MLOOP           ";break;
+               case 27 : name = "CD_SHAPE_KEYINDEX  ";break;
+               case 28 : name = "CD_SHAPEKEY        ";break;
+               case 29 : name = "CD_BWEIGHT         ";break;
+               case 30 : name = "CD_CREASE          ";break;
+               case 31 : name = "CD_ORIGSPACE_MLOOP ";break;
+               case 32 : name = "CD_PREVIEW_MLOOPCOL";break;
+               case 33 : name = "CD_BM_ELEM_PYPTR   ";break;
+                       /* BMESH ONLY END */
+               case 34 : name = "CD_PAINT_MASK      ";break;
+               case 35 : name = "CD_GRID_PAINT_MASK ";break;
+               case 36 : name = "CD_MVERT_SKIN      ";break;
+               case 37 : name = "CD_FREESTYLE_EDGE  ";break;
+               case 38 : name = "CD_FREESTYLE_FACE  ";break;
+               case 39 : name = "CD_MLOOPTANGENT    ";break;
+               case 40 : name = "CD_TESSLOOPNORMAL  ";break;
+               case 41 : name = "CD_CUSTOMLOOPNORMAL";break;
+
+               case 42 : name = "CD_NUMTYPES        ";break;
+               default: name = "No Name";
+       }
+       return name;
+}
+
+#if 0
+static void debug_cd(Mesh *me)
+{
+       char *name = "";
+       printf("Debugging Custom Data:\n\n");
+       printf("%i Customdata Layers in vdata\n", CustomData_number_of_layers_typemask(&me->vdata, CD_MASK_EVERYTHING));
+       printf("%i Customdata Layers in edata\n", CustomData_number_of_layers_typemask(&me->edata, CD_MASK_EVERYTHING));
+       printf("%i Customdata Layers in ldata\n", CustomData_number_of_layers_typemask(&me->ldata, CD_MASK_EVERYTHING));
+       printf("%i Customdata Layers in pdata\n", CustomData_number_of_layers_typemask(&me->pdata, CD_MASK_EVERYTHING));
+       for (int i = 0; i < CustomData_number_of_layers_typemask(&me->vdata, CD_MASK_EVERYTHING); i++) {
+               name = cd_type_name(me->vdata.layers[i].type);
+               printf("Layer found with name %s\n", name);
+       }
+       for (int i = 0; i < CustomData_number_of_layers_typemask(&me->edata, CD_MASK_EVERYTHING); i++) {
+               name = cd_type_name(me->edata.layers[i].type);
+               printf("Layer found with name %s\n", name);
+       }
+       for (int i = 0; i < CustomData_number_of_layers_typemask(&me->ldata, CD_MASK_EVERYTHING); i++) {
+               name = cd_type_name(me->ldata.layers[i].type);
+               printf("Layer found with name %s\n", name);
+       }
+       for (int i = 0; i < CustomData_number_of_layers_typemask(&me->pdata, CD_MASK_EVERYTHING); i++) {
+               name = cd_type_name(me->pdata.layers[i].type);
+               printf("Layer found with name %s\n", name);
+       }
+}
+#endif
+
+/* Copy customdata from source to destination. Layers need to be matching!
+ * redirect_map needs to be the new positions ofelements in the dest array. -1 marks elements which do not get copied over. */
+static void CustomData_copy_partial(const struct CustomData *source, struct CustomData *dest, CustomDataMask mask, int *redirect_map, int tot_elem)
+{
+       int num_layers_to_copy;
+       int d_size;
+       CustomDataLayer *curr_l, *dest_l;
+       char *name;
+
+       num_layers_to_copy = CustomData_number_of_layers_typemask(source, mask);
+
+       for (int l = 0; l < num_layers_to_copy; l++) {
+               curr_l = &source->layers[l];
+               dest_l = &dest->layers[l];
+               if (CD_TYPE_AS_MASK(curr_l->type) & mask) {
+                       for (int i = 0; i < tot_elem; i++) {
+                               if(redirect_map[i] != -1) {
+                                       d_size = CustomData_sizeof(curr_l->type);
+                                       memcpy(dest_l->data + redirect_map[i] * d_size, curr_l->data + i * d_size, d_size);
+                               }
+                       }
+                       name = cd_type_name(curr_l->type);
+                       printf("Layer %s copied %i values of size %i.\n", name, tot_elem, CustomData_sizeof(curr_l->type));
+               }
+       }
+}
+
+/* Function used to remove vertices from a basic mesh
+ * Can be optimised easily be introucing multithreading
+ * Parts and ideas how multithreading can be introduced are marked with "MT:"
+ * TODO: Add Multithreading */
+static void remove_verts_from_mesh(Mesh *me, int *v_to_rm, int num_v_to_rm){
+       int *v_rd_table, *e_rd_table, *l_rd_table, *p_rd_table;
+       int next_del = 0, next_del_pos = 0;
+       MEdge e;
+       MLoop l;
+       MPoly p;
+
+       int n_totvert, n_totedge, n_totloop, n_totpoly;
+
+       CustomData vdata, edata, ldata, pdata;
+
+       /* MT: Prefix Sum / Scan to calculate new positions for vertices.
+        * Calculating the new positions with the vertices removed
+        */
+       v_rd_table = MEM_callocN(sizeof(int) * me->totvert, "Vertex redirect table");
+
+       qsort(v_to_rm, num_v_to_rm, sizeof(int), cmpfunc);
+
+       n_totvert = 0;
+       next_del = v_to_rm[0];
+       for (int i = 0; i < me->totvert; i++) {
+               if (i == next_del) {
+                       if (next_del_pos + 1 < num_v_to_rm) {
+                               next_del_pos ++;
+                               next_del = v_to_rm[next_del_pos];
+                       }
+                       v_rd_table[i] = -1;
+               } else {
+                       v_rd_table[i] = n_totvert;
+                       n_totvert ++;
+               }
+       }
+
+       /* MT: parallelize loop, !shared access to sum value
+        * Calculate new edge positions + remap vertice pointer
+        */
+       e_rd_table = MEM_callocN(sizeof(int) * me->totedge, "Edge redirect table");
+       n_totedge = 0;
+       for (int i = 0; i < me->totedge; i++) {
+               e = me->medge[i];
+               if(v_rd_table[e.v1] == -1 || v_rd_table[e.v2] == -1) {
+                       e_rd_table[i] = -1;
+               } else {
+                       e.v1 = v_rd_table[e.v1];
+                       e.v2 = v_rd_table[e.v2];
+                       me->medge[i] = e;
+                       e_rd_table[i] = n_totedge;
+                       n_totedge ++;
+               }
+       }
+
+       /* MT: same as above
+        * Calculate new loop positions + remap edge pointers */
+       l_rd_table = MEM_callocN(sizeof(int) * me->totloop, "Loop redirect table");
+       n_totloop = 0;
+       for (int i = 0; i < me->totloop; i++) {
+               l = me->mloop[i];
+               if(v_rd_table[l.v] == -1 || e_rd_table[l.e] == -1) {
+                       l_rd_table[i] = -1;
+               } else {
+                       l.v = v_rd_table[l.v];
+                       l.e = e_rd_table[l.e];
+                       me->mloop[i] = l;
+                       l_rd_table[i] = n_totloop;
+                       n_totloop ++;
+               }
+       }
+
+       /* MT: same as above
+        * Calculate new poly positions + remap pointers */
+       p_rd_table = MEM_callocN(sizeof(int) * me->totpoly, "Poly redirect table");
+       n_totpoly = 0;
+       for (int i = 0; i < me->totpoly; i++) {
+               p = me->mpoly[i];
+               for(int l = p.loopstart; l < p.loopstart + p.totloop; l++){
+                       if(l_rd_table[l] == -1) {
+                               p_rd_table[i] = -1;
+                               /* TODO: Bad practise? easily solved other way*/
+                               goto skip_poly;
+                       }
+               }
+               me->mpoly[i].loopstart = l_rd_table[me->mpoly[i].loopstart];
+               p_rd_table[i] = n_totpoly;
+               n_totpoly ++;
+               skip_poly:;
+       }
+
+       /*Redirection tables are done. Continue to copy and allocate new Customdata blocks*/
+       CustomData_copy(&me->vdata, &vdata, CD_MASK_EVERYTHING, CD_CALLOC, n_totvert);
+       CustomData_copy(&me->edata, &edata, CD_MASK_EVERYTHING, CD_CALLOC, n_totedge);
+       CustomData_copy(&me->ldata, &ldata, CD_MASK_EVERYTHING, CD_CALLOC, n_totloop);
+       CustomData_copy(&me->pdata, &pdata, CD_MASK_EVERYTHING, CD_CALLOC, n_totpoly);
+
+       CustomData_copy_partial(&me->vdata, &vdata, CD_MASK_EVERYTHING, v_rd_table, me->totvert);
+       CustomData_copy_partial(&me->edata, &edata, CD_MASK_EVERYTHING, e_rd_table, me->totedge);
+       CustomData_copy_partial(&me->ldata, &ldata, CD_MASK_EVERYTHING, l_rd_table, me->totloop);
+       CustomData_copy_partial(&me->pdata, &pdata, CD_MASK_EVERYTHING, p_rd_table, me->totpoly);
+
+       CustomData_free(&me->vdata, me->totvert);
+       CustomData_free(&me->edata, me->totedge);
+       CustomData_free(&me->ldata, me->totloop);
+       CustomData_free(&me->pdata, me->totpoly);
+
+       me->vdata = vdata;
+       me->edata = edata;
+       me->ldata = ldata;
+       me->pdata = pdata;
+
+       me->totvert = n_totvert;
+       me->totedge = n_totedge;
+       me->totloop = n_totloop;
+       me->totpoly = n_totpoly;
+
+
+       BKE_mesh_update_customdata_pointers(me, true);
+}
+
+static void calc_ring_bbs(SilhouetteData *sil, Mesh *me)
+{
+       int len, edge;
+       BB *curr;
+       sil->fillet_ring_bbs = MEM_callocN(sizeof(BB) * sil->num_rings, "ring bb mem");
+
+       for (int r = 0; r < sil->num_rings; r++) {
+               len = r + 1 < sil->num_rings ? sil->fillet_ring_new_start[r + 1] - sil->fillet_ring_new_start[r] : sil->fillet_ring_tot - sil->fillet_ring_new_start[r];
+               curr = &sil->fillet_ring_bbs[r];
+               BB_reset(curr);
+               for (int e = 0; e < len; e++) {
+                       edge = sil->fillet_ring_new[sil->fillet_ring_new_start[r] + e];
+                       BB_expand(curr, me->mvert[me->medge[edge].v1].co);
+                       BB_expand(curr, me->mvert[me->medge[edge].v2].co);
+               }
+       }
+}
+
+/* We calculate a start and an endpoint of the two node ends intersecting. Now we need to determine the right side on which the intersection happens 
+ * Returning the points in correct order (positive looping results in inner side);
+ */
+static void order_positive_is_inside(Mesh *me, SilhouetteData *sil, MeshElemMap *emap, int *r11, int *r12, int r1_start, int r1_tot, int r2_start, int r2_tot)
+{
+       int dist_1;
+       int center;
+       int comp_v1, comp_v2;
+       int comp_e;
+       int tmp_swap;
+       if (*r11 > *r12) {
+               dist_1 = r1_tot - *r11 + *r12;
+       } else {
+               dist_1 = *r12 - *r11;
+       }
+
+       center = r1_start + (*r11 + (dist_1 / 2)) % r1_tot;
+       comp_v1 = me->medge[sil->fillet_ring_new[center]].v2;
+
+       for (int e = 0; e < emap[comp_v1].count; e++) {
+               comp_e = emap[comp_v1].indices[e];
+               if (comp_e != sil->fillet_ring_new[center] && comp_e != sil->fillet_ring_new[center + 1] && comp_e != sil->fillet_ring_new[center - 1]) {
+                       comp_v2 = me->medge[comp_e].v1 == comp_v1 ? me->medge[comp_e].v2 : me->medge[comp_e].v1;
+                       for (int e_l = 0; e_l < r2_tot; e_l ++) {
+                               if (me->medge[sil->fillet_ring_new[r2_start + e_l]].v2 == comp_v2) {
+                                       return;
+                               }
+                       }
+               }
+       }
+       tmp_swap = *r11;
+       *r11 = *r12;
+       *r12 = tmp_swap;
+       return;
+}
+
+typedef enum MergeRingFlag {
+       ADDED_TO_MERGE = 1,
+} MergeRingFlag;
+
+
+typedef struct MergeRingInfo {
+       int r1;
+       int r2;
+       int r1_start;
+       int r2_start;
+       int r1_tot;
+       int r2_tot;
+       int r1_e_a;
+       int r1_e_b;
+       int r2_e_a;
+       int r2_e_b;
+       int flag;
+} MergeRingInfo;
+
+static void join_node_separated_rings(SilhouetteData *sil, Mesh *me, MeshElemMap *emap)
+{
+       MergeRingInfo *merge_info = NULL;
+       int *merged_to_one = NULL;
+       MEdge e1_c, e2_c;
+       MergeRingInfo t_m_info;
+
+       BLI_array_declare(merge_info);
+
+       printf("Joining rings. In total %i rings to check.\n", sil->num_rings);
+       for (int r1 = 0; r1 < sil->num_rings - 1; r1++) {
+               for (int r2 = r1 + 1; r2 < sil->num_rings; r2++) {
+                       if (bb_intersect(&sil->fillet_ring_bbs[r1], &sil->fillet_ring_bbs[r2])) {
+                               t_m_info.r1_start = sil->fillet_ring_new_start[r1];
+                               t_m_info.r2_start = sil->fillet_ring_new_start[r2];
+                               t_m_info.r1_tot = sil->fillet_ring_new_start[r1 + 1] - t_m_info.r1_start;
+                               t_m_info.r2_tot = r2 + 1 < sil->num_rings ? sil->fillet_ring_new_start[r2 + 1] - t_m_info.r2_start : sil->fillet_ring_tot - t_m_info.r2_start;
+                               t_m_info.r1_e_a = -1, t_m_info.r1_e_b = -1, t_m_info.r2_e_a = -1, t_m_info.r2_e_b = -1;
+                               for (int e1 = 0; e1 < t_m_info.r1_tot; e1++) {
+                                       e1_c = me->medge[sil->fillet_ring_new[t_m_info.r1_start + e1]];
+                                       for (int e2 = 0; e2 < t_m_info.r2_tot; e2++) {
+                                               e2_c = me->medge[sil->fillet_ring_new[t_m_info.r2_start + e2]];
+                                               if (e1_c.v1 == e2_c.v1 || e1_c.v1 == e2_c.v2 || e1_c.v2 == e2_c.v1 || e1_c.v2 == e2_c.v2) {
+                                                       if (t_m_info.r1_e_a == -1) {
+                                                               t_m_info.r1_e_a = e1;
+                                                               t_m_info.r2_e_a = e2;
+                                                       } else {
+                                                               if (abs(t_m_info.r1_e_a - e1) > 3) {
+                                                                       t_m_info.r1_e_b = e1;
+                                                                       t_m_info.r2_e_b = e2;
+                                                                       /* Found start and endpoint of the two ring intersections */
+                                                                       order_positive_is_inside(me, sil, emap, &t_m_info.r1_e_a, &t_m_info.r1_e_b, t_m_info.r1_start, t_m_info.r1_tot, t_m_info.r2_start, t_m_info.r2_tot);
+                                                                       order_positive_is_inside(me, sil, emap, &t_m_info.r2_e_a, &t_m_info.r2_e_b, t_m_info.r2_start, t_m_info.r2_tot, t_m_info.r1_start, t_m_info.r1_tot);
+
+                                                                       BLI_array_append(merge_info, t_m_info);
+#ifdef DEBUG_DRAW
+                                                                       bl_debug_color_set(0xffffff);
+                                                                       bl_debug_draw_point(me->mvert[me->medge[sil->fillet_ring_new[t_m_info.r1_start + t_m_info.r1_e_a]].v1].co, 0.2f);
+                                                                       bl_debug_color_set(0x000000);
+                                                                       bl_debug_draw_point(me->mvert[me->medge[sil->fillet_ring_new[t_m_info.r1_start + t_m_info.r1_e_b]].v1].co, 0.3f);
+                                                                       bl_debug_color_set(0x000000);
+
+                                                                       bl_debug_color_set(0x00ff00);
+                                                                       for (int e_ins = 0; e_ins < t_m_info.r1_tot; e_ins ++) {
+                                                                               if((t_m_info.r1_e_a + e_ins) % t_m_info.r1_tot == t_m_info.r1_e_b) {
+                                                                                       bl_debug_color_set(0x000000);
+                                                                                       break;
+                                                                               }
+                                                                               bl_debug_draw_medge_add(me, sil->fillet_ring_new[t_m_info.r1_start + (t_m_info.r1_e_a + e_ins) % t_m_info.r1_tot]);
+                                                                       }
+                                                                       bl_debug_color_set(0x000000);
+#endif 
+                                                                       /* TODO: Is this a bad coding practise?
+                                                                        * Maybe:
+                                                                        * e1 = r1_tot;
+                                                                        * e2 = r2_tot;
+                                                                        * r2++;
+                                                                        */
+                                                                       goto next_ring;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       /* Continue with the next ring */
+                       next_ring:;
+               }
+       }
+
+       BLI_array_declare(merged_to_one);
+       for (int i = 0; i < BLI_array_count(merge_info); i++) {
+               if (!merge_info[i].flag & ADDED_TO_MERGE) {
+                       BLI_array_append(merged_to_one, merge_info[0].r1);
+                       merge_info[0].flag |= ADDED_TO_MERGE;
+                       for (int j = i; j < BLI_array_count(merge_info); j++) {
+                               for (int k = 0; k < BLI_array_count(merged_to_one); k++) {
+
+                               }
+                       }
+               }
+       }
+       BLI_array_free(merged_to_one);
+       BLI_array_free(merge_info);
+}
+
+static void prep_int_shared_mem(int **mem, int *r_num, int *r_start, int len, const char *str)
+{
+       if (!mem) {
+               *r_start = 0;
+               *mem = MEM_callocN(sizeof(int) * len, str);
+               *r_num = len;
+       } else {
+               *r_start = *r_num;
+               *r_num = *r_num + len;
+               *mem = MEM_reallocN(*mem, sizeof(int) * (*r_num));
+       }
+}
+
 /* Calculate the intersections with existing geometry. Used to connect the new Silhouette to the old mesh
  * Fillets are the smooth transition from one part to another.
  */
-static void do_calc_fillet_line_task_cb_ex(
-                                                                                  void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+static void do_calc_fillet_line_task_cb_ex(void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
 {
        SculptThreadedTaskData *data = userdata;
        SculptSession *ss = data->ob->sculpt;
        SilhouetteData *sil = data->sil;
+       Mesh *me = data->ob->data;
+       PBVHNode *curr_node = data->nodes[n];
 
        PBVHVertexIter vd;
-       float (*proxy)[3];
        float point[2];
+       float sil_plane[4];
+       float fuzz;
+       MEdge e_comp;
+       int v_i, vd_i;
+       GHash *vert_hash = BLI_ghash_int_new("vertices within intersection");
+       GHash *edge_hash = BLI_ghash_int_new("edges on the intersection");
+       int *edge_ring_fillet = NULL;
+       int *ring_start = NULL;
+       int v_rm_start_in_shared_arr = 0, int_e_start_in_shared_arr = 0, fillet_edge_ring_start_shared_arr = 0, fillet_ring_start_start_shared_arr = 0;
+       int r_size;
+       BLI_array_declare(edge_ring_fillet);
+       BLI_array_declare(ring_start);
+       int comp_v, idx;
+
+       /*GHashIterState state;*/
+       GHashIterator gh_iter;
+       int curr_edge = -1, last_edge = -1, start_edge = -1, tmp_curr_edge = -1;
+
+       plane_from_point_normal_v3(sil_plane, sil->anchor, sil->z_vec);
+
+       /* TODO: Is this a unique vert iteration? If not edges might be added twice by different threads */
+       BKE_pbvh_vertex_iter_begin(ss->pbvh, curr_node, vd, PBVH_ITER_UNIQUE)
+       {
+               /* get the interior vertices of the 2d drawn silhouette and all relevant vertices 
+                * Ignores smoothness, assuming the smoothness blures the fillets anyways it should be ok. */
+               fuzz = SIL_FILLET_BLUR_MIN + sil->smoothness * 0.01f * SIL_FILLET_BLUR_MAX;
+               if (dist_squared_to_plane_v3(vd.co, sil_plane) <= sil->depth + fuzz) {
+                       if (!BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(vd.vert_indices[vd.i]))) {
+                               ED_view3d_project_float_v2_m4(sil->ar, vd.co, point, data->mat);
+                               if (isect_point_poly_v2(point, (float(*)[2])sil->current_stroke->points_v2, sil->current_stroke->totvert, false)){
+                                       if (!BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(vd.vert_indices[vd.i]))) {
+                                               BLI_ghash_insert(vert_hash, SET_INT_IN_POINTER(vd.vert_indices[vd.i]), SET_INT_IN_POINTER(vd.vert_indices[vd.i]));
+                                               vd_i = vd.vert_indices[vd.i];
+                                               for (int e = 0; e < sil->emap[vd_i].count; e++){
+                                                       e_comp = me->medge[sil->emap[vd_i].indices[e]];
+                                                       v_i = e_comp.v1 == vd_i ? e_comp.v2 : e_comp.v1;
+                                                       if (!BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(v_i)))
+                                                       {
+                                                               ED_view3d_project_float_v2_m4(sil->ar, me->mvert[v_i].co, point, data->mat);
+                                                               if (!isect_point_poly_v2(point, (float(*)[2])sil->current_stroke->points_v2, sil->current_stroke->totvert, false) || dist_squared_to_plane_v3(me->mvert[v_i].co, sil_plane) > sil->depth + fuzz) {
+                                                                       BLI_ghash_insert(edge_hash, SET_INT_IN_POINTER(sil->emap[vd_i].indices[e]), SET_INT_IN_POINTER(sil->emap[vd_i].indices[e]));
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       BKE_pbvh_vertex_iter_end;
 
-       proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
+       /* Finished writing all vertices which are within the intersection and need to be removed.
+        * write them to the shared array. Lock the mutex to avoid collisions */
+       BLI_mutex_lock(&data->mutex);
+       prep_int_shared_mem(&data->v_to_rm, &data->num_v_to_rm, &v_rm_start_in_shared_arr, BLI_ghash_size(vert_hash), "verts to remove");
+       prep_int_shared_mem(&sil->inter_edges, &sil->num_inter_edges, &int_e_start_in_shared_arr, BLI_ghash_size(edge_hash), "edges on transition");
 
-       BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
-       {
-               /* get the interior vertices of the 2d drawn silhouette and all relevant vertices */
-               ED_view3d_project_float_v2_m4(sil->ar, vd.co, point, data->mat);
-               if (isect_point_poly_v2(point, (float(*)[2])sil->current_stroke->points_v2, sil->current_stroke->totvert, false)){
+       /* Copy vertice data over.*/
+       GHASH_ITER_INDEX (gh_iter, vert_hash, idx) {
+               data->v_to_rm[v_rm_start_in_shared_arr + idx] = BLI_ghashIterator_getKey(&gh_iter);
+       }
+
+       /* Copy edge data over. */
+       GHASH_ITER_INDEX (gh_iter, edge_hash, idx) {
+               sil->inter_edges[int_e_start_in_shared_arr + idx] = BLI_ghashIterator_getKey(&gh_iter);
+       }
+       BLI_mutex_unlock(&data->mutex);
+
+       /* TODO: Workaround, do a BLI_ghash_pop style while loop
+        * TODO: A adjacency search might fail if there is not a single path to be searched, shouldn't be a problem on first thought though.
+        * Breaker is a anti crash method in case the algorithm gets caught in an endless loop. Shouldn't happen!*/
+       int breaker;
+       int debug_test_v;
+       do {
+               breaker = me->totedge;
+               BLI_ghashIterator_init(&gh_iter, edge_hash);
+               if(!BLI_ghashIterator_done(&gh_iter)){
+                       start_edge = BLI_ghashIterator_getValue(&gh_iter);
+                       BLI_ghash_remove(edge_hash, start_edge, NULL, NULL);
+                       comp_v = BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(me->medge[start_edge].v1)) ? me->medge[start_edge].v2 : me->medge[start_edge].v1;
+                       debug_test_v = comp_v;
+                       BLI_assert(!BLI_ghash_haskey(vert_hash, SET_INT_IN_POINTER(comp_v)));
+                       start_edge = get_adjacent_edge(me, sil->emap, start_edge, comp_v, edge_hash, vert_hash);
+                       if(start_edge >= 0) {
+                               BLI_array_append(ring_start, BLI_array_count(edge_ring_fillet));
+                               curr_edge = start_edge;
+                               last_edge = -1;
+
+                               while(!(curr_edge == start_edge && last_edge != -1) && curr_edge != -1 && breaker > 0) {
+                                       BLI_array_append(edge_ring_fillet, curr_edge);
+                                       if(last_edge == -1) {
+                                               comp_v = me->medge[start_edge].v1;
+                                       } else {
+                                               if (me->medge[curr_edge].v1 == me->medge[last_edge].v1 || me->medge[curr_edge].v1 == me->medge[last_edge].v2) {
+                                                       comp_v = me->medge[curr_edge].v2;
+                                               } else {
+                                                       comp_v = me->medge[curr_edge].v1;
+                                               }
+                                       }
+                                       remove_connected_from_edgehash(sil->emap, edge_hash, comp_v);
+                                       tmp_curr_edge = get_adjacent_edge(me, sil->emap, curr_edge, comp_v, edge_hash, vert_hash);
+                                       last_edge = curr_edge;
+                                       curr_edge = tmp_curr_edge;
+                                       breaker --;
+                               }
+                               printf("Found a cut loop!\n");
+                               BLI_assert(breaker > 0);
+                               /* TODO: Bug shouldn't reach but does on some occasion.*/
+                               if (breaker == 0) {
+                                       BLI_array_empty(edge_ring_fillet);
 #ifdef DEBUG_DRAW
-                       bl_debug_color_set(0xff0000);
-                       bl_debug_draw_point(vd.co, 0.2f);
-                       bl_debug_color_set(0x000000);
+
+                                       bl_debug_color_set(0x00ff00);
+                                       bl_debug_draw_point(me->mvert[debug_test_v].co, 0.2f);
+                                       bl_debug_draw_medge_add(me, start_edge);
+                                       bl_debug_color_set(0x000000);
 #endif
+                               }
+                       }
                }
+       } while (!BLI_ghashIterator_done(&gh_iter));
+
+       /* Prep ring memory*/
+       BLI_mutex_lock(&data->mutex);
+       prep_int_shared_mem(&sil->fillet_ring_new, &sil->fillet_ring_tot, &fillet_edge_ring_start_shared_arr, BLI_array_count(edge_ring_fillet), "edges on transition");
+       prep_int_shared_mem(&sil->fillet_ring_new_start, &sil->num_rings, &fillet_ring_start_start_shared_arr, BLI_array_count(ring_start), "start of individual rings");
+
+       /* Copy ring memory */
+       for (int i = 0; i < BLI_array_count(edge_ring_fillet); i++) {
+               sil->fillet_ring_new[fillet_edge_ring_start_shared_arr + i] = edge_ring_fillet[i];
        }
-       BKE_pbvh_vertex_iter_end;
+       /* Offset start pointers to account chunks beforehand */
+       for (int i = 0; i < BLI_array_count(ring_start); i++) {
+               sil->fillet_ring_new_start[fillet_ring_start_start_shared_arr + i] = ring_start[i] + fillet_edge_ring_start_shared_arr;
+       }
+       BLI_mutex_unlock(&data->mutex);
+
+       /*TODO: merge rings from multiple threads / nodes*/
+#ifdef DEBUG_DRAW
+       for(int r = 0; r < BLI_array_count(ring_start); r++) {
+               r_size = r < BLI_array_count(ring_start) - 1 ? ring_start[r + 1] - ring_start[r] : BLI_array_count(edge_ring_fillet) - ring_start[r];
+               for(int i = 0; i < r_size; i++) {
+                       if(i == 0){
+                               bl_debug_color_set(0x00ffff);
+                       } else {
+                               bl_debug_color_set(0xff00ff);
+                       }
+                       bl_debug_draw_medge_add(me, edge_ring_fillet[ring_start[r] + i]);
+                       bl_debug_color_set(0x000000);
+               }
+       }
+#endif
+       BLI_array_free(ring_start);
+       BLI_array_free(edge_ring_fillet);
+       BLI_ghash_free(vert_hash, NULL, NULL);
+       BLI_ghash_free(edge_hash, NULL, NULL);
 }
 
 static void do_calc_fillet_line(Object *ob, SilhouetteData *silhouette, PBVHNode **nodes, int totnode)
 {
+       Mesh *me = ob->data;
        float projmat[4][4];
+       MeshElemMap *emap;
+       int *emap_mem;
+       int *v_remove = NULL;
+       RegionView3D *rv3d;
+       View3D *v3d;
+       /*rctf viewplane;
+       float clipend;
+       float clipnear;*/
+
+       rv3d = silhouette->ar->regiondata;
+       v3d = silhouette->vc.v3d;
+
+       BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge);
+       silhouette->emap = emap;
        /* calc the projection matrix used to convert 3d vertice in 2d space */
+       mul_m4_m4m4(projmat, (float (*)[4])rv3d->persmat, ob->obmat);
+       /* TODO: Orthographic projection:
        ED_view3d_ob_project_mat_get(silhouette->ar->regiondata, ob, projmat);
 
+       float vmat[4][4], winmat[4][4];;
+
+
+       mul_m4_m4m4(vmat, (float (*)[4])rv3d->viewmat, ob->obmat);
+       mul_m4_m4m4(projmat, (float (*)[4])rv3d->persmat, ob->obmat);
+
+       ED_view3d_viewplane_get(v3d, rv3d, silhouette->ar->winx, silhouette->ar->winy, &viewplane, &clipnear, &clipend, NULL);
+       orthographic_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, -clipend, clipend);
+
+       mul_m4_m4m4(vmat, (float (*)[4])rv3d->viewmat, ob->obmat);
+       mul_m4_m4m4(projmat, winmat, vmat);*/
+
        /* threaded loop over nodes */
        SculptThreadedTaskData data = {
                .ob = ob, .nodes = nodes,
-               .sil = silhouette, .mat = projmat
+               .sil = silhouette, .mat = projmat, .v_to_rm = v_remove
        };
 
        BLI_task_parallel_range_ex(
                                                           0, totnode, &data, NULL, 0, do_calc_fillet_line_task_cb_ex,
                                                           (totnode > SCULPT_THREADED_LIMIT), false);
+
+       v_remove = data.v_to_rm;
+
+       calc_ring_bbs(silhouette, me);
+
+#ifdef DEBUG_DRAW
+       for (int r = 0; r < silhouette->num_rings; r ++) {
+               bl_debug_draw_BB_add(&silhouette->fillet_ring_bbs[r], 0x0000ff);
+       }
+#endif
+
+       /*TODO: Join multiple parts together when totnode > 1.*/
+       join_node_separated_rings(silhouette, me, emap);
+
+       if (v_remove) {
+               printf("Removing vertices/edges/loops/polys from mesh.\n");
+               remove_verts_from_mesh(me, v_remove, data.num_v_to_rm);
+               MEM_freeN(v_remove);
+       }
+
+       MEM_freeN(emap);
+       MEM_freeN(emap_mem);
 }
 
 static void sculpt_silhouette_calc_mesh(bContext *C, wmOperator *op)
@@ -7108,6 +7879,13 @@ static void sculpt_silhouette_calc_mesh(bContext *C, wmOperator *op)
 
        /* Rebuild mesh caches
         * TODO: Proper PBVH etc. */
+
+       BKE_mesh_calc_edges(me, false, true);
+       BKE_mesh_tessface_clear(me);
+       BKE_mesh_calc_normals(me);
+       DAG_id_tag_update(&me->id, 0);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
+
        BKE_object_free_derived_caches(ob);
 }
 
@@ -7151,8 +7929,9 @@ static int sculpt_silhouette_exec(bContext *C, wmOperator *op)
                op->customdata = sil;
        }
 
+       /*TODO: Add undo for fillets etc*/
        if (sil->current_stroke->totvert > 3) {
-               sculpt_undo_push_begin("draw Silhouette");
+               /*sculpt_undo_push_begin("draw Silhouette");*/
                v_start = me->totvert;
                e_start = me->totedge;
                l_start = me->totloop;
@@ -7160,8 +7939,8 @@ static int sculpt_silhouette_exec(bContext *C, wmOperator *op)
                sculpt_silhouette_calc_mesh(C, op);
                sculpt_silhouette_stroke_done(C, op);
 
-               sculpt_undo_silhouette_push(ob, v_start, e_start, l_start, p_start);
-               sculpt_undo_push_end(C);
+               /*sculpt_undo_silhouette_push(ob, v_start, e_start, l_start, p_start);
+               sculpt_undo_push_end(C);*/
        }
 
        return OPERATOR_FINISHED;