Sculpt: Sample Voxel Size

This adds support to the current sculpt sample detail operator to sample
a voxel size for the voxel remesher when sculpting on regular meshes.

It provides an approximation of a voxel size that is going to preserve
the sampled sculpt details after remeshing. It is not going to be 100%
accurate in all cases as it relies on having consistent edge length in
the whole sculpt, but it saves a lot of time and avoids guessing the
voxel size by trying different values.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D6251
This commit is contained in:
Pablo Dobarro 2019-11-26 18:53:38 +01:00
parent 9251b07720
commit 47645a8db6
Notes: blender-bot 2023-02-14 00:57:33 +01:00
Referenced by issue #72647, Sculpt mode crash - Sampling detail size (DYNTOPO) while having dyntopo toggled off crashes blender
2 changed files with 95 additions and 28 deletions

View File

@ -1193,7 +1193,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:
row = sub.row(align=True)
row.prop(sculpt, "constant_detail_resolution")
row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER')
props = row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER')
props.mode = 'DYNTOPO'
elif (sculpt.detail_type_method == 'BRUSH'):
sub.prop(sculpt, "detail_percent")
else:
@ -1252,7 +1253,10 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
col = layout.column()
mesh = context.active_object.data
col.prop(mesh, "remesh_voxel_size")
row = col.row(align=True)
row.prop(mesh, "remesh_voxel_size")
props = row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER')
props.mode = 'VOXEL'
col.prop(mesh, "remesh_voxel_adaptivity")
col.prop(mesh, "use_remesh_fix_poles")
col.prop(mesh, "use_remesh_smooth_normals")

View File

@ -8445,7 +8445,77 @@ static void SCULPT_OT_detail_flood_fill(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static void sample_detail(bContext *C, int mx, int my)
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 *ar, 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 - ar->winrct.xmin, my - ar->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 void sample_detail(bContext *C, int mx, int my, int mode)
{
/* Find 3D view to pick from. */
bScreen *screen = CTX_wm_screen(C);
@ -8466,28 +8536,13 @@ static void sample_detail(bContext *C, int mx, int my)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
/* Pick sample detail. */
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 - ar->winrct.xmin, my - ar->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));
switch (mode) {
case SAMPLE_DETAIL_DYNTOPO:
sample_detail_dyntopo(C, &vc, ar, mx, my);
break;
case SAMPLE_DETAIL_VOXEL:
sample_detail_voxel(C, &vc, mx, my);
break;
}
/* Restore context. */
@ -8499,7 +8554,8 @@ 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[0], ss_co[1]);
int mode = RNA_enum_get(op->ptr, "mode");
sample_detail(C, ss_co[0], ss_co[1], mode);
return OPERATOR_FINISHED;
}
@ -8518,7 +8574,8 @@ static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wm
if (event->val == KM_PRESS) {
int ss_co[2] = {event->x, event->y};
sample_detail(C, ss_co[0], ss_co[1]);
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));
@ -8551,7 +8608,7 @@ static void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
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_constant_or_manual_detail_poll;
ot->poll = sculpt_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@ -8565,6 +8622,12 @@ static void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
"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