diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index e39d7198b25..4dc44342fbe 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1239,12 +1239,19 @@ class VIEW3D_PT_sculpt_topology(Panel, View3DPaintPanel): col.active = context.sculpt_object.use_dynamic_topology_sculpting sub = col.column(align=True) sub.active = brush and brush.sculpt_tool not in ('MASK') - sub.prop(sculpt, "detail_size") + if (sculpt.detail_type_method == 'CONSTANT'): + row = sub.row(align=True) + row.prop(sculpt, "detail_size") + row.prop(sculpt, "constant_detail_scale") + else: + sub.prop(sculpt, "detail_size") sub.prop(sculpt, "detail_refine_method", text="") sub.prop(sculpt, "detail_type_method", text="") col.separator() col.prop(sculpt, "use_smooth_shading") col.operator("sculpt.optimize") + if (sculpt.detail_type_method == 'CONSTANT'): + col.operator("sculpt.detail_flood_fill") col.separator() col.prop(sculpt, "symmetrize_direction") col.operator("sculpt.symmetrize") diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 7a0c8a1f187..0828ea54280 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -135,7 +135,7 @@ typedef enum { PBVH_Subdivide = 1, PBVH_Collapse = 2, } PBVHTopologyUpdateMode; -int BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, +bool BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, const float center[3], float radius); /* Node Access */ diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 0176d2055cd..3fae446f1f4 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -1104,7 +1104,7 @@ void BKE_pbvh_build_bmesh(PBVH *bvh, BMesh *bm, int smooth_shading, } /* Collapse short edges, subdivide long edges */ -int BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, +bool BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, const float center[3], float radius) { /* 2 is enough for edge faces - manifold edge */ @@ -1112,7 +1112,7 @@ int BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, BLI_buffer_declare_static(BMFace *, deleted_faces, BLI_BUFFER_NOP, 32); const int cd_vert_mask_offset = CustomData_get_offset(&bvh->bm->vdata, CD_PAINT_MASK); - int modified = FALSE; + bool modified = false; int n; if (mode & PBVH_Collapse) { @@ -1122,6 +1122,7 @@ int BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, EdgeQueueContext eq_ctx = {&q, queue_pool, bvh->bm, cd_vert_mask_offset}; short_edge_queue_create(&eq_ctx, bvh, center, radius); + modified |= !BLI_heap_is_empty(q.heap); pbvh_bmesh_collapse_short_edges(&eq_ctx, bvh, &edge_loops, &deleted_faces); BLI_heap_free(q.heap, NULL); @@ -1135,6 +1136,7 @@ int BKE_pbvh_bmesh_update_topology(PBVH *bvh, PBVHTopologyUpdateMode mode, EdgeQueueContext eq_ctx = {&q, queue_pool, bvh->bm, cd_vert_mask_offset}; long_edge_queue_create(&eq_ctx, bvh, center, radius); + modified |= !BLI_heap_is_empty(q.heap); pbvh_bmesh_subdivide_long_edges(&eq_ctx, bvh, &edge_loops); BLI_heap_free(q.heap, NULL); BLI_mempool_destroy(queue_pool); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 7af05e7bf4a..c6f8007d57e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4510,7 +4510,8 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st sculpt_restore_mesh(sd, ob); if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, (float)sd->detail_size / 100.0f); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, + sd->constant_detail_scale * (float)sd->detail_size / 100.0f); } else { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, @@ -5164,8 +5165,12 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE; } - if (!ts->sculpt->detail_size) + if (!ts->sculpt->detail_size) { ts->sculpt->detail_size = 30; + } + + if (ts->sculpt->constant_detail_scale == 0.0) + ts->sculpt->constant_detail_scale = 1.0f; /* Create sculpt mode session data */ if (ob->sculpt) @@ -5204,6 +5209,76 @@ static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +static int sculpt_and_dynamic_topology_constant_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); +} + +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]; + int i, totnodes; + PBVHNode **nodes; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); + + if (!totnodes) + return OPERATOR_CANCELLED; + + for (i = 0; i < totnodes; i++) { + BKE_pbvh_node_mark_topology_update(nodes[i]); + } + /* get the bounding box, store the size to bb_max and center (zero) to bb_min */ + BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); + sub_v3_v3(bb_max, bb_min); + zero_v3(bb_min); + size = max_fff(bb_max[0], bb_max[1], bb_max[2]); + + /* update topology size */ + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, + sd->constant_detail_scale * (float)sd->detail_size / 100.0f); + + 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, bb_min, size)) { + for (i = 0; i < totnodes; i++) + BKE_pbvh_node_mark_topology_update(nodes[i]); + } + + MEM_freeN(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 = "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_dynamic_topology_constant_detail_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + void ED_operatortypes_sculpt(void) { WM_operatortype_append(SCULPT_OT_brush_stroke); @@ -5212,4 +5287,5 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); WM_operatortype_append(SCULPT_OT_optimize); WM_operatortype_append(SCULPT_OT_symmetrize); + WM_operatortype_append(SCULPT_OT_detail_flood_fill); } diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index d66a1f3ab0b..02b213a1b68 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -855,7 +855,9 @@ typedef struct Sculpt { /* gravity factor for sculpting */ float gravity_factor; - int pad; + + /* scale for constant detail size */ + float constant_detail_scale; struct Object *gravity_object; void *pad2; diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 635c9a3c374..77cad9affb7 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -416,7 +416,13 @@ static void rna_def_sculpt(BlenderRNA *brna) prop = RNA_def_property(srna, "detail_size", PROP_INT, PROP_NONE); RNA_def_property_ui_range(prop, 2, 100, 0, -1); - RNA_def_property_ui_text(prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)"); + RNA_def_property_ui_text(prop, "Detail Size", "Maximum edge length for dynamic topology sculpting"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "constant_detail_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.001, 100.0); + RNA_def_property_ui_range(prop, 0.1, 100.0, 10, 2); + RNA_def_property_ui_text(prop, "Scale", "Multiplier for constant detail size"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "use_smooth_shading", PROP_BOOLEAN, PROP_NONE);