From 7c0e285948408c39902b3349e3da9a2fbc1e2fc2 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Fri, 3 Apr 2020 23:41:54 +0200 Subject: Cleanup: Move Detail Operators and Dyntopo to their own files --- source/blender/editors/sculpt_paint/CMakeLists.txt | 2 + source/blender/editors/sculpt_paint/sculpt.c | 772 +-------------------- .../blender/editors/sculpt_paint/sculpt_detail.c | 428 ++++++++++++ .../blender/editors/sculpt_paint/sculpt_dyntopo.c | 443 ++++++++++++ .../editors/sculpt_paint/sculpt_filter_mask.c | 27 +- .../blender/editors/sculpt_paint/sculpt_intern.h | 44 +- .../editors/sculpt_paint/sculpt_mask_expand.c | 4 +- source/blender/editors/sculpt_paint/sculpt_undo.c | 12 +- 8 files changed, 954 insertions(+), 778 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/sculpt_detail.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_dyntopo.c (limited to 'source/blender/editors/sculpt_paint') diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index f9858804394..b8754953741 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -61,6 +61,8 @@ set(SRC sculpt.c sculpt_automasking.c sculpt_cloth.c + sculpt_detail.c + sculpt_dyntopo.c sculpt_face_set.c sculpt_filter_mask.c sculpt_filter_mesh.c diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 5bd0f3f2f48..b304a476dec 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2585,15 +2585,6 @@ typedef struct { struct IsectRayPrecalc isect_precalc; } SculptRaycastData; -typedef struct { - const float *ray_start; - bool hit; - float depth; - float edge_length; - - struct IsectRayPrecalc isect_precalc; -} SculptDetailRaycastData; - typedef struct { SculptSession *ss; const float *ray_start, *ray_normal; @@ -6495,7 +6486,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS)); } -static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) +void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) { SculptSession *ss = ob->sculpt; View3D *v3d = CTX_wm_view3d(C); @@ -6578,24 +6569,12 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t } } -static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) -{ - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptDetailRaycastData *srd = data_v; - if (BKE_pbvh_bmesh_node_raycast_detail( - node, srd->ray_start, &srd->isect_precalc, &srd->depth, &srd->edge_length)) { - srd->hit = true; - *tmin = srd->depth; - } - } -} - -static float sculpt_raycast_init(ViewContext *vc, - const float mouse[2], - float ray_start[3], - float ray_end[3], - float ray_normal[3], - bool original) +float SCULPT_raycast_init(ViewContext *vc, + const float mouse[2], + float ray_start[3], + float ray_end[3], + float ray_normal[3], + bool original) { float obimat[4][4]; float dist; @@ -6661,8 +6640,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, } /* PBVH raycast to get active vertex and face normal. */ - depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); - sculpt_stroke_modifiers_check(C, ob, brush); + depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + SCULPT_stroke_modifiers_check(C, ob, brush); SculptRaycastData srd = { .original = original, @@ -6781,9 +6760,9 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - sculpt_stroke_modifiers_check(C, ob, brush); + SCULPT_stroke_modifiers_check(C, ob, brush); - depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BM_mesh_elem_table_ensure(ss->bm, BM_VERT); @@ -7075,7 +7054,7 @@ static void sculpt_stroke_update_step(bContext *C, SculptSession *ss = ob->sculpt; const Brush *brush = BKE_paint_brush(&sd->paint); - sculpt_stroke_modifiers_check(C, ob, brush); + SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); sculpt_restore_mesh(sd, ob); @@ -7156,7 +7135,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str BLI_assert(brush == ss->cache->brush); /* const, so we shouldn't change. */ ups->draw_inverted = false; - sculpt_stroke_modifiers_check(C, ob, brush); + SCULPT_stroke_modifiers_check(C, ob, brush); /* Alt-Smooth. */ if (ss->cache->alt_smooth) { @@ -7337,401 +7316,18 @@ 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) -{ - if (bm->totloop != bm->totface * 3) { - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); - } -} - -void sculpt_pbvh_clear(Object *ob) -{ - SculptSession *ss = ob->sculpt; - - /* Clear out any existing DM and PBVH. */ - if (ss->pbvh) { - BKE_pbvh_free(ss->pbvh); - ss->pbvh = NULL; - } - - if (ss->pmap) { - MEM_freeN(ss->pmap); - ss->pmap = NULL; - } - - if (ss->pmap_mem) { - MEM_freeN(ss->pmap_mem); - ss->pmap_mem = NULL; - } - - BKE_object_free_derived_caches(ob); - - /* Tag to rebuild PBVH in depsgraph. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); -} - -void sculpt_dyntopo_node_layers_add(SculptSession *ss) -{ - int cd_node_layer_index; - - char layer_id[] = "_dyntopo_node_id"; - - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); - } - - ss->cd_vert_node_offset = CustomData_get_n_offset( - &ss->bm->vdata, - CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); - - ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; - - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); - } - - ss->cd_face_node_offset = CustomData_get_n_offset( - &ss->bm->pdata, - CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); - - ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; -} - -static void sculpt_dynamic_topology_enable_ex(Main *bmain, - Depsgraph *depsgraph, - Scene *scene, - Object *ob) -{ - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); - - sculpt_pbvh_clear(ob); - - ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != - 0; - - /* Dynamic topology doesn't ensure selection state is valid, so remove [#36280]. */ - BKE_mesh_mselect_clear(me); - - /* Create triangles-only BMesh. */ - ss->bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - - BM_mesh_bm_from_me(ss->bm, - me, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - .use_shapekey = true, - .active_shapekey = ob->shapenr, - })); - sculpt_dynamic_topology_triangulate(ss->bm); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - sculpt_dyntopo_node_layers_add(ss); - /* Make sure the data for existing faces are initialized. */ - if (me->totpoly != ss->bm->totface) { - BM_mesh_normals_update(ss->bm); - } - - /* Enable dynamic topology. */ - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - - /* Enable logging for undo/redo. */ - ss->bm_log = BM_log_create(ss->bm); - - /* Update dependency graph, so modifiers that depend on dyntopo being enabled - * are re-evaluated and the PBVH is re-created. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_scene_graph_update_tagged(depsgraph, bmain); -} - -/* 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. */ -static void sculpt_dynamic_topology_disable_ex( - Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, SculptUndoNode *unode) -{ - 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. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - me->totvert = geometry->totvert; - me->totloop = geometry->totloop; - me->totpoly = geometry->totpoly; - me->totedge = geometry->totedge; - me->totface = 0; - CustomData_copy( - &geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert); - CustomData_copy( - &geometry->edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge); - CustomData_copy( - &geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop); - CustomData_copy( - &geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); - - BKE_mesh_update_customdata_pointers(me, false); - } - else { - BKE_sculptsession_bm_to_me(ob, true); - - /* Reset Face Sets as they are no longer valid. */ - if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) { - CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly); - } - ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); - for (int i = 0; i < me->totpoly; i++) { - ss->face_sets[i] = 1; - } - me->face_sets_color_default = 1; - - /* Sync the visibility to vertices manually as the pmap is still not initialized. */ - for (int i = 0; i < me->totvert; i++) { - me->mvert[i].flag &= ~ME_HIDE; - me->mvert[i].flag |= ME_VERT_PBVH_UPDATE; - } - } - - /* Clear data. */ - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - - /* Typically valid but with global-undo they can be NULL. [#36234] */ - if (ss->bm) { - BM_mesh_free(ss->bm); - ss->bm = NULL; - } - if (ss->bm_log) { - BM_log_free(ss->bm_log); - ss->bm_log = NULL; - } - - BKE_particlesystem_reset_all(ob); - BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); - - /* Update dependency graph, so modifiers that depend on dyntopo being enabled - * are re-evaluated and the PBVH is re-created. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_scene_graph_update_tagged(depsgraph, bmain); -} - -void sculpt_dynamic_topology_disable(bContext *C, SculptUndoNode *unode) -{ - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - sculpt_dynamic_topology_disable_ex(bmain, depsgraph, scene, ob, unode); -} - -static void sculpt_dynamic_topology_disable_with_undo(Main *bmain, - Depsgraph *depsgraph, - Scene *scene, - Object *ob) -{ - 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_ex(bmain, depsgraph, scene, ob, NULL); - SCULPT_undo_push_end(); - } -} - -static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, - Depsgraph *depsgraph, - Scene *scene, - Object *ob) -{ - SculptSession *ss = ob->sculpt; - if (ss->bm == NULL) { - SCULPT_undo_push_begin("Dynamic topology enable"); - sculpt_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); - SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - SCULPT_undo_push_end(); - } -} - -static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - - WM_cursor_wait(true); - - if (ss->bm) { - sculpt_dynamic_topology_disable_with_undo(bmain, depsgraph, scene, ob); - } - else { - sculpt_dynamic_topology_enable_with_undo(bmain, depsgraph, scene, ob); - } - - WM_cursor_wait(false); - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - - return OPERATOR_FINISHED; -} - -enum eDynTopoWarnFlag { - DYNTOPO_WARN_VDATA = (1 << 0), - DYNTOPO_WARN_EDATA = (1 << 1), - DYNTOPO_WARN_LDATA = (1 << 2), - DYNTOPO_WARN_MODIFIER = (1 << 3), -}; - -static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) -{ - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); - uiLayout *layout = UI_popup_menu_layout(pup); - - if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { - const char *msg_error = TIP_("Vertex Data Detected!"); - const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); - uiItemL(layout, msg_error, ICON_INFO); - uiItemL(layout, msg, ICON_NONE); - uiItemS(layout); - } - - if (flag & DYNTOPO_WARN_MODIFIER) { - const char *msg_error = TIP_("Generative Modifiers Detected!"); - const char *msg = TIP_( - "Keeping the modifiers will increase polycount when returning to object mode"); - - uiItemL(layout, msg_error, ICON_INFO); - uiItemL(layout, msg, ICON_NONE); - uiItemS(layout); - } - - uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, NULL); - - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; -} - -static enum eDynTopoWarnFlag sculpt_dynamic_topology_check(Scene *scene, Object *ob) -{ - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; - - enum eDynTopoWarnFlag flag = 0; - - BLI_assert(ss->bm == NULL); - UNUSED_VARS_NDEBUG(ss); - - for (int i = 0; i < CD_NUMTYPES; i++) { - if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { - if (CustomData_has_layer(&me->vdata, i)) { - flag |= DYNTOPO_WARN_VDATA; - } - if (CustomData_has_layer(&me->edata, i)) { - flag |= DYNTOPO_WARN_EDATA; - } - if (CustomData_has_layer(&me->ldata, i)) { - flag |= DYNTOPO_WARN_LDATA; - } - } - } - - { - VirtualModifierData virtualModifierData; - ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); - - /* Exception for shape keys because we can edit those. */ - for (; md; md = md->next) { - const ModifierTypeInfo *mti = modifierType_getInfo(md->type); - if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) { - continue; - } - - if (mti->type == eModifierTypeType_Constructive) { - flag |= DYNTOPO_WARN_MODIFIER; - break; - } - } - } - - return flag; -} - -static int sculpt_dynamic_topology_toggle_invoke(bContext *C, - wmOperator *op, - const wmEvent *UNUSED(event)) -{ - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - - if (!ss->bm) { - Scene *scene = CTX_data_scene(C); - enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); - - if (flag) { - /* The mesh has customdata that will be lost, let the user confirm this is OK. */ - return dyntopo_warning_popup(C, op->type, flag); - } - } - - 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); + SCULPT_pbvh_clear(ob); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; } -static bool 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 @@ -7792,7 +7388,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) "symmetrize input=%avef direction=%i dist=%f", sd->symmetrize_direction, 0.00001f); - sculpt_dynamic_topology_triangulate(ss->bm); + SCULPT_dynamic_topology_triangulate(ss->bm); /* Bisect operator flags edges (keep tags clean for edge queue). */ BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); @@ -7853,7 +7449,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) } /* Redraw. */ - sculpt_pbvh_clear(ob); + SCULPT_pbvh_clear(ob); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; @@ -7959,7 +7555,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, message_unsupported = TIP_("multi-res modifier"); } else { - enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); + enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); if (flag == 0) { /* pass */ } @@ -7988,7 +7584,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, if (has_undo) { SCULPT_undo_push_begin("Dynamic topology enable"); } - sculpt_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); + SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (has_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); SCULPT_undo_push_end(); @@ -8131,338 +7727,6 @@ static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static bool sculpt_and_constant_or_manual_detail_poll(bContext *C) -{ - Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - - return SCULPT_mode_poll(C) && ob->sculpt->bm && - (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); -} - -static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - float size; - float bb_min[3], bb_max[3], center[3], dim[3]; - int totnodes; - PBVHNode **nodes; - - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); - - if (!totnodes) { - return OPERATOR_CANCELLED; - } - - for (int i = 0; i < totnodes; i++) { - BKE_pbvh_node_mark_topology_update(nodes[i]); - } - /* Get the bounding box, it's center and size. */ - BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); - add_v3_v3v3(center, bb_min, bb_max); - mul_v3_fl(center, 0.5f); - sub_v3_v3v3(dim, bb_max, bb_min); - size = max_fff(dim[0], dim[1], dim[2]); - - /* Update topology size. */ - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); - - SCULPT_undo_push_begin("Dynamic topology flood fill"); - SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); - - while (BKE_pbvh_bmesh_update_topology( - ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false)) { - for (int i = 0; i < totnodes; i++) { - BKE_pbvh_node_mark_topology_update(nodes[i]); - } - } - - MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); - - /* Force rebuild of pbvh for better BB placement. */ - sculpt_pbvh_clear(ob); - /* Redraw. */ - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Detail Flood Fill"; - ot->idname = "SCULPT_OT_detail_flood_fill"; - ot->description = "Flood fill the mesh with the selected detail setting"; - - /* API callbacks. */ - ot->exec = sculpt_detail_flood_fill_exec; - ot->poll = sculpt_and_constant_or_manual_detail_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -typedef enum eSculptSampleDetailModeTypes { - SAMPLE_DETAIL_DYNTOPO = 0, - SAMPLE_DETAIL_VOXEL = 1, -} eSculptSampleDetailModeTypes; - -static EnumPropertyItem prop_sculpt_sample_detail_mode_types[] = { - {SAMPLE_DETAIL_DYNTOPO, "DYNTOPO", 0, "Dyntopo", "Sample dyntopo detail"}, - {SAMPLE_DETAIL_VOXEL, "VOXEL", 0, "Voxel", "Sample mesh voxel size"}, - {0, NULL, 0, NULL, NULL}, -}; - -static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) -{ - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Object *ob = vc->obact; - Mesh *mesh = ob->data; - - SculptSession *ss = ob->sculpt; - SculptCursorGeometryInfo sgi; - SCULPT_vertex_random_access_init(ss); - - /* Update the active vertex. */ - float mouse[2] = {mx, my}; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false); - - /* Average the edge length of the connected edges to the active vertex. */ - int active_vertex = SCULPT_active_vertex_get(ss); - const float *active_vertex_co = SCULPT_active_vertex_co_get(ss); - float edge_length = 0.0f; - int tot = 0; - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { - edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index)); - tot += 1; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (tot > 0) { - mesh->remesh_voxel_size = edge_length / (float)tot; - } -} - -static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, int mx, int my) -{ - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = vc->obact; - Brush *brush = BKE_paint_brush(&sd->paint); - - sculpt_stroke_modifiers_check(C, ob, brush); - - float mouse[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; - float ray_start[3], ray_end[3], ray_normal[3]; - float depth = sculpt_raycast_init(vc, mouse, ray_start, ray_end, ray_normal, false); - - SculptDetailRaycastData srd; - srd.hit = 0; - srd.ray_start = ray_start; - srd.depth = depth; - srd.edge_length = 0.0f; - isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - - BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false); - - if (srd.hit && srd.edge_length > 0.0f) { - /* Convert edge length to world space detail resolution. */ - sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); - } -} - -static int sample_detail(bContext *C, int mx, int my, int mode) -{ - /* Find 3D view to pick from. */ - bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); - ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mx, my) : NULL; - if (region == NULL) { - return OPERATOR_CANCELLED; - } - - /* Set context to 3D view. */ - ScrArea *prev_area = CTX_wm_area(C); - ARegion *prev_region = CTX_wm_region(C); - CTX_wm_area_set(C, area); - CTX_wm_region_set(C, region); - - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - Object *ob = vc.obact; - SculptSession *ss = ob->sculpt; - - if (!ss->pbvh) { - return OPERATOR_CANCELLED; - } - - /* Pick sample detail. */ - switch (mode) { - case SAMPLE_DETAIL_DYNTOPO: - if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { - CTX_wm_area_set(C, prev_area); - CTX_wm_region_set(C, prev_region); - return OPERATOR_CANCELLED; - } - sample_detail_dyntopo(C, &vc, region, mx, my); - break; - case SAMPLE_DETAIL_VOXEL: - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { - CTX_wm_area_set(C, prev_area); - CTX_wm_region_set(C, prev_region); - return OPERATOR_CANCELLED; - } - sample_detail_voxel(C, &vc, mx, my); - break; - } - - /* Restore context. */ - CTX_wm_area_set(C, prev_area); - CTX_wm_region_set(C, prev_region); - - return OPERATOR_FINISHED; -} - -static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) -{ - int ss_co[2]; - RNA_int_get_array(op->ptr, "location", ss_co); - int mode = RNA_enum_get(op->ptr, "mode"); - return sample_detail(C, ss_co[0], ss_co[1], mode); -} - -static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) -{ - ED_workspace_status_text(C, TIP_("Click on the mesh to set the detail")); - WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; -} - -static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - switch (event->type) { - case LEFTMOUSE: - if (event->val == KM_PRESS) { - int ss_co[2] = {event->x, event->y}; - - int mode = RNA_enum_get(op->ptr, "mode"); - sample_detail(C, ss_co[0], ss_co[1], mode); - - RNA_int_set_array(op->ptr, "location", ss_co); - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - - return OPERATOR_FINISHED; - } - break; - - case RIGHTMOUSE: { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - - return OPERATOR_CANCELLED; - } - } - - return OPERATOR_RUNNING_MODAL; -} - -static void SCULPT_OT_sample_detail_size(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Sample Detail Size"; - ot->idname = "SCULPT_OT_sample_detail_size"; - ot->description = "Sample the mesh detail on clicked point"; - - /* API callbacks. */ - ot->invoke = sculpt_sample_detail_size_invoke; - ot->exec = sculpt_sample_detail_size_exec; - ot->modal = sculpt_sample_detail_size_modal; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_int_array(ot->srna, - "location", - 2, - NULL, - 0, - SHRT_MAX, - "Location", - "Screen Coordinates of sampling", - 0, - SHRT_MAX); - RNA_def_enum(ot->srna, - "mode", - prop_sculpt_sample_detail_mode_types, - SAMPLE_DETAIL_DYNTOPO, - "Detail Mode", - "Target sculpting workflow that is going to use the sampled size"); -} - -/* Dynamic-topology detail size. - * - * This should be improved further, perhaps by showing a triangle - * grid rather than brush alpha. */ -static void set_brush_rc_props(PointerRNA *ptr, const char *prop) -{ - char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); - RNA_string_set(ptr, "data_path_primary", path); - MEM_freeN(path); -} - -static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - - PointerRNA props_ptr; - wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); - - WM_operator_properties_create_ptr(&props_ptr, ot); - - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set( - &props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); - } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); - } - else { - set_brush_rc_props(&props_ptr, "detail_size"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); - } - - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); - - WM_operator_properties_free(&props_ptr); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_set_detail_size(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Set Detail Size"; - ot->idname = "SCULPT_OT_set_detail_size"; - ot->description = - "Set the mesh detail (either relative or constant one, depending on current dyntopo mode)"; - - /* API callbacks. */ - ot->exec = sculpt_set_detail_size_exec; - ot->poll = sculpt_and_dynamic_topology_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c new file mode 100644 index 00000000000..a99aa3d1bcf --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -0,0 +1,428 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" + +#include "bmesh.h" + +#include +#include + +typedef struct { + const float *ray_start; + bool hit; + float depth; + float edge_length; + + struct IsectRayPrecalc isect_precalc; +} SculptDetailRaycastData; + +static bool sculpt_and_constant_or_manual_detail_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + return SCULPT_mode_poll(C) && ob->sculpt->bm && + (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); +} + +static bool sculpt_and_dynamic_topology_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + return SCULPT_mode_poll(C) && ob->sculpt->bm; +} + +static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + float size; + float bb_min[3], bb_max[3], center[3], dim[3]; + int totnodes; + PBVHNode **nodes; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); + + if (!totnodes) { + return OPERATOR_CANCELLED; + } + + for (int i = 0; i < totnodes; i++) { + BKE_pbvh_node_mark_topology_update(nodes[i]); + } + /* Get the bounding box, it's center and size. */ + BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); + add_v3_v3v3(center, bb_min, bb_max); + mul_v3_fl(center, 0.5f); + sub_v3_v3v3(dim, bb_max, bb_min); + size = max_fff(dim[0], dim[1], dim[2]); + + /* Update topology size. */ + float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + + SCULPT_undo_push_begin("Dynamic topology flood fill"); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); + + while (BKE_pbvh_bmesh_update_topology( + ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false)) { + for (int i = 0; i < totnodes; i++) { + BKE_pbvh_node_mark_topology_update(nodes[i]); + } + } + + MEM_SAFE_FREE(nodes); + SCULPT_undo_push_end(); + + /* Force rebuild of pbvh for better BB placement. */ + SCULPT_pbvh_clear(ob); + /* Redraw. */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Detail Flood Fill"; + ot->idname = "SCULPT_OT_detail_flood_fill"; + ot->description = "Flood fill the mesh with the selected detail setting"; + + /* API callbacks. */ + ot->exec = sculpt_detail_flood_fill_exec; + ot->poll = sculpt_and_constant_or_manual_detail_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +typedef enum eSculptSampleDetailModeTypes { + SAMPLE_DETAIL_DYNTOPO = 0, + SAMPLE_DETAIL_VOXEL = 1, +} eSculptSampleDetailModeTypes; + +static EnumPropertyItem prop_sculpt_sample_detail_mode_types[] = { + {SAMPLE_DETAIL_DYNTOPO, "DYNTOPO", 0, "Dyntopo", "Sample dyntopo detail"}, + {SAMPLE_DETAIL_VOXEL, "VOXEL", 0, "Voxel", "Sample mesh voxel size"}, + {0, NULL, 0, NULL, NULL}, +}; + +static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = vc->obact; + Mesh *mesh = ob->data; + + SculptSession *ss = ob->sculpt; + SculptCursorGeometryInfo sgi; + SCULPT_vertex_random_access_init(ss); + + /* Update the active vertex. */ + float mouse[2] = {mx, my}; + SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false); + + /* Average the edge length of the connected edges to the active vertex. */ + int active_vertex = SCULPT_active_vertex_get(ss); + const float *active_vertex_co = SCULPT_active_vertex_co_get(ss); + float edge_length = 0.0f; + int tot = 0; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { + edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index)); + tot += 1; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + if (tot > 0) { + mesh->remesh_voxel_size = edge_length / (float)tot; + } +} + +static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) +{ + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptDetailRaycastData *srd = data_v; + if (BKE_pbvh_bmesh_node_raycast_detail( + node, srd->ray_start, &srd->isect_precalc, &srd->depth, &srd->edge_length)) { + srd->hit = true; + *tmin = srd->depth; + } + } +} + +static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, int mx, int my) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = vc->obact; + Brush *brush = BKE_paint_brush(&sd->paint); + + SCULPT_stroke_modifiers_check(C, ob, brush); + + float mouse[2] = {mx - region->winrct.xmin, my - region->winrct.ymin}; + float ray_start[3], ray_end[3], ray_normal[3]; + float depth = SCULPT_raycast_init(vc, mouse, ray_start, ray_end, ray_normal, false); + + SculptDetailRaycastData srd; + srd.hit = 0; + srd.ray_start = ray_start; + srd.depth = depth; + srd.edge_length = 0.0f; + isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); + + BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false); + + if (srd.hit && srd.edge_length > 0.0f) { + /* Convert edge length to world space detail resolution. */ + sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); + } +} + +static int sample_detail(bContext *C, int mx, int my, int mode) +{ + /* Find 3D view to pick from. */ + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); + ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mx, my) : NULL; + if (region == NULL) { + return OPERATOR_CANCELLED; + } + + /* Set context to 3D view. */ + ScrArea *prev_area = CTX_wm_area(C); + ARegion *prev_region = CTX_wm_region(C); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + Object *ob = vc.obact; + SculptSession *ss = ob->sculpt; + + if (!ss->pbvh) { + return OPERATOR_CANCELLED; + } + + /* Pick sample detail. */ + switch (mode) { + case SAMPLE_DETAIL_DYNTOPO: + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + CTX_wm_area_set(C, prev_area); + CTX_wm_region_set(C, prev_region); + return OPERATOR_CANCELLED; + } + sample_detail_dyntopo(C, &vc, region, mx, my); + break; + case SAMPLE_DETAIL_VOXEL: + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + CTX_wm_area_set(C, prev_area); + CTX_wm_region_set(C, prev_region); + return OPERATOR_CANCELLED; + } + sample_detail_voxel(C, &vc, mx, my); + break; + } + + /* Restore context. */ + CTX_wm_area_set(C, prev_area); + CTX_wm_region_set(C, prev_region); + + return OPERATOR_FINISHED; +} + +static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) +{ + int ss_co[2]; + RNA_int_get_array(op->ptr, "location", ss_co); + int mode = RNA_enum_get(op->ptr, "mode"); + return sample_detail(C, ss_co[0], ss_co[1], mode); +} + +static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) +{ + ED_workspace_status_text(C, TIP_("Click on the mesh to set the detail")); + WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + switch (event->type) { + case LEFTMOUSE: + if (event->val == KM_PRESS) { + int ss_co[2] = {event->x, event->y}; + + int mode = RNA_enum_get(op->ptr, "mode"); + sample_detail(C, ss_co[0], ss_co[1], mode); + + RNA_int_set_array(op->ptr, "location", ss_co); + WM_cursor_modal_restore(CTX_wm_window(C)); + ED_workspace_status_text(C, NULL); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); + + return OPERATOR_FINISHED; + } + break; + + case RIGHTMOUSE: { + WM_cursor_modal_restore(CTX_wm_window(C)); + ED_workspace_status_text(C, NULL); + + return OPERATOR_CANCELLED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +void SCULPT_OT_sample_detail_size(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Sample Detail Size"; + ot->idname = "SCULPT_OT_sample_detail_size"; + ot->description = "Sample the mesh detail on clicked point"; + + /* API callbacks. */ + ot->invoke = sculpt_sample_detail_size_invoke; + ot->exec = sculpt_sample_detail_size_exec; + ot->modal = sculpt_sample_detail_size_modal; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int_array(ot->srna, + "location", + 2, + NULL, + 0, + SHRT_MAX, + "Location", + "Screen Coordinates of sampling", + 0, + SHRT_MAX); + RNA_def_enum(ot->srna, + "mode", + prop_sculpt_sample_detail_mode_types, + SAMPLE_DETAIL_DYNTOPO, + "Detail Mode", + "Target sculpting workflow that is going to use the sampled size"); +} + +/* Dynamic-topology detail size. + * + * This should be improved further, perhaps by showing a triangle + * grid rather than brush alpha. */ +static void set_brush_rc_props(PointerRNA *ptr, const char *prop) +{ + char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); + RNA_string_set(ptr, "data_path_primary", path); + MEM_freeN(path); +} + +static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); + + WM_operator_properties_create_ptr(&props_ptr, ot); + + if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { + set_brush_rc_props(&props_ptr, "constant_detail_resolution"); + RNA_string_set( + &props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); + } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + set_brush_rc_props(&props_ptr, "constant_detail_resolution"); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); + } + else { + set_brush_rc_props(&props_ptr, "detail_size"); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); + } + + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + + WM_operator_properties_free(&props_ptr); + + return OPERATOR_FINISHED; +} + +void SCULPT_OT_set_detail_size(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Set Detail Size"; + ot->idname = "SCULPT_OT_set_detail_size"; + ot->description = + "Set the mesh detail (either relative or constant one, depending on current dyntopo mode)"; + + /* API callbacks. */ + ot->exec = sculpt_set_detail_size_exec; + ot->poll = sculpt_and_dynamic_topology_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c new file mode 100644 index 00000000000..5f75c1d6813 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -0,0 +1,443 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include +#include + +void SCULPT_dynamic_topology_triangulate(BMesh *bm) +{ + if (bm->totloop != bm->totface * 3) { + BM_mesh_triangulate( + bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); + } +} + +void SCULPT_pbvh_clear(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + /* Clear out any existing DM and PBVH. */ + if (ss->pbvh) { + BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + } + + if (ss->pmap) { + MEM_freeN(ss->pmap); + ss->pmap = NULL; + } + + if (ss->pmap_mem) { + MEM_freeN(ss->pmap_mem); + ss->pmap_mem = NULL; + } + + BKE_object_free_derived_caches(ob); + + /* Tag to rebuild PBVH in depsgraph. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); +} + +void SCULPT_dyntopo_node_layers_add(SculptSession *ss) +{ + int cd_node_layer_index; + + char layer_id[] = "_dyntopo_node_id"; + + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); + if (cd_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); + } + + ss->cd_vert_node_offset = CustomData_get_n_offset( + &ss->bm->vdata, + CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); + + ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); + if (cd_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); + } + + ss->cd_face_node_offset = CustomData_get_n_offset( + &ss->bm->pdata, + CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); + + ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; +} + +void SCULPT_dynamic_topology_enable_ex(Main *bmain, + Depsgraph *depsgraph, + Scene *scene, + Object *ob) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + + SCULPT_pbvh_clear(ob); + + ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != + 0; + + /* Dynamic topology doesn't ensure selection state is valid, so remove [#36280]. */ + BKE_mesh_mselect_clear(me); + + /* Create triangles-only BMesh. */ + ss->bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + + BM_mesh_bm_from_me(ss->bm, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); + SCULPT_dynamic_topology_triangulate(ss->bm); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + SCULPT_dyntopo_node_layers_add(ss); + /* Make sure the data for existing faces are initialized. */ + if (me->totpoly != ss->bm->totface) { + BM_mesh_normals_update(ss->bm); + } + + /* Enable dynamic topology. */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* Enable logging for undo/redo. */ + ss->bm_log = BM_log_create(ss->bm); + + /* Update dependency graph, so modifiers that depend on dyntopo being enabled + * are re-evaluated and the PBVH is re-created. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_scene_graph_update_tagged(depsgraph, bmain); +} + +/* 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. */ +static void SCULPT_dynamic_topology_disable_ex( + Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, SculptUndoNode *unode) +{ + 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. */ + SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; + me->totvert = geometry->totvert; + me->totloop = geometry->totloop; + me->totpoly = geometry->totpoly; + me->totedge = geometry->totedge; + me->totface = 0; + CustomData_copy( + &geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert); + CustomData_copy( + &geometry->edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge); + CustomData_copy( + &geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop); + CustomData_copy( + &geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); + + BKE_mesh_update_customdata_pointers(me, false); + } + else { + BKE_sculptsession_bm_to_me(ob, true); + + /* Reset Face Sets as they are no longer valid. */ + if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) { + CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly); + } + ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + for (int i = 0; i < me->totpoly; i++) { + ss->face_sets[i] = 1; + } + me->face_sets_color_default = 1; + + /* Sync the visibility to vertices manually as the pmap is still not initialized. */ + for (int i = 0; i < me->totvert; i++) { + me->mvert[i].flag &= ~ME_HIDE; + me->mvert[i].flag |= ME_VERT_PBVH_UPDATE; + } + } + + /* Clear data. */ + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* Typically valid but with global-undo they can be NULL. [#36234] */ + if (ss->bm) { + BM_mesh_free(ss->bm); + ss->bm = NULL; + } + if (ss->bm_log) { + BM_log_free(ss->bm_log); + ss->bm_log = NULL; + } + + BKE_particlesystem_reset_all(ob); + BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); + + /* Update dependency graph, so modifiers that depend on dyntopo being enabled + * are re-evaluated and the PBVH is re-created. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_scene_graph_update_tagged(depsgraph, bmain); +} + +void SCULPT_dynamic_topology_disable(bContext *C, SculptUndoNode *unode) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + SCULPT_dynamic_topology_disable_ex(bmain, depsgraph, scene, ob, unode); +} + +void sculpt_dynamic_topology_disable_with_undo(Main *bmain, + Depsgraph *depsgraph, + Scene *scene, + Object *ob) +{ + 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_ex(bmain, depsgraph, scene, ob, NULL); + SCULPT_undo_push_end(); + } +} + +static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, + Depsgraph *depsgraph, + Scene *scene, + Object *ob) +{ + SculptSession *ss = ob->sculpt; + if (ss->bm == NULL) { + SCULPT_undo_push_begin("Dynamic topology enable"); + SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + SCULPT_undo_push_end(); + } +} + +static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + WM_cursor_wait(true); + + if (ss->bm) { + sculpt_dynamic_topology_disable_with_undo(bmain, depsgraph, scene, ob); + } + else { + sculpt_dynamic_topology_enable_with_undo(bmain, depsgraph, scene, ob); + } + + WM_cursor_wait(false); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); + + return OPERATOR_FINISHED; +} + + +static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) +{ + uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); + uiLayout *layout = UI_popup_menu_layout(pup); + + if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { + const char *msg_error = TIP_("Vertex Data Detected!"); + const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } + + if (flag & DYNTOPO_WARN_MODIFIER) { + const char *msg_error = TIP_("Generative Modifiers Detected!"); + const char *msg = TIP_( + "Keeping the modifiers will increase polycount when returning to object mode"); + + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } + + uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, NULL); + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) +{ + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; + + enum eDynTopoWarnFlag flag = 0; + + BLI_assert(ss->bm == NULL); + UNUSED_VARS_NDEBUG(ss); + + for (int i = 0; i < CD_NUMTYPES; i++) { + if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { + if (CustomData_has_layer(&me->vdata, i)) { + flag |= DYNTOPO_WARN_VDATA; + } + if (CustomData_has_layer(&me->edata, i)) { + flag |= DYNTOPO_WARN_EDATA; + } + if (CustomData_has_layer(&me->ldata, i)) { + flag |= DYNTOPO_WARN_LDATA; + } + } + } + + { + VirtualModifierData virtualModifierData; + ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + + /* Exception for shape keys because we can edit those. */ + for (; md; md = md->next) { + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); + if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) { + continue; + } + + if (mti->type == eModifierTypeType_Constructive) { + flag |= DYNTOPO_WARN_MODIFIER; + break; + } + } + } + + return flag; +} + +static int sculpt_dynamic_topology_toggle_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + if (!ss->bm) { + Scene *scene = CTX_data_scene(C); + enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); + + if (flag) { + /* The mesh has customdata that will be lost, let the user confirm this is OK. */ + return dyntopo_warning_popup(C, op->type, flag); + } + } + + return sculpt_dynamic_topology_toggle_exec(C, op); +} + +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; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index d895ecc7119..38bbd083994 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -264,20 +264,21 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void SCULPT_mask_filter_smooth_apply(Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, const int smooth_iterations) { - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = nodes, - .filter_type = MASK_FILTER_SMOOTH, - }; +void SCULPT_mask_filter_smooth_apply( + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, const int smooth_iterations) +{ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .filter_type = MASK_FILTER_SMOOTH, + }; - for (int i = 0; i < smooth_iterations; i++) { - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings( - &settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); - } + for (int i = 0; i < smooth_iterations; i++) { + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); + } } void SCULPT_OT_mask_filter(struct wmOperatorType *ot) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 3858a990e97..e2fd54596e7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -77,6 +77,14 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, bool use_sampled_normal); void SCULPT_geometry_preview_lines_update(bContext *C, struct SculptSession *ss, float radius); +void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush); +float SCULPT_raycast_init(struct ViewContext *vc, + const float mouse[2], + float ray_start[3], + float ray_end[3], + float ray_normal[3], + bool original); + /* Sculpt PBVH abstraction API */ void SCULPT_vertex_random_access_init(struct SculptSession *ss); @@ -249,11 +257,33 @@ void SCULPT_floodfill_execute( void SCULPT_floodfill_free(SculptFloodFill *flood); /* Dynamic topology */ -void sculpt_pbvh_clear(Object *ob); -void sculpt_dyntopo_node_layers_add(struct SculptSession *ss); -void sculpt_dynamic_topology_disable(bContext *C, struct SculptUndoNode *unode); + +enum eDynTopoWarnFlag { + DYNTOPO_WARN_VDATA = (1 << 0), + DYNTOPO_WARN_EDATA = (1 << 1), + DYNTOPO_WARN_LDATA = (1 << 2), + DYNTOPO_WARN_MODIFIER = (1 << 3), +}; + +void SCULPT_dynamic_topology_enable_ex(struct Main *bmain, + struct Depsgraph *depsgraph, + Scene *scene, + Object *ob); +void SCULPT_dynamic_topology_disable(bContext *C, struct SculptUndoNode *unode); +void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain, + struct Depsgraph *depsgraph, + Scene *scene, + Object *ob); + bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush); +void SCULPT_dynamic_topology_triangulate(struct BMesh *bm); +void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss); + +enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); + +void SCULPT_pbvh_clear(Object *ob); + /* Automasking. */ float SCULPT_automasking_factor_get(SculptSession *ss, int vert); @@ -851,4 +881,12 @@ void SCULPT_OT_dirty_mask(struct wmOperatorType *ot); /* Mask and Face Sets Expand. */ void SCULPT_OT_mask_expand(struct wmOperatorType *ot); +/* Detail size. */ +void SCULPT_OT_detail_flood_fill(struct wmOperatorType *ot); +void SCULPT_OT_sample_detail_size(struct wmOperatorType *ot); +void SCULPT_OT_set_detail_size(struct wmOperatorType *ot); + +/* Dyntopo. */ +void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot); + #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 2d4fc2b0258..34ca92acef9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -68,7 +68,6 @@ #include #include - static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); @@ -209,7 +208,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * /* Smooth iterations. */ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false); const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations"); - SCULPT_mask_filter_smooth_apply(sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations); + SCULPT_mask_filter_smooth_apply( + sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations); /* Pivot position. */ if (RNA_boolean_get(op->ptr, "update_pivot")) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index f21bfe245dd..f78af10ad67 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -422,7 +422,7 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C, } } else { - sculpt_pbvh_clear(ob); + SCULPT_pbvh_clear(ob); } } @@ -432,7 +432,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; Mesh *me = ob->data; - sculpt_pbvh_clear(ob); + SCULPT_pbvh_clear(ob); /* Create empty BMesh and enable logging. */ ss->bm = BM_mesh_create(&bm_mesh_allocsize_default, @@ -440,7 +440,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) .use_toolflags = false, })); BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - sculpt_dyntopo_node_layers_add(ss); + SCULPT_dyntopo_node_layers_add(ss); me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; /* Restore the BMLog using saved entries. */ @@ -453,7 +453,7 @@ static void sculpt_undo_bmesh_restore_begin(bContext *C, SculptSession *ss) { if (unode->applied) { - sculpt_dynamic_topology_disable(C, unode); + SCULPT_dynamic_topology_disable(C, unode); unode->applied = false; } else { @@ -481,7 +481,7 @@ static void sculpt_undo_bmesh_restore_end(bContext *C, } else { /* Disable dynamic topology sculpting. */ - sculpt_dynamic_topology_disable(C, NULL); + SCULPT_dynamic_topology_disable(C, NULL); unode->applied = true; } } @@ -552,7 +552,7 @@ static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry) static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) { - sculpt_pbvh_clear(object); + SCULPT_pbvh_clear(object); if (unode->applied) { sculpt_undo_geometry_restore_data(&unode->geometry_modified, object); -- cgit v1.2.3