Sculpt: various bugfixes

* BMLog now saves face material indices
* Fixed id corruption in join mesh edge case.
* The mesh->bmesh conversion function now checks
  if IDs are corrupted in a bit smarter way:
  + Any id that's greater then 5 times the sum of
    total elements with ids is assumed to be corrupt.
    Avoids very large allocations for the fast element->id
    map, which is a simple lookup table.
  + Alternative is to automatically switch to the slower
    GHash id->element map.
  + Sculpt code would have to detect this case and regenerate
    IDs?
* The slide relax brush is now internally split into two
  seperate commands.
* Basic smoothing now uses fewer iterations, velocity smooth
  is used to speed up convergence.
This commit is contained in:
Joseph Eagar 2021-10-28 10:50:38 -07:00
parent 6aa14c3433
commit b701cd63ad
9 changed files with 968 additions and 685 deletions

View File

@ -1699,11 +1699,11 @@ void BKE_builtin_apply_hard_edge_mode(BrushChannelSet *chset, bool do_apply)
}
}
void BKE_builtin_commandlist_create(Brush *brush,
BrushChannelSet *chset,
BrushCommandList *cl,
int tool,
BrushMappingData *mapdata)
ATTR_NO_OPT void BKE_builtin_commandlist_create(Brush *brush,
BrushChannelSet *chset,
BrushCommandList *cl,
int tool,
BrushMappingData *mapdata)
{
BrushCommand *cmd;
BrushChannel *ch;
@ -1736,7 +1736,7 @@ void BKE_builtin_commandlist_create(Brush *brush,
float autosmooth_projection = BKE_brush_channelset_get_float(
chset, "autosmooth_projection", NULL);
bool is_cloth = tool = SCULPT_TOOL_CLOTH;
bool is_cloth = tool == SCULPT_TOOL_CLOTH;
is_cloth = is_cloth ||
(ELEM(tool, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_POSE) &&
BRUSHSET_GET_INT(chset, deform_target, NULL) == BRUSH_DEFORM_TARGET_CLOTH_SIM);
@ -1761,7 +1761,9 @@ void BKE_builtin_commandlist_create(Brush *brush,
float autosmooth = BKE_brush_channelset_get_float(chset, "autosmooth", NULL);
if (!no_autosmooth && autosmooth > 0.0f) {
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true), SCULPT_TOOL_SMOOTH);
int smooth_tool = tool != SCULPT_TOOL_SLIDE_RELAX ? SCULPT_TOOL_SMOOTH : SCULPT_TOOL_RELAX;
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true), smooth_tool);
BKE_builtin_apply_hard_edge_mode(cmd->params, hard_edge_mode);
BKE_brush_commandset_inherit_all_mappings(cmd->params);

View File

