diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 2f0d69bfd98..12843fde852 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -65,6 +65,7 @@ #include "BKE_report.h" #include "BKE_lattice.h" /* for armature_deform_verts */ #include "BKE_node.h" +#include "BKE_object.h" #include "BKE_subsurf.h" #include "BIF_glutil.h" @@ -86,6 +87,8 @@ #include "GPU_buffers.h" +#include "bmesh.h" + #include #include #include @@ -98,8 +101,13 @@ void ED_sculpt_force_update(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob && (ob->mode & OB_MODE_SCULPT)) + if (ob && (ob->mode & OB_MODE_SCULPT)) { multires_force_update(ob); + + /* Set reorder=false so that saving the file doesn't reorder + * the BMesh's elements */ + sculptsession_bm_to_me(ob, FALSE); + } } float *ED_sculpt_get_last_stroke(struct Object *ob) @@ -172,7 +180,8 @@ static int sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) Mesh *me = (Mesh *)ob->data; MultiresModifierData *mmd = sculpt_multires_active(scene, ob); - if (mmd) return 0; + if (mmd || ob->sculpt->bm) + return 0; /* non-locked shape keys could be handled in the same way as deformed mesh */ if ((ob->shapeflag & OB_SHAPE_LOCK) == 0 && me->key && ob->shapenr) @@ -281,12 +290,130 @@ typedef struct StrokeCache { rcti previous_r; /* previous redraw rectangle */ } StrokeCache; +/************** Access to original unmodified vertex data *************/ + +typedef struct { + BMLog *bm_log; + + SculptUndoNode *unode; + float (*coords)[3]; + short (*normals)[3]; + float *vmasks; + + /* Original coordinate, normal, and mask */ + const float *co; + float mask; + short no[3]; +} SculptOrigVertData; + + +/* Initialize a SculptOrigVertData for accessing original vertex data; + * handles BMesh, mesh, and multires */ +static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data, + Object *ob, + SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; + + memset(data, 0, sizeof(*data)); + data->unode = unode; + + if (bm) { + data->bm_log = ss->bm_log; + } + else { + data->coords = data->unode->co; + data->normals = data->unode->no; + data->vmasks = data->unode->mask; + } +} + +/* Initialize a SculptOrigVertData for accessing original vertex data; + * handles BMesh, mesh, and multires */ +static void sculpt_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node) +{ + SculptUndoNode *unode; + unode = sculpt_undo_push_node(ob, node, SCULPT_UNDO_COORDS); + sculpt_orig_vert_data_unode_init(data, ob, unode); + +} + +/* Update a SculptOrigVertData for a particular vertex from the PBVH + * iterator */ +static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, + PBVHVertexIter *iter) +{ + if (orig_data->unode->type == SCULPT_UNDO_COORDS) { + if (orig_data->coords) { + orig_data->co = orig_data->coords[iter->i]; + } + else { + orig_data->co = BM_log_original_vert_co(orig_data->bm_log, iter->bm_vert); + } + + if (orig_data->normals) { + copy_v3_v3_short(orig_data->no, orig_data->normals[iter->i]); + } + else { + /* TODO: log doesn't store normals yet */ + normal_float_to_short_v3(orig_data->no, iter->bm_vert->no); + } + } + else if (orig_data->unode->type == SCULPT_UNDO_MASK) { + if (orig_data->vmasks) { + orig_data->mask = orig_data->vmasks[iter->i]; + } + else { + orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); + } + } +} + +/**********************************************************************/ + +/* Returns true if the stroke will use dynamic topology, false + otherwise. + + Factors: some brushes like grab cannot do dynamic topology. + Others, like smooth, are better without. Same goes for alt- + key smoothing. */ +static int sculpt_stroke_dynamic_topology(const SculptSession *ss, + const Brush *brush) +{ + return ((BLI_pbvh_type(ss->pbvh) == PBVH_BMESH) && + + (!ss->cache || (!ss->cache->alt_smooth)) && + + /* Requires mesh restore, which doesn't work with + * dynamic-topology */ + !(brush->flag & BRUSH_ANCHORED) && + !(brush->flag & BRUSH_RESTORE_MESH) && + + (!ELEM6(brush->sculpt_tool, + /* These brushes, as currently coded, cannot + * support dynamic topology */ + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER, + + /* These brushes could handle dynamic topology, + * but user feedback indicates it's better not + * to */ + SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_MASK))); +} /*** paint mesh ***/ -static void paint_mesh_restore_co(Sculpt *sd, SculptSession *ss) +static void paint_mesh_restore_co(Sculpt *sd, Object *ob) { + SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; + const Brush *brush = paint_brush(&sd->paint); int i; PBVHNode **nodes; @@ -301,20 +428,27 @@ static void paint_mesh_restore_co(Sculpt *sd, SculptSession *ss) #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for (n = 0; n < totnode; n++) { SculptUndoNode *unode; - - unode = sculpt_undo_get_node(nodes[n]); + SculptUndoType type = (brush->sculpt_tool == SCULPT_TOOL_MASK ? + SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); + + unode = sculpt_undo_push_node(ob, nodes[n], type); if (unode) { PBVHVertexIter vd; + SculptOrigVertData orig_data; + sculpt_orig_vert_data_unode_init(&orig_data, ob, unode); + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (unode->type == SCULPT_UNDO_COORDS) { - copy_v3_v3(vd.co, unode->co[vd.i]); - if (vd.no) copy_v3_v3_short(vd.no, unode->no[vd.i]); - else normal_short_to_float_v3(vd.fno, unode->no[vd.i]); + sculpt_orig_vert_data_update(&orig_data, &vd); + + if (orig_data.unode->type == SCULPT_UNDO_COORDS) { + copy_v3_v3(vd.co, orig_data.co); + if (vd.no) copy_v3_v3_short(vd.no, orig_data.no); + else normal_short_to_float_v3(vd.fno, orig_data.no); } - else if (unode->type == SCULPT_UNDO_MASK) { - *vd.mask = unode->mask[vd.i]; + else if (orig_data.unode->type == SCULPT_UNDO_MASK) { + *vd.mask = orig_data.mask; } if (vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } @@ -405,7 +539,7 @@ static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) test->dist = 0.0f; /* just for initialize */ } -static int sculpt_brush_test(SculptBrushTest *test, float co[3]) +static int sculpt_brush_test(SculptBrushTest *test, const float co[3]) { float distsq = len_squared_v3v3(co, test->location); @@ -418,7 +552,7 @@ static int sculpt_brush_test(SculptBrushTest *test, float co[3]) } } -static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3]) +static int sculpt_brush_test_sq(SculptBrushTest *test, const float co[3]) { float distsq = len_squared_v3v3(co, test->location); @@ -734,7 +868,8 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) } /* Return a multiplier for brush strength on a particular vertex. */ -static float tex_strength(SculptSession *ss, Brush *br, float point[3], +static float tex_strength(SculptSession *ss, Brush *br, + const float point[3], const float len, const float sculpt_normal[3], const short vno[3], @@ -926,6 +1061,11 @@ static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nod original = (paint_brush(&sd->paint)->sculpt_tool == SCULPT_TOOL_GRAB ? TRUE : ss->cache->original); + /* In general the original coords are not available with dynamic + * topology */ + if (ss->bm) + original = FALSE; + (void)sd; /* unused w/o openmp */ zero_v3(an); @@ -1210,6 +1350,81 @@ static float neighbor_average_mask(SculptSession *ss, unsigned vert) return vmask[vert]; } +/* Same logic as neighbor_average(), but for bmesh rather than mesh */ +static void bmesh_neighbor_average(float avg[3], BMVert *v) +{ + int vfcount = BM_vert_face_count(v); + + zero_v3(avg); + + /* Don't modify corner vertices */ + if (vfcount > 1) { + BMIter iter; + BMFace *f; + int i, total = 0; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + BMLoop *l1 = BM_face_vert_share_loop(f, v); + BMLoop *l2 = BM_loop_other_vert_loop(l1, v); + BMVert *adj_v[3] = { + BM_edge_other_vert(l1->e, v), + v, + BM_edge_other_vert(l2->e, v)}; + + for (i = 0; i < 3; i++) { + if (vfcount != 2 || BM_vert_face_count(adj_v[i]) <= 2) { + add_v3_v3(avg, adj_v[i]->co); + total++; + } + } + } + + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + return; + } + } + + copy_v3_v3(avg, v->co); +} + +/* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ +static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMFace *f; + float avg = 0; + int i, total = 0; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + BMLoop *l1 = BM_face_vert_share_loop(f, v); + BMLoop *l2 = BM_loop_other_vert_loop(l1, v); + BMVert *adj_v[3] = { + BM_edge_other_vert(l1->e, v), + v, + BM_edge_other_vert(l2->e, v)}; + + for (i = 0; i < 3; i++) { + BMVert *v2 = adj_v[i]; + float *vmask = CustomData_bmesh_get(&bm->vdata, + v2->head.data, + CD_PAINT_MASK); + avg += (*vmask); + total++; + } + } + + if (total > 0) { + return avg / (float)total; + } + else { + float *vmask = CustomData_bmesh_get(&bm->vdata, + v->head.data, + CD_PAINT_MASK); + return (*vmask); + } +} + static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength, int smooth_mask) { Brush *brush = paint_brush(&sd->paint); @@ -1251,6 +1466,47 @@ static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, BLI_pbvh_vertex_iter_end; } +static void do_bmesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength, int smooth_mask) +{ + Brush *brush = paint_brush(&sd->paint); + PBVHVertexIter vd; + SculptBrushTest test; + + CLAMP(bstrength, 0.0f, 1.0f); + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, + ss->cache->view_normal, vd.no, vd.fno, + smooth_mask ? 0 : *vd.mask); + if (smooth_mask) { + float val = bmesh_neighbor_average_mask(ss->bm, vd.bm_vert) - *vd.mask; + val *= fade * bstrength; + *vd.mask += val; + CLAMP(*vd.mask, 0, 1); + } + else { + float avg[3], val[3]; + + bmesh_neighbor_average(avg, vd.bm_vert); + sub_v3_v3v3(val, avg, vd.co); + mul_v3_fl(val, fade); + + add_v3_v3(val, vd.co); + + sculpt_clip(sd, ss, vd.co, val); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; +} + static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength, int smooth_mask) { @@ -1433,6 +1689,9 @@ static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, do_mesh_smooth_brush(sd, ss, nodes[n], strength, smooth_mask); break; + case PBVH_BMESH: + do_bmesh_smooth_brush(sd, ss, nodes[n], strength, smooth_mask); + break; } } @@ -1659,15 +1918,11 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptUndoNode *unode; SculptBrushTest test; - float (*origco)[3]; - short (*origno)[3]; + SculptOrigVertData orig_data; float (*proxy)[3]; - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - origco = unode->co; - origno = unode->no; + sculpt_orig_vert_data_init(&orig_data, ob, nodes[n]); proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1675,10 +1930,15 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength * tex_strength(ss, brush, origco[vd.i], test.dist, - ss->cache->sculpt_normal_symm, origno[vd.i], - NULL, vd.mask ? *vd.mask : 0.0f); + sculpt_orig_vert_data_update(&orig_data, &vd); + + if (sculpt_brush_test(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, brush, + orig_data.co, + test.dist, + ss->cache->sculpt_normal_symm, + orig_data.no, + NULL, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1797,15 +2057,11 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptUndoNode *unode; SculptBrushTest test; - float (*origco)[3]; - short (*origno)[3]; + SculptOrigVertData orig_data; float (*proxy)[3]; - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - origco = unode->co; - origno = unode->no; + sculpt_orig_vert_data_init(&orig_data, ob, nodes[n]); proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1813,10 +2069,15 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength * tex_strength(ss, brush, origco[vd.i], test.dist, + sculpt_orig_vert_data_update(&orig_data, &vd); + + if (sculpt_brush_test(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, brush, + orig_data.co, + test.dist, ss->cache->sculpt_normal_symm, - origno[vd.i], NULL, vd.mask ? *vd.mask : 0.0f); + orig_data.no, + NULL, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1850,15 +2111,11 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptUndoNode *unode; SculptBrushTest test; - float (*origco)[3]; - short (*origno)[3]; + SculptOrigVertData orig_data; float (*proxy)[3]; - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - origco = unode->co; - origno = unode->no; + sculpt_orig_vert_data_init(&orig_data, ob, nodes[n]); proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1866,13 +2123,18 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength * tex_strength(ss, brush, origco[vd.i], test.dist, - ss->cache->sculpt_normal_symm, - origno[vd.i], NULL, vd.mask ? *vd.mask : 0.0f); + sculpt_orig_vert_data_update(&orig_data, &vd); - mul_v3_m4v3(proxy[vd.i], m, origco[vd.i]); - sub_v3_v3(proxy[vd.i], origco[vd.i]); + if (sculpt_brush_test(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, brush, + orig_data.co, + test.dist, + ss->cache->sculpt_normal_symm, + orig_data.no, + NULL, vd.mask ? *vd.mask : 0.0f); + + mul_v3_m4v3(proxy[vd.i], m, orig_data.co); + sub_v3_v3(proxy[vd.i], orig_data.co); mul_v3_fl(proxy[vd.i], fade); if (vd.mvert) @@ -1901,13 +2163,12 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; - SculptUndoNode *unode; - float (*origco)[3], *layer_disp; + SculptOrigVertData orig_data; + float *layer_disp; /* XXX: layer brush needs conversion to proxy but its more complicated */ /* proxy = BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */ - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - origco = unode->co; + sculpt_orig_vert_data_init(&orig_data, ob, nodes[n]); #pragma omp critical { @@ -1918,7 +2179,9 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test(&test, origco[vd.i])) { + sculpt_orig_vert_data_update(&orig_data, &vd); + + if (sculpt_brush_test(&test, orig_data.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, ss->cache->sculpt_normal_symm, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); @@ -1940,7 +2203,7 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode add_v3_v3(val, ss->layer_co[index]); } else { - add_v3_v3(val, origco[vd.i]); + add_v3_v3(val, orig_data.co); } sculpt_clip(sd, ss, vd.co, val); @@ -2081,7 +2344,7 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); sculpt_brush_test_init(ss, &test); - if (ss->cache->original) { + if (ss->cache->original && unode->co) { BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { @@ -2687,6 +2950,63 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) BKE_key_convert_from_vertcos(ob, kb, vertCos); } +/* Note: we do the topology update before any brush actions to avoid + * issues with the proxies. The size of the proxy can't change, so + * topology must be updated first. */ +static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush) +{ + SculptSession *ss = ob->sculpt; + SculptSearchSphereData data; + PBVHNode **nodes = NULL; + float radius; + int n, totnode; + + /* Build a list of all nodes that are potentially within the + * brush's area of influence */ + data.ss = ss; + data.sd = sd; + + radius = ss->cache->radius * 1.25; + + data.radius_squared = radius * radius; + data.original = ELEM4(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER); + + BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); + + /* Only act if some verts are inside the brush area */ + if (totnode) { + PBVHTopologyUpdateMode mode = PBVH_Subdivide; + + if (sd->flags & SCULPT_DYNTOPO_COLLAPSE) { + mode |= PBVH_Collapse; + } + + for (n = 0; n < totnode; n++) { + sculpt_undo_push_node(ob, nodes[n], + brush->sculpt_tool == SCULPT_TOOL_MASK ? + SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); + BLI_pbvh_node_mark_update(nodes[n]); + + if (BLI_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BLI_pbvh_node_mark_topology_update(nodes[n]); + BLI_pbvh_bmesh_node_save_orig(nodes[n]); + } + } + + if (BLI_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BLI_pbvh_bmesh_update_topology(ss->pbvh, mode, + ss->cache->location, + ss->cache->radius); + } + + MEM_freeN(nodes); + } +} + static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) { SculptSession *ss = ob->sculpt; @@ -2834,7 +3154,7 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) int proxy_count; float (*orco)[3]; - if (use_orco) + if (use_orco && !ss->bm) orco = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS)->co; BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); @@ -2844,8 +3164,15 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) float val[3]; int p; - if (use_orco) - copy_v3_v3(val, orco[vd.i]); + if (use_orco) { + if (ss->bm) { + copy_v3_v3(val, + BM_log_original_vert_co(ss->bm_log, + vd.bm_vert)); + } + else + copy_v3_v3(val, orco[vd.i]); + } else copy_v3_v3(val, vd.co); @@ -2977,7 +3304,10 @@ static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); } +typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush); + static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, + BrushActionFunc action, const char symm, const int axis, const float feather) { @@ -2988,7 +3318,7 @@ static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; ss->cache->radial_symmetry_pass = i; calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather); - do_brush_action(sd, ob, brush); + action(sd, ob, brush); } } @@ -3005,7 +3335,8 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) multires_stitch_grids(ob); } -static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob) +static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob, + BrushActionFunc action) { Brush *brush = paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; @@ -3016,7 +3347,6 @@ static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob) float feather = calc_symmetry_feather(sd, ss->cache); cache->bstrength = brush_strength(sd, cache, feather); - cache->symmetry = symm; /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ @@ -3026,23 +3356,13 @@ static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob) cache->radial_symmetry_pass = 0; calc_brushdata_symm(sd, cache, i, 0, 0, feather); - do_brush_action(sd, ob, brush); + action(sd, ob, brush); - do_radial_symmetry(sd, ob, brush, i, 'X', feather); - do_radial_symmetry(sd, ob, brush, i, 'Y', feather); - do_radial_symmetry(sd, ob, brush, i, 'Z', feather); + do_radial_symmetry(sd, ob, brush, action, i, 'X', feather); + do_radial_symmetry(sd, ob, brush, action, i, 'Y', feather); + do_radial_symmetry(sd, ob, brush, action, i, 'Z', feather); } } - - sculpt_combine_proxies(sd, ob); - - /* hack to fix noise texture tearing mesh */ - sculpt_fix_noise_tear(sd, ob); - - if (ss->modifiers_active) - sculpt_flush_stroke_deform(sd, ob); - - cache->first_time = 0; } static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) @@ -3405,8 +3725,9 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio ED_view3d_global_to_vector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal); /* Initialize layer brush displacements and persistent coords */ if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - /* not supported yet for multires */ - if (!ss->multires && !ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + /* not supported yet for multires or dynamic topology */ + if (!ss->multires && !ss->bm && !ss->layer_co && + (brush->flag & BRUSH_PERSISTENT)) { if (!ss->layer_co) ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); @@ -3723,14 +4044,23 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) if (BLI_pbvh_node_get_tmin(node) < *tmin) { SculptRaycastData *srd = data_v; float (*origco)[3] = NULL; + int use_origco = FALSE; if (srd->original && srd->ss->cache) { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode = sculpt_undo_get_node(node); - origco = (unode) ? unode->co : NULL; + if (BLI_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { + use_origco = TRUE; + } + else { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode = sculpt_undo_get_node(node); + origco = (unode) ? unode->co : NULL; + use_origco = origco ? TRUE : FALSE; + } } - if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) { + if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, use_origco, + srd->ray_start, srd->ray_normal, &srd->dist)) + { srd->hit = 1; *tmin = srd->dist; } @@ -3828,8 +4158,9 @@ static int sculpt_brush_stroke_init(bContext *C, wmOperator *op) return 1; } -static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) +static void sculpt_restore_mesh(Sculpt *sd, Object *ob) { + SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); /* Restore the mesh before continuing with anchored stroke */ @@ -3838,7 +4169,7 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || (brush->flag & BRUSH_RESTORE_MESH)) { - paint_mesh_restore_co(sd, ss); + paint_mesh_restore_co(sd, ob); } } @@ -3916,11 +4247,32 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; + const Brush *brush = paint_brush(&sd->paint); sculpt_stroke_modifiers_check(C, ob); sculpt_update_cache_variants(C, sd, ob, stroke, itemptr); - sculpt_restore_mesh(sd, ss); - do_symmetrical_brush_actions(sd, ob); + sculpt_restore_mesh(sd, ob); + + BLI_pbvh_bmesh_detail_size_set(ss->pbvh, + (ss->cache->radius / + (float)ss->cache->pixel_radius) * + (float)sd->detail_size); + + if (sculpt_stroke_dynamic_topology(ss, brush)) { + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update); + } + + do_symmetrical_brush_actions(sd, ob, do_brush_action); + + sculpt_combine_proxies(sd, ob); + + /* hack to fix noise texture tearing mesh */ + sculpt_fix_noise_tear(sd, ob); + + if (ss->modifiers_active) + sculpt_flush_stroke_deform(sd, ob); + + ss->cache->first_time = FALSE; /* Cleanup */ sculpt_flush_update(C); @@ -3980,6 +4332,9 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str sculpt_undo_push_end(); BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); + + if (BLI_pbvh_type(ss->pbvh) == PBVH_BMESH) + BLI_pbvh_bmesh_after_stroke(ss->pbvh); /* optimization: if there is locked key and active modifiers present in */ /* the stack, keyblock is updating at each step. otherwise we could update */ @@ -4054,7 +4409,7 @@ static int sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) Sculpt *sd = CTX_data_tool_settings(C)->sculpt; if (ss->cache) { - paint_mesh_restore_co(sd, ss); + paint_mesh_restore_co(sd, ob); } paint_stroke_cancel(C, op); @@ -4136,6 +4491,222 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/************************** Dynamic Topology **************************/ + +static void sculpt_dynamic_topology_triangulate(BMesh *bm) +{ + BMO_op_callf(bm, BMO_FLAG_DEFAULTS, "triangulate faces=%af"); +} + +void sculpt_pbvh_clear(Object *ob) +{ + SculptSession *ss = ob->sculpt; + DerivedMesh *dm = ob->derivedFinal; + + /* Clear out any existing DM and PBVH */ + if (ss->pbvh) + BLI_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + if (dm) + dm->getPBVH(NULL, dm); + BKE_object_free_display(ob); +} + +void sculpt_update_after_dynamic_topology_toggle(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Sculpt *sd = scene->toolsettings->sculpt; + + /* Create the PBVH */ + sculpt_update_mesh_elements(scene, sd, ob, FALSE, FALSE); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +} + +void sculpt_dynamic_topology_enable(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & + SCULPT_DYNTOPO_SMOOTH_SHADING); + + /* Create triangles-only BMesh */ + ss->bm = BM_mesh_create(&bm_mesh_allocsize_default); + + BM_mesh_bm_from_me(ss->bm, me, TRUE, ob->shapenr); + sculpt_dynamic_topology_triangulate(ss->bm); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + BM_mesh_normals_update(ss->bm, TRUE); + + /* Enable dynamic topology */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* Enable logging for undo/redo */ + ss->bm_log = BM_log_create(ss->bm); + + /* Refresh */ + sculpt_update_after_dynamic_topology_toggle(C); +} + +/* Free the sculpt BMesh and BMLog + * + * If 'unode' is given, the BMesh's data is copied out to the unode + * before the BMesh is deleted so that it can be restored from */ +void sculpt_dynamic_topology_disable(bContext *C, + SculptUndoNode *unode) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + if (unode) { + /* Free all existing custom data */ + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + CustomData_free(&me->ldata, me->totloop); + CustomData_free(&me->pdata, me->totpoly); + + /* Copy over stored custom data */ + me->totvert = unode->bm_enter_totvert; + me->totloop = unode->bm_enter_totloop; + me->totpoly = unode->bm_enter_totpoly; + me->totedge = unode->bm_enter_totedge; + me->totface = 0; + CustomData_copy(&unode->bm_enter_vdata, &me->vdata, CD_MASK_MESH, + CD_DUPLICATE, unode->bm_enter_totvert); + CustomData_copy(&unode->bm_enter_edata, &me->edata, CD_MASK_MESH, + CD_DUPLICATE, unode->bm_enter_totedge); + CustomData_copy(&unode->bm_enter_ldata, &me->ldata, CD_MASK_MESH, + CD_DUPLICATE, unode->bm_enter_totloop); + CustomData_copy(&unode->bm_enter_pdata, &me->pdata, CD_MASK_MESH, + CD_DUPLICATE, unode->bm_enter_totpoly); + + mesh_update_customdata_pointers(me, FALSE); + } else { + sculptsession_bm_to_me(ob, TRUE); + } + + BM_mesh_free(ss->bm); + + /* Clear data */ + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + ss->bm = NULL; + BM_log_free(ss->bm_log); + ss->bm_log = NULL; + + /* Refresh */ + sculpt_update_after_dynamic_topology_toggle(C); +} + +static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + if (ss->bm) { + sculpt_undo_push_begin("Dynamic topology disable"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END); + sculpt_dynamic_topology_disable(C, NULL); + } + else { + sculpt_undo_push_begin("Dynamic topology enable"); + sculpt_dynamic_topology_enable(C); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + } + sculpt_undo_push_end(); + + return OPERATOR_FINISHED; +} + +static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, + wmEvent *UNUSED(event)) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; + const char *msg = "Dynamic-topology sculpting will not preserve" + "vertex colors, UVs, or other customdata"; + + if (!ss->bm) { + int i; + + for (i = 0; i < CD_NUMTYPES; i++) { + if (!ELEM7(i, CD_MVERT, CD_MEDGE, CD_MFACE, + CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, + CD_ORIGINDEX) && + (CustomData_has_layer(&me->vdata, i) || + CustomData_has_layer(&me->edata, i) || + CustomData_has_layer(&me->fdata, i))) { + /* The mesh has customdata that will be lost, let the + * user confirm this is OK */ + return WM_operator_confirm_message(C, op, msg); + } + } + } + + return sculpt_dynamic_topology_toggle_exec(C, op); +} + +static void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dynamic Topology Toggle"; + ot->idname = "SCULPT_OT_dynamic_topology_toggle"; + ot->description = "Dynamic topology alters the mesh topology while sculpting"; + + /* api callbacks */ + ot->invoke = sculpt_dynamic_topology_toggle_invoke; + ot->exec = sculpt_dynamic_topology_toggle_exec; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/************************* SCULPT_OT_optimize *************************/ + +static int sculpt_optimize_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + sculpt_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +static int sculpt_and_dynamic_topology_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + return sculpt_mode_poll(C) && ob->sculpt->bm; +} + +/* The BVH gets less optimal more quickly with dynamic topology than + * regular sculpting. There is no doubt more clever stuff we can do to + * optimize it on the fly, but for now this gives the user a nicer way + * to recalculate it than toggling modes. */ +static void SCULPT_OT_optimize(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Optimize"; + ot->idname = "SCULPT_OT_optimize"; + ot->description = "Recalculate the sculpt BVH to improve performance"; + + /* api callbacks */ + ot->exec = sculpt_optimize_exec; + ot->poll = sculpt_and_dynamic_topology_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /**** Toggle operator for turning sculpt mode on or off ****/ static void sculpt_init_session(Scene *scene, Object *ob) @@ -4221,6 +4792,7 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; MultiresModifierData *mmd = sculpt_multires_active(scene, ob); int flush_recalc = 0; @@ -4233,9 +4805,16 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *UNUSED(op)) if (mmd) multires_force_update(ob); - if (flush_recalc) + if (flush_recalc || (ob->sculpt && ob->sculpt->bm)) DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + /* Dynamic topology must be disabled before exiting sculpt + * mode to ensure the undo stack stays in a consistent + * state */ + sculpt_dynamic_topology_toggle_exec(C, NULL); + } + /* Leave sculptmode */ ob->mode &= ~OB_MODE_SCULPT; @@ -4256,6 +4835,9 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *UNUSED(op)) ts->sculpt->flags |= SCULPT_SYMM_X; } + if (!ts->sculpt->detail_size) + ts->sculpt->detail_size = 30; + /* Create sculpt mode session data */ if (ob->sculpt) free_sculptsession(ob); @@ -4270,7 +4852,7 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *UNUSED(op)) } BKE_paint_init(&ts->sculpt->paint, PAINT_CURSOR_SCULPT); - + paint_cursor_start(C, sculpt_poll); } @@ -4298,4 +4880,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_brush_stroke); WM_operatortype_append(SCULPT_OT_sculptmode_toggle); WM_operatortype_append(SCULPT_OT_set_persistent_base); + WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); + WM_operatortype_append(SCULPT_OT_optimize); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index cfc908bf453..e56962a3964 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -49,6 +49,7 @@ struct Object; struct Scene; struct Sculpt; struct SculptStroke; +struct SculptUndoNode; /* Interface */ struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob); @@ -67,12 +68,22 @@ void free_sculptsession_deformMats(struct SculptSession *ss); /* Stroke */ int sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]); +/* Dynamic topology */ +void sculpt_pbvh_clear(Object *ob); +void sculpt_update_after_dynamic_topology_toggle(bContext *C); +void sculpt_dynamic_topology_enable(struct bContext *C); +void sculpt_dynamic_topology_disable(struct bContext *C, + struct SculptUndoNode *unode); + /* Undo */ typedef enum { SCULPT_UNDO_COORDS, SCULPT_UNDO_HIDDEN, - SCULPT_UNDO_MASK + SCULPT_UNDO_MASK, + SCULPT_UNDO_DYNTOPO_BEGIN, + SCULPT_UNDO_DYNTOPO_END, + SCULPT_UNDO_DYNTOPO_SYMMETRIZE, } SculptUndoType; typedef struct SculptUndoNode { @@ -101,6 +112,18 @@ typedef struct SculptUndoNode { int *grids; /* to restore into right location */ BLI_bitmap *grid_hidden; + /* bmesh */ + struct BMLogEntry *bm_entry; + int applied; + CustomData bm_enter_vdata; + CustomData bm_enter_edata; + CustomData bm_enter_ldata; + CustomData bm_enter_pdata; + int bm_enter_totvert; + int bm_enter_totedge; + int bm_enter_totloop; + int bm_enter_totpoly; + /* shape keys */ char shapeName[sizeof(((KeyBlock *)0))->name]; } SculptUndoNode; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 053e17cde07..08e61a6d05d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -65,6 +65,7 @@ #include "GPU_buffers.h" #include "ED_sculpt.h" +#include "bmesh.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -261,6 +262,111 @@ static int sculpt_undo_restore_mask(bContext *C, DerivedMesh *dm, SculptUndoNode return 1; } +static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + if (unode->applied) { + BM_log_undo(ss->bm, ss->bm_log); + unode->applied = FALSE; + } + else { + BM_log_redo(ss->bm, ss->bm_log); + unode->applied = TRUE; + } + + /* A bit lame, but for now just recreate the PBVH. The alternative + * is to store changes to the PBVH in the undo stack. */ + sculpt_pbvh_clear(ob); +} + +/* Create empty sculpt BMesh and enable logging */ +static void sculpt_undo_bmesh_enable(Object *ob, + SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + /* Create empty BMesh and enable logging */ + ss->bm = BM_mesh_create(&bm_mesh_allocsize_default); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* Restore the BMLog using saved entries */ + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, + unode->bm_entry); +} + +static void sculpt_undo_bmesh_restore_begin(bContext *C, + SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + if (unode->applied) { + sculpt_dynamic_topology_disable(C, unode); + unode->applied = FALSE; + } + else { + sculpt_undo_bmesh_enable(ob, unode); + + /* Restore the mesh from the first log entry */ + BM_log_redo(ss->bm, ss->bm_log); + + unode->applied = TRUE; + } +} + +static void sculpt_undo_bmesh_restore_end(bContext *C, + SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + if (unode->applied) { + sculpt_undo_bmesh_enable(ob, unode); + + /* Restore the mesh from the last log entry */ + BM_log_undo(ss->bm, ss->bm_log); + + unode->applied = FALSE; + } + else { + /* Disable dynamic topology sculpting */ + sculpt_dynamic_topology_disable(C, NULL); + unode->applied = TRUE; + } +} + +/* Handle all dynamic-topology updates + * + * Returns TRUE if this was a dynamic-topology undo step, otherwise + * returns FALSE to indicate the non-dyntopo code should run. */ +static int sculpt_undo_bmesh_restore(bContext *C, + SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + switch (unode->type) { + case SCULPT_UNDO_DYNTOPO_BEGIN: + sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); + return TRUE; + + case SCULPT_UNDO_DYNTOPO_END: + sculpt_undo_bmesh_restore_end(C, unode, ob, ss); + return TRUE; + + default: + if (ss->bm_log) { + sculpt_undo_bmesh_restore_generic(unode, ob, ss); + return TRUE; + } + break; + } + + return FALSE; +} + static void sculpt_undo_restore(bContext *C, ListBase *lb) { Scene *scene = CTX_data_scene(C); @@ -289,6 +395,9 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) /* call _after_ sculpt_update_mesh_elements() which may update 'ob->derivedFinal' */ dm = mesh_get_derived_final(scene, ob, 0); + if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) + return; + for (unode = lb->first; unode; unode = unode->next) { if (!(strcmp(unode->idname, ob->id.name) == 0)) continue; @@ -306,9 +415,6 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) continue; } } - else { - continue; - } switch (unode->type) { case SCULPT_UNDO_COORDS: @@ -323,6 +429,12 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) if (sculpt_undo_restore_mask(C, dm, unode)) update = TRUE; break; + + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; } } @@ -387,6 +499,17 @@ static void sculpt_undo_free(ListBase *lb) } if (unode->mask) MEM_freeN(unode->mask); + if (unode->bm_entry) { + BM_log_entry_drop(unode->bm_entry); + } + if (unode->bm_enter_totvert) + CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); + if (unode->bm_enter_totedge) + CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge); + if (unode->bm_enter_totloop) + CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop); + if (unode->bm_enter_totpoly) + CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly); } } @@ -437,11 +560,13 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, unode->type = type; unode->node = node; - BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); - BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); + if (node) { + BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); + BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, + &maxgrid, &gridsize, NULL, NULL); - unode->totvert = totvert; + unode->totvert = totvert; + } /* we will use this while sculpting, is mapalloc slow to access then? */ @@ -466,6 +591,11 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask"); undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float) * sizeof(int)) * allvert); break; + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; } BLI_addtail(lb, unode); @@ -540,6 +670,80 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) BLI_pbvh_vertex_iter_end; } +static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, + PBVHNode *node, + SculptUndoType type) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); + SculptUndoNode *unode = lb->first; + SculptSession *ss = ob->sculpt; + PBVHVertexIter vd; + + if (!lb->first) { + unode = MEM_callocN(sizeof(*unode), AT); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + unode->type = type; + unode->applied = TRUE; + + if (type == SCULPT_UNDO_DYNTOPO_END) { + unode->bm_entry = BM_log_entry_add(ss->bm_log); + BM_log_before_all_removed(ss->bm, ss->bm_log); + } + else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { + Mesh *me = ob->data; + + /* Store a copy of the mesh's current vertices, loops, and + * polys. A full copy like this is needed because entering + * dynamic-topology immediately does topological edits + * (converting polys to triangles) that the BMLog can't + * fully restore from */ + CustomData_copy(&me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH, + CD_DUPLICATE, me->totvert); + CustomData_copy(&me->edata, &unode->bm_enter_edata, CD_MASK_MESH, + CD_DUPLICATE, me->totedge); + CustomData_copy(&me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH, + CD_DUPLICATE, me->totloop); + CustomData_copy(&me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH, + CD_DUPLICATE, me->totpoly); + unode->bm_enter_totvert = me->totvert; + unode->bm_enter_totedge = me->totedge; + unode->bm_enter_totloop = me->totloop; + unode->bm_enter_totpoly = me->totpoly; + + unode->bm_entry = BM_log_entry_add(ss->bm_log); + BM_log_all_added(ss->bm, ss->bm_log); + } + else { + unode->bm_entry = BM_log_entry_add(ss->bm_log); + } + + BLI_addtail(lb, unode); + } + + if (node) { + switch (type) { + case SCULPT_UNDO_COORDS: + case SCULPT_UNDO_HIDDEN: + case SCULPT_UNDO_MASK: + /* Before any vertex values get modified, ensure their + * original positions are logged */ + BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { + BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + } + BLI_pbvh_vertex_iter_end; + break; + + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + break; + } + } + + return unode; +} + SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) { @@ -549,7 +753,18 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, /* list is manipulated by multiple threads, so we lock */ BLI_lock_thread(LOCK_CUSTOM1); - if ((unode = sculpt_undo_get_node(node))) { + if (ss->bm || + ELEM(type, + SCULPT_UNDO_DYNTOPO_BEGIN, + SCULPT_UNDO_DYNTOPO_END)) + { + /* Dynamic topology stores only one undo node per stroke, + * regardless of the number of PBVH nodes modified */ + unode = sculpt_undo_bmesh_push(ob, node, type); + BLI_unlock_thread(LOCK_CUSTOM1); + return unode; + } + else if ((unode = sculpt_undo_get_node(node))) { BLI_unlock_thread(LOCK_CUSTOM1); return unode; } @@ -583,6 +798,11 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, case SCULPT_UNDO_MASK: sculpt_undo_store_mask(ob, unode); break; + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; } /* store active shape key */