diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_bmesh.c | 44 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_intern.h | 3 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 147 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_sculpt_paint.c | 10 |
7 files changed, 200 insertions, 14 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 4dc44342fbe..b67c8effa3f 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1240,9 +1240,7 @@ class VIEW3D_PT_sculpt_topology(Panel, View3DPaintPanel): sub = col.column(align=True) sub.active = brush and brush.sculpt_tool not in ('MASK') if (sculpt.detail_type_method == 'CONSTANT'): - row = sub.row(align=True) - row.prop(sculpt, "detail_size") - row.prop(sculpt, "constant_detail_scale") + sub.prop(sculpt, "constant_detail") else: sub.prop(sculpt, "detail_size") sub.prop(sculpt, "detail_refine_method", text="") @@ -1251,6 +1249,7 @@ class VIEW3D_PT_sculpt_topology(Panel, View3DPaintPanel): col.prop(sculpt, "use_smooth_shading") col.operator("sculpt.optimize") if (sculpt.detail_type_method == 'CONSTANT'): + col.operator("sculpt.sample_detail_size") col.operator("sculpt.detail_flood_fill") col.separator() col.prop(sculpt, "symmetrize_direction") diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 0828ea54280..3601ff1ce04 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -98,6 +98,9 @@ int BKE_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3], int use const float ray_start[3], const float ray_normal[3], float *dist); +int BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, const float ray_start[3], + const float ray_normal[3], float *detail, float *dist); + /* for orthographic cameras, project the far away ray segment points to the root node so * we can have better precision. */ void BKE_pbvh_raycast_project_ray_root(PBVH *bvh, bool original, float ray_start[3], diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 3fae446f1f4..808b3faad9b 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -1035,6 +1035,50 @@ int pbvh_bmesh_node_raycast(PBVHNode *node, const float ray_start[3], return hit; } +int BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, const float ray_start[3], + const float ray_normal[3], float *detail, float *dist) +{ + GHashIterator gh_iter; + int hit = 0; + BMFace *f_hit = NULL; + + if (node->flag & PBVH_FullyHidden) + return 0; + + GHASH_ITER (gh_iter, node->bm_faces) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + + BLI_assert(f->len == 3); + if (f->len == 3 && !paint_is_bmesh_face_hidden(f)) { + BMVert *v_tri[3]; + int hit_local; + BM_face_as_array_vert_tri(f, v_tri); + hit_local = ray_face_intersection(ray_start, ray_normal, + v_tri[0]->co, + v_tri[1]->co, + v_tri[2]->co, + NULL, dist); + if (hit_local) { + f_hit = f; + } + hit |= hit_local; + } + } + + if (hit) { + float len1, len2, len3; + BMVert *v_tri[3]; + BM_face_as_array_vert_tri(f_hit, v_tri); + len1 = len_v3v3(v_tri[0]->co, v_tri[1]->co); + len2 = len_v3v3(v_tri[1]->co, v_tri[2]->co); + len3 = len_v3v3(v_tri[2]->co, v_tri[0]->co); + + *detail = (len1 + len2 + len3)/3.0f; + } + + return hit; +} + void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode) { diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index d4cd6bcf9d8..75d2a8333a1 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -185,6 +185,9 @@ int pbvh_bmesh_node_raycast(PBVHNode *node, const float ray_start[3], const float ray_normal[3], float *dist, int use_original); +int pbvh_bmesh_node_raycast_detail(PBVHNode *node, const float ray_start[3], + const float ray_normal[3], float *detail, float *dist); + void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode); #endif diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index c6f8007d57e..9ef5852555f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4261,6 +4261,13 @@ typedef struct { int original; } SculptRaycastData; +typedef struct { + float *ray_start, *ray_normal; + int hit; + float dist; + float detail; +} SculptDetailRaycastData; + static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) { if (BKE_pbvh_node_get_tmin(node) < *tmin) { @@ -4289,6 +4296,18 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) } } +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->ray_normal, + &srd->detail, &srd->dist)) { + srd->hit = 1; + *tmin = srd->dist; + } + } +} + /* Do a raycast in the tree to find the 3d brush location * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise @@ -4511,7 +4530,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, - sd->constant_detail_scale * (float)sd->detail_size / 100.0f); + sd->constant_detail / 100.0f); } else { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, @@ -5169,8 +5188,8 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) ts->sculpt->detail_size = 30; } - if (ts->sculpt->constant_detail_scale == 0.0) - ts->sculpt->constant_detail_scale = 1.0f; + if (ts->sculpt->constant_detail == 0.0f) + ts->sculpt->constant_detail = 30.0f; /* Create sculpt mode session data */ if (ob->sculpt) @@ -5244,7 +5263,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) /* update topology size */ BKE_pbvh_bmesh_detail_size_set(ss->pbvh, - sd->constant_detail_scale * (float)sd->detail_size / 100.0f); + sd->constant_detail/ 100.0f); sculpt_undo_push_begin("Dynamic topology flood fill"); sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); @@ -5268,7 +5287,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) static void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) { /* identifiers */ - ot->name = "Flood Fill"; + ot->name = "Detail Flood Fill"; ot->idname = "SCULPT_OT_detail_flood_fill"; ot->description = "Flood fill the mesh with the selected detail setting"; @@ -5279,6 +5298,123 @@ static void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static void sample_detail(bContext *C, int ss_co[2]) +{ + ViewContext vc; + Object *ob; + Sculpt *sd; + float ray_start[3], ray_end[3], ray_normal[3], dist; + float obimat[4][4]; + SculptDetailRaycastData srd; + RegionView3D *rv3d; + float mouse[2] = {ss_co[0], ss_co[1]}; + view3d_set_viewcontext(C, &vc); + + rv3d = vc.ar->regiondata; + ob = vc.obact; + + sd = CTX_data_tool_settings(C)->sculpt; + + sculpt_stroke_modifiers_check(C, ob); + + /* TODO: what if the segment is totally clipped? (return == 0) */ + ED_view3d_win_to_segment(vc.ar, vc.v3d, mouse, ray_start, ray_end, true); + + invert_m4_m4(obimat, ob->obmat); + mul_m4_v3(obimat, ray_start); + mul_m4_v3(obimat, ray_end); + + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); + + if (!rv3d->is_persp) { + BKE_pbvh_raycast_project_ray_root(ob->sculpt->pbvh, false, ray_start, ray_end, ray_normal); + + /* recalculate the normal */ + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); + } + + srd.hit = 0; + srd.ray_start = ray_start; + srd.ray_normal = ray_normal; + srd.dist = dist; + srd.detail = sd->constant_detail; + + BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, + ray_start, ray_normal, false); + + if (srd.hit) + { + sd->constant_detail = srd.detail * 100.0f; + } +} + +static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) +{ + int ss_co[2]; + RNA_int_get_array(op->ptr, "location", ss_co); + sample_detail(C, ss_co); + return OPERATOR_FINISHED; +} + + +static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) { + ScrArea *sa = CTX_wm_area(C); + ED_area_headerprint(sa, "Click on the mesh to set the detail"); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wmEvent *e) { + + switch (e->type) { + case LEFTMOUSE: + if (e->val == KM_PRESS) { + ScrArea *sa = CTX_wm_area(C); + int ss_co[2] = {e->mval[0], e->mval[1]}; + + sample_detail(C, ss_co); + + RNA_int_set_array(op->ptr, "location", ss_co); + ED_area_headerprint(sa, NULL); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); + + return OPERATOR_FINISHED; + } + break; + + case RIGHTMOUSE: + { + ScrArea *sa = CTX_wm_area(C); + ED_area_headerprint(sa, NULL); + return OPERATOR_CANCELLED; + break; + } + } + + 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_and_dynamic_topology_constant_detail_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); +} + void ED_operatortypes_sculpt(void) { WM_operatortype_append(SCULPT_OT_brush_stroke); @@ -5288,4 +5424,5 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_optimize); WM_operatortype_append(SCULPT_OT_symmetrize); WM_operatortype_append(SCULPT_OT_detail_flood_fill); + WM_operatortype_append(SCULPT_OT_sample_detail_size); } diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 02b213a1b68..b9621b4753c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -857,7 +857,7 @@ typedef struct Sculpt { float gravity_factor; /* scale for constant detail size */ - float constant_detail_scale; + float constant_detail; 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 77cad9affb7..55301198031 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -414,15 +414,15 @@ static void rna_def_sculpt(BlenderRNA *brna) "Show diffuse color of object and overlay sculpt mask on top of it"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_ShowDiffuseColor_update"); - prop = RNA_def_property(srna, "detail_size", PROP_INT, PROP_NONE); + prop = RNA_def_property(srna, "detail_size", PROP_INT, PROP_PIXEL); 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"); + RNA_def_property_ui_text(prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)"); 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); + prop = RNA_def_property(srna, "constant_detail", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_range(prop, 0.001, 10000.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_ui_text(prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (as percentage of blender unit)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "use_smooth_shading", PROP_BOOLEAN, PROP_NONE); |