@ -464,7 +464,8 @@ typedef struct {
void *customdata_f;
char hflag;
size_t len;
uint len;
short mat_nr;
void *customdata_res[MAX_FACE_RESERVED];
uint v_ids_res[MAX_FACE_RESERVED];
@ -844,6 +845,7 @@ static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f)
lf->len = (size_t)f->len;
lf->id = (uint)BM_ELEM_GET_ID(log->bm, f);
lf->mat_nr = f->mat_nr;
SET_TRACE(lf);
@ -1379,7 +1381,9 @@ static void bm_log_faces_restore(
BMFace *f = BM_face_create_verts(
bm, vs_tmp, (int)BLI_array_len(vs_tmp), NULL, BM_CREATE_SKIP_ID, true);
f->head.hflag = lf->hflag;
f->mat_nr = lf->mat_nr;
copy_v3_v3(f->no, lf->no);
@ -1515,6 +1519,7 @@ static void bm_log_face_values_swap(BMLog *log,
swap_v3_v3(f->no, lf->no);
SWAP(char, f->head.hflag, lf->hflag);
SWAP(short, f->mat_nr, lf->mat_nr);
void *old_cdata = NULL;

View File

@ -280,9 +280,9 @@ void BM_enter_multires_space(Object *ob, BMesh *bm, int space)
*/
void BM_mesh_bm_from_me(Object *ob,
BMesh *bm,
const Mesh *me,
const struct BMeshFromMeshParams *params)
BMesh *bm,
const Mesh *me,
const struct BMeshFromMeshParams *params)
{
const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer ||
bm->pdata.totlayer || bm->ldata.totlayer));
@ -500,18 +500,30 @@ void BM_mesh_bm_from_me(Object *ob,
BM_mesh_cd_flag_apply(bm, me->cd_flag);
}
#define IS_GARBAGE_ID(id) ((id) < 0 || (id) > id_garbage_threshold)
int *existing_id_layers[4] = {NULL, NULL, NULL, NULL};
/* threshold to detect garbage IDs, number of elements with ids multiplied by 5 */
int id_garbage_threshold = 0;
int use_exist_ids = 0;
int has_ids = bm->idmap.flag & BM_HAS_IDS ?
(bm->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)) :
0;
if (bm->idmap.flag & BM_HAS_IDS) {
int tots[4] = {me->totvert + bm->totvert,
me->totedge + bm->totedge,
me->totloop + bm->totloop,
me->totpoly + bm->totface};
if (!params->ignore_id_layers) {
for (int i = 0; i < 4; i++) {
existing_id_layers[i] = (int *)CustomData_get_layer(cdatas[i], CD_MESH_ID);
if (existing_id_layers[i]) {
id_garbage_threshold += tots[i];
use_exist_ids |= 1 << i;
}
}
@ -522,6 +534,8 @@ void BM_mesh_bm_from_me(Object *ob,
bm_init_idmap_cdlayers(bm);
}
id_garbage_threshold *= 5;
const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
@ -560,7 +574,7 @@ void BM_mesh_bm_from_me(Object *ob,
bm_elem_check_toolflags(bm, (BMElem *)v);
if (has_ids & BM_VERT) {
if (use_exist_ids & BM_VERT) {
if ((use_exist_ids & BM_VERT) && !IS_GARBAGE_ID(existing_id_layers[0][i])) {
bm_assign_id(bm, (BMElem *)v, existing_id_layers[0][i], false);
}
else {
@ -611,7 +625,7 @@ void BM_mesh_bm_from_me(Object *ob,
bm_elem_check_toolflags(bm, (BMElem *)e);
if (has_ids & BM_EDGE) {
if (use_exist_ids & BM_EDGE) {
if ((use_exist_ids & BM_EDGE) && !IS_GARBAGE_ID(existing_id_layers[1][i])) {
bm_assign_id(bm, (BMElem *)e, existing_id_layers[1][i], false);
}
else {
@ -682,7 +696,7 @@ void BM_mesh_bm_from_me(Object *ob,
CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true);
if (has_ids & BM_LOOP) {
if (use_exist_ids & BM_LOOP) {
if ((use_exist_ids & BM_LOOP) && !IS_GARBAGE_ID(existing_id_layers[2][j - 1])) {
bm_assign_id(bm, (BMElem *)l_iter, existing_id_layers[2][j - 1], false);
}
else {
@ -697,7 +711,7 @@ void BM_mesh_bm_from_me(Object *ob,
bm_elem_check_toolflags(bm, (BMElem *)f);
if (has_ids & BM_FACE) {
if (use_exist_ids & BM_FACE) {
if ((use_exist_ids & BM_FACE) && !IS_GARBAGE_ID(existing_id_layers[3][i])) {
bm_assign_id(bm, (BMElem *)f, existing_id_layers[3][i], false);
}
else {

View File

@ -77,6 +77,134 @@
/* join selected meshes into the active mesh, context sensitive
* return 0 if no join is made (error) and 1 if the join is done */
static void get_id_range(Mesh *mesh,
CustomData *vdata,
CustomData *edata,
CustomData *ldata,
CustomData *pdata,
int totvert,
int totedge,
int totloop,
int totpoly,
int *r_min,
int *r_max)
{
const CustomData *datas[4] = {vdata, edata, ldata, pdata};
int tots[4] = {totvert, totedge, totloop, totpoly};
int min_id = 0, max_id = 0;
for (int i = 0; i < 4; i++) {
int *ids = CustomData_get_layer(datas[i], CD_MESH_ID);
if (!ids) {
continue;
}
if (!tots[i]) {
continue;
}
if (i == 0) {
min_id = max_id = *ids;
}
else {
min_id = MIN2(min_id, *ids);
max_id = MAX2(max_id, *ids);
}
ids++;
for (int j = 1; j < tots[i]; j++, ids++) {
min_id = MIN2(min_id, *ids);
max_id = MAX2(max_id, *ids);
}
}
*r_min = min_id;
*r_max = max_id;
}
static void handle_missing_id_layers(Mesh *src,
Mesh *dst,
CustomData *vdata,
CustomData *edata,
CustomData *ldata,
CustomData *pdata,
int totvert,
int totedge,
int totloop,
int totpoly)
{
const CustomData *src_datas[4] = {&src->vdata, &src->edata, &src->ldata, &src->pdata};
const CustomData *dst_datas[4] = {vdata, edata, ldata, pdata};
int srctots[4] = {src->totvert, src->totedge, src->totloop, src->totpoly};
int dsttots[4] = {totvert, totedge, totloop, totpoly};
// find starting max id in dst
int dst_range[2], src_range[2];
get_id_range(src,
&src->vdata,
&src->edata,
&src->ldata,
&src->pdata,
src->totvert,
src->totedge,
src->totloop,
src->totpoly,
src_range,
src_range + 1);
get_id_range(dst,
vdata,
edata,
ldata,
pdata,
totvert,
totedge,
totloop,
totpoly,
dst_range,
dst_range + 1);
for (int i = 0; i < 4; i++) {
const CustomData *srcdata = src_datas[i];
const CustomData *dstdata = dst_datas[i];
const bool haveid_src = CustomData_has_layer(srcdata, CD_MESH_ID);
const bool haveid_dst = CustomData_has_layer(dstdata, CD_MESH_ID);
if (haveid_dst && haveid_src) {
// assign ids
int offset = dst_range[1] - src_range[0] + 1;
int *srcids = CustomData_get_layer(srcdata, CD_MESH_ID);
int *dstids = CustomData_get_layer(dstdata, CD_MESH_ID);
int start = dsttots[i];
int end = start + srctots[i];
// offset ids
dstids += start;
for (int i = start; i < end; i++, dstids++, srcids++) {
*dstids = (*srcids) + offset;
}
}
else if (haveid_dst) {
int curid = dst_range[1] + 1;
int *dstids = CustomData_get_layer(dstdata, CD_MESH_ID);
int start = dsttots[i];
int end = start + srctots[i];
dstids += start;
// assign new ids
for (int i = start; i < end; i++, dstids++) {
*dstids = curid++;
}
}
}
}
static void join_mesh_single(Depsgraph *depsgraph,
Main *bmain,
Scene *scene,
@ -286,6 +414,17 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
}
handle_missing_id_layers(me,
(Mesh *)ob_dst->data,
vdata,
edata,
ldata,
pdata,
*vertofs,
*edgeofs,
*loopofs,
*polyofs);
/* these are used for relinking (cannot be set earlier, or else reattaching goes wrong) */
*vertofs += me->totvert;
*mvert_pp += me->totvert;

View File

@ -1045,6 +1045,10 @@ char SCULPT_mesh_symmetry_xyz_get(Object *object)
int SCULPT_active_face_set_get(SculptSession *ss)
{
if (ss->active_face_index.i == SCULPT_REF_NONE) {
return SCULPT_FACE_SET_NONE;
}
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
return ss->face_sets[ss->active_face_index.i];
@ -4072,6 +4076,7 @@ static float brush_strength(const Sculpt *sd,
}
case SCULPT_TOOL_DRAW_FACE_SETS:
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_RELAX:
case SCULPT_TOOL_SLIDE_RELAX:
return alpha * pressure * overlap * feather * 2.0f;
case SCULPT_TOOL_PAINT:
@ -5186,6 +5191,8 @@ bool brush_uses_commandlist(Brush *brush)
case SCULPT_TOOL_PINCH:
case SCULPT_TOOL_SIMPLIFY:
case SCULPT_TOOL_SNAKE_HOOK:
case SCULPT_TOOL_SLIDE_RELAX:
case SCULPT_TOOL_RELAX:
case SCULPT_TOOL_INFLATE:
case SCULPT_TOOL_PAINT:
case SCULPT_TOOL_SMEAR:
@ -5485,7 +5492,10 @@ void do_brush_action(
SCULPT_do_elastic_deform_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SLIDE_RELAX:
SCULPT_do_slide_relax_brush(sd, ob, nodes, totnode);
SCULPT_do_slide_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_RELAX:
SCULPT_do_relax_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_BOUNDARY:
SCULPT_do_boundary_brush(sd, ob, nodes, totnode);
@ -5988,7 +5998,10 @@ static void SCULPT_run_command(
SCULPT_do_elastic_deform_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SLIDE_RELAX:
SCULPT_do_slide_relax_brush(sd, ob, nodes, totnode);
SCULPT_do_slide_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_RELAX:
SCULPT_do_relax_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_BOUNDARY:
SCULPT_do_boundary_brush(sd, ob, nodes, totnode);
@ -6834,6 +6847,8 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "DynTopo";
case SCULPT_TOOL_AUTO_FSET:
return "Auto Face Set";
case SCULPT_TOOL_RELAX:
return "Relax";
}
return "Sculpting";
@ -7057,6 +7072,7 @@ static void sculpt_update_cache_invariants(
}
else if (ELEM(brush->sculpt_tool,
SCULPT_TOOL_SLIDE_RELAX,
SCULPT_TOOL_RELAX,
SCULPT_TOOL_DRAW_FACE_SETS,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_SMEAR)) {
@ -8536,9 +8552,14 @@ void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerR
}
BrushCommandList *list = ss->cache->commandlist = BKE_brush_commandlist_create();
int tool = brush->sculpt_tool;
if (tool == SCULPT_TOOL_SLIDE_RELAX && ss->cache->alt_smooth) {
tool = SCULPT_TOOL_RELAX;
}
BKE_builtin_commandlist_create(
brush, ss->cache->channels_final, list, brush->sculpt_tool, &ss->cache->input_mapping);
brush, ss->cache->channels_final, list, tool, &ss->cache->input_mapping);
}
SCULPT_run_commandlist(sd, ob, brush, ss->cache->commandlist, ups);
@ -8657,6 +8678,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
}
else if (ELEM(brush->sculpt_tool,
SCULPT_TOOL_SLIDE_RELAX,
SCULPT_TOOL_RELAX,
SCULPT_TOOL_DRAW_FACE_SETS,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_SMEAR)) {

View File

@ -3218,6 +3218,8 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
const bool do_reproject = SCULPT_need_reproject;
const int boundflag = SCULPT_BOUNDARY_ALL;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
@ -3239,7 +3241,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
copy_v3_v3(oldco, vd.co);
SCULPT_vertex_normal_get(ss, vd.vertex, oldno);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, SCULPT_BOUNDARY_DEFAULT, vd.co);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, boundflag, vd.co);
if (do_reproject) {
SCULPT_reproject_cdata(ss, vd.vertex, oldco, oldno);
@ -3252,7 +3254,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
void SCULPT_do_slide_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -3272,14 +3274,32 @@ void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
if (ss->cache->alt_smooth) {
SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
}
BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings);
}
void SCULPT_do_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
return;
}
else {
BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings);
BKE_curvemapping_init(brush->curve);
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
}
}

View File

@ -422,6 +422,8 @@ typedef struct SculptSmoothArgs {
bool do_weighted_smooth : 1;
bool preserve_fset_boundaries : 1;
float bound_smooth_radius; // if 0, ss->cache->radius will be used
float vel_smooth_fac;
SculptCustomLayer *vel_scl;
} SculptSmoothArgs;
/* Utils. */
@ -1095,6 +1097,7 @@ typedef struct SculptThreadedTaskData {
float crease_pinch_factor;
bool use_curvature;
float vel_smooth_fac;
int iterations;
} SculptThreadedTaskData;
/*************** Brush testing declarations ****************/
@ -2140,10 +2143,11 @@ void SCULPT_do_scene_project_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,
int totnode);
void SCULPT_do_slide_relax_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,
int totnode);
void SCULPT_do_slide_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,
int totnode);
void SCULPT_do_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
void SCULPT_do_fairing_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,

File diff suppressed because it is too large Load Diff

View File

@ -508,7 +508,8 @@ typedef enum eBrushSculptTool {
SCULPT_TOOL_TOPOLOGY_RAKE = 40,
SCULPT_TOOL_DYNTOPO = 41,
SCULPT_TOOL_AUTO_FSET = 42
SCULPT_TOOL_AUTO_FSET = 42,
SCULPT_TOOL_RELAX = 43
} eBrushSculptTool;
/* Brush.uv_sculpt_tool */
@ -547,7 +548,6 @@ typedef enum eBrushUVSculptTool {
SCULPT_TOOL_CLOTH, \
SCULPT_TOOL_DISPLACEMENT_ERASER, \
SCULPT_TOOL_FAIRING, \
SCULPT_TOOL_SLIDE_RELAX, \
SCULPT_TOOL_ELASTIC_DEFORM, \
SCULPT_TOOL_BOUNDARY, \
SCULPT_TOOL_POSE, \