Detail sampling operator
authorAntony Riakiotakis <kalast@gmail.com>
Sat, 22 Mar 2014 21:35:07 +0000 (23:35 +0200)
committerAntony Riakiotakis <kalast@gmail.com>
Sat, 22 Mar 2014 21:35:41 +0000 (23:35 +0200)
Located on topology panel.

To use just click on button and click on mesh.
Operator will just use the dimensions of the triangles below to set the
constant detail setting.

Also changed pair of scale/detail size with nice separate float
percentage value.

release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/BKE_pbvh.h
source/blender/blenkernel/intern/pbvh_bmesh.c
source/blender/blenkernel/intern/pbvh_intern.h
source/blender/editors/sculpt_paint/sculpt.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_sculpt_paint.c

index 4dc44342fbec970ad485801846b3e80ebdc346f1..b67c8effa3f1c03277e56e88e01cde5e8236a478 100644 (file)
@@ -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")
index 0828ea54280256c1e266e608386037f1acada4fc..3601ff1ce0427c733c49f587139cf8f5710bc63c 100644 (file)
@@ -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],
index 3fae446f1f4e9130dc2ad53ce937d57cc16784f0..808b3faad9b96cd9f32f0d5a5e53729b6cc2a422 100644 (file)
@@ -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)
 {
index d4cd6bcf9d885aa8691ed9f82dc7cd8e67a9636e..75d2a8333a1cdcd5a7101cdc52859c5f9465ebfc 100644 (file)
@@ -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
index c6f8007d57e1cae9f51ddb8afdee99cc6ad65af4..9ef5852555f14cb7b3597af791ed013142671905 100644 (file)
@@ -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);
 }
index 02b213a1b683a677b913cd8c2265105ca8b5d9ed..b9621b4753c47f4df8fbe2f42d1b873ac4adb545 100644 (file)
@@ -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;
index 77cad9affb72d53662a17ada9f809e6d65bd03d7..55301198031ef3353ae6b942edb078b41cd53487 100644 (file)
@@ -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);