Cleanup: Move all Face Set functionality to its own file
This commit is contained in:
@@ -60,6 +60,7 @@ set(SRC
|
|||||||
paint_vertex_weight_utils.c
|
paint_vertex_weight_utils.c
|
||||||
sculpt.c
|
sculpt.c
|
||||||
sculpt_cloth.c
|
sculpt_cloth.c
|
||||||
|
sculpt_face_set.c
|
||||||
sculpt_multiplane_scrape.c
|
sculpt_multiplane_scrape.c
|
||||||
sculpt_pose.c
|
sculpt_pose.c
|
||||||
sculpt_undo.c
|
sculpt_undo.c
|
||||||
|
@@ -216,7 +216,7 @@ static void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3])
|
|||||||
|
|
||||||
/* Sculpt Face Sets and Visibility. */
|
/* Sculpt Face Sets and Visibility. */
|
||||||
|
|
||||||
static int SCULPT_active_face_set_get(SculptSession *ss)
|
int SCULPT_active_face_set_get(SculptSession *ss)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -232,7 +232,7 @@ static int SCULPT_active_face_set_get(SculptSession *ss)
|
|||||||
return SCULPT_FACE_SET_NONE;
|
return SCULPT_FACE_SET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
|
void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -247,7 +247,7 @@ static void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
|
bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -260,7 +260,7 @@ static bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
|
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -281,7 +281,7 @@ static void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SCULPT_face_sets_visibility_invert(SculptSession *ss)
|
void SCULPT_face_sets_visibility_invert(SculptSession *ss)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -295,7 +295,7 @@ static void SCULPT_face_sets_visibility_invert(SculptSession *ss)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
|
void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -322,7 +322,7 @@ static void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
|
bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES: {
|
case PBVH_FACES: {
|
||||||
@@ -342,7 +342,7 @@ static bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index)
|
bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES: {
|
case PBVH_FACES: {
|
||||||
@@ -366,7 +366,7 @@ static bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
|
void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES: {
|
case PBVH_FACES: {
|
||||||
@@ -526,7 +526,7 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SCULPT_face_set_next_available_get(SculptSession *ss)
|
int SCULPT_face_set_next_available_get(SculptSession *ss)
|
||||||
{
|
{
|
||||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||||
case PBVH_FACES:
|
case PBVH_FACES:
|
||||||
@@ -3676,155 +3676,6 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
|
|||||||
BKE_pbvh_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
|
BKE_pbvh_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
|
|
||||||
const int n,
|
|
||||||
const TaskParallelTLS *__restrict tls)
|
|
||||||
{
|
|
||||||
SculptThreadedTaskData *data = userdata;
|
|
||||||
SculptSession *ss = data->ob->sculpt;
|
|
||||||
const Brush *brush = data->brush;
|
|
||||||
const float bstrength = ss->cache->bstrength;
|
|
||||||
|
|
||||||
PBVHVertexIter vd;
|
|
||||||
|
|
||||||
SculptBrushTest test;
|
|
||||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
|
||||||
ss, &test, data->brush->falloff_shape);
|
|
||||||
|
|
||||||
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
||||||
{
|
|
||||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
|
|
||||||
MeshElemMap *vert_map = &ss->pmap[vd.index];
|
|
||||||
for (int j = 0; j < ss->pmap[vd.index].count; j++) {
|
|
||||||
const MPoly *p = &ss->mpoly[vert_map->indices[j]];
|
|
||||||
|
|
||||||
float poly_center[3];
|
|
||||||
BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], ss->mvert, poly_center);
|
|
||||||
|
|
||||||
if (sculpt_brush_test_sq_fn(&test, poly_center)) {
|
|
||||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
|
||||||
brush,
|
|
||||||
vd.co,
|
|
||||||
sqrtf(test.dist),
|
|
||||||
vd.no,
|
|
||||||
vd.fno,
|
|
||||||
vd.mask ? *vd.mask : 0.0f,
|
|
||||||
vd.index,
|
|
||||||
tls->thread_id);
|
|
||||||
|
|
||||||
if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
|
|
||||||
ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
|
||||||
{
|
|
||||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
|
||||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
|
||||||
brush,
|
|
||||||
vd.co,
|
|
||||||
sqrtf(test.dist),
|
|
||||||
vd.no,
|
|
||||||
vd.fno,
|
|
||||||
vd.mask ? *vd.mask : 0.0f,
|
|
||||||
vd.index,
|
|
||||||
tls->thread_id);
|
|
||||||
|
|
||||||
if (fade > 0.05f) {
|
|
||||||
SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BKE_pbvh_vertex_iter_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
|
|
||||||
const int n,
|
|
||||||
const TaskParallelTLS *__restrict tls)
|
|
||||||
{
|
|
||||||
SculptThreadedTaskData *data = userdata;
|
|
||||||
SculptSession *ss = data->ob->sculpt;
|
|
||||||
const Brush *brush = data->brush;
|
|
||||||
float bstrength = ss->cache->bstrength;
|
|
||||||
|
|
||||||
PBVHVertexIter vd;
|
|
||||||
|
|
||||||
SculptBrushTest test;
|
|
||||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
|
||||||
ss, &test, data->brush->falloff_shape);
|
|
||||||
|
|
||||||
const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
|
|
||||||
/* This operations needs a stregth tweak as the relax deformation is too weak by default. */
|
|
||||||
if (relax_face_sets) {
|
|
||||||
bstrength *= 2.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
||||||
{
|
|
||||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
|
||||||
if (relax_face_sets != SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
|
|
||||||
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
|
||||||
brush,
|
|
||||||
vd.co,
|
|
||||||
sqrtf(test.dist),
|
|
||||||
vd.no,
|
|
||||||
vd.fno,
|
|
||||||
vd.mask ? *vd.mask : 0.0f,
|
|
||||||
vd.index,
|
|
||||||
tls->thread_id);
|
|
||||||
|
|
||||||
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
|
|
||||||
if (vd.mvert) {
|
|
||||||
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BKE_pbvh_vertex_iter_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
|
|
||||||
{
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
|
||||||
|
|
||||||
if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0 &&
|
|
||||||
ss->cache->radial_symmetry_pass == 0) {
|
|
||||||
if (ss->cache->invert) {
|
|
||||||
/* When inverting the brush, pick the paint face mask ID from the mesh. */
|
|
||||||
ss->cache->paint_face_set = SCULPT_active_face_set_get(ss);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* By default create a new Face Sets. */
|
|
||||||
ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_curvemapping_initialize(brush->curve);
|
|
||||||
|
|
||||||
/* Threaded loop over nodes. */
|
|
||||||
SculptThreadedTaskData data = {
|
|
||||||
.sd = sd,
|
|
||||||
.ob = ob,
|
|
||||||
.brush = brush,
|
|
||||||
.nodes = nodes,
|
|
||||||
};
|
|
||||||
|
|
||||||
PBVHParallelSettings settings;
|
|
||||||
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
|
|
||||||
if (ss->cache->alt_smooth) {
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
BKE_pbvh_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
BKE_pbvh_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
|
static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
|
||||||
const int n,
|
const int n,
|
||||||
@@ -6405,7 +6256,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
|
|||||||
SCULPT_do_cloth_brush(sd, ob, nodes, totnode);
|
SCULPT_do_cloth_brush(sd, ob, nodes, totnode);
|
||||||
break;
|
break;
|
||||||
case SCULPT_TOOL_DRAW_FACE_SETS:
|
case SCULPT_TOOL_DRAW_FACE_SETS:
|
||||||
do_draw_face_sets_brush(sd, ob, nodes, totnode);
|
SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11259,753 +11110,6 @@ static void SCULPT_OT_set_pivot_position(wmOperatorType *ot)
|
|||||||
10000.0f);
|
10000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum eSculptFaceGroupsCreateModes {
|
|
||||||
SCULPT_FACE_SET_MASKED = 0,
|
|
||||||
SCULPT_FACE_SET_VISIBLE = 1,
|
|
||||||
SCULPT_FACE_SET_ALL = 2,
|
|
||||||
SCULPT_FACE_SET_SELECTION = 3,
|
|
||||||
} eSculptFaceGroupsCreateModes;
|
|
||||||
|
|
||||||
static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_MASKED,
|
|
||||||
"MASKED",
|
|
||||||
0,
|
|
||||||
"Face Set From Masked",
|
|
||||||
"Create a new Face Set from the masked faces",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_VISIBLE,
|
|
||||||
"VISIBLE",
|
|
||||||
0,
|
|
||||||
"Face Set From Visible",
|
|
||||||
"Create a new Face Set from the visible vertices",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_ALL,
|
|
||||||
"ALL",
|
|
||||||
0,
|
|
||||||
"Face Set Full Mesh",
|
|
||||||
"Create an unique Face Set with all faces in the sculpt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_SELECTION,
|
|
||||||
"SELECTION",
|
|
||||||
0,
|
|
||||||
"Face Set From Edit Mode Selection",
|
|
||||||
"Create an Face Set corresponding to the Edit Mode face selection",
|
|
||||||
},
|
|
||||||
{0, NULL, 0, NULL, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static int sculpt_face_set_create_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
|
||||||
{
|
|
||||||
Object *ob = CTX_data_active_object(C);
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
ARegion *region = CTX_wm_region(C);
|
|
||||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
||||||
|
|
||||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
|
||||||
|
|
||||||
/* Dyntopo not suported. */
|
|
||||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
|
|
||||||
|
|
||||||
const int tot_vert = SCULPT_vertex_count_get(ss);
|
|
||||||
float threshold = 0.5f;
|
|
||||||
|
|
||||||
PBVH *pbvh = ob->sculpt->pbvh;
|
|
||||||
PBVHNode **nodes;
|
|
||||||
int totnode;
|
|
||||||
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
|
||||||
|
|
||||||
if (!nodes) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCULPT_undo_push_begin("face set change");
|
|
||||||
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
|
||||||
|
|
||||||
const int next_face_set = SCULPT_face_set_next_available_get(ss);
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_MASKED) {
|
|
||||||
for (int i = 0; i < tot_vert; i++) {
|
|
||||||
if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
|
|
||||||
SCULPT_vertex_face_set_set(ss, i, next_face_set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_VISIBLE) {
|
|
||||||
for (int i = 0; i < tot_vert; i++) {
|
|
||||||
if (SCULPT_vertex_visible_get(ss, i)) {
|
|
||||||
SCULPT_vertex_face_set_set(ss, i, next_face_set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_ALL) {
|
|
||||||
for (int i = 0; i < tot_vert; i++) {
|
|
||||||
SCULPT_vertex_face_set_set(ss, i, next_face_set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_SELECTION) {
|
|
||||||
Mesh *mesh = ob->data;
|
|
||||||
BMesh *bm;
|
|
||||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
|
||||||
bm = BM_mesh_create(&allocsize,
|
|
||||||
&((struct BMeshCreateParams){
|
|
||||||
.use_toolflags = true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
BM_mesh_bm_from_me(bm,
|
|
||||||
mesh,
|
|
||||||
(&(struct BMeshFromMeshParams){
|
|
||||||
.calc_face_normal = true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
BMIter iter;
|
|
||||||
BMFace *f;
|
|
||||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
|
||||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
|
||||||
ss->face_sets[BM_elem_index_get(f)] = next_face_set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BM_mesh_free(bm);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < totnode; i++) {
|
|
||||||
BKE_pbvh_node_mark_redraw(nodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
MEM_SAFE_FREE(nodes);
|
|
||||||
|
|
||||||
SCULPT_undo_push_end();
|
|
||||||
|
|
||||||
ED_region_tag_redraw(region);
|
|
||||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SCULPT_OT_face_sets_create(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Create Face Set";
|
|
||||||
ot->idname = "SCULPT_OT_face_sets_create";
|
|
||||||
ot->description = "Create a new Face Set";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->invoke = sculpt_face_set_create_invoke;
|
|
||||||
ot->poll = SCULPT_mode_poll;
|
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
|
|
||||||
RNA_def_enum(
|
|
||||||
ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum eSculptFaceSetsInitMode {
|
|
||||||
SCULPT_FACE_SETS_FROM_LOOSE_PARTS = 0,
|
|
||||||
SCULPT_FACE_SETS_FROM_MATERIALS = 1,
|
|
||||||
SCULPT_FACE_SETS_FROM_NORMALS = 2,
|
|
||||||
SCULPT_FACE_SETS_FROM_UV_SEAMS = 3,
|
|
||||||
SCULPT_FACE_SETS_FROM_CREASES = 4,
|
|
||||||
SCULPT_FACE_SETS_FROM_SHARP_EDGES = 5,
|
|
||||||
SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT = 6,
|
|
||||||
SCULPT_FACE_SETS_FROM_FACE_MAPS = 7,
|
|
||||||
} eSculptFaceSetsInitMode;
|
|
||||||
|
|
||||||
static EnumPropertyItem prop_sculpt_face_sets_init_types[] = {
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_LOOSE_PARTS,
|
|
||||||
"LOOSE_PARTS",
|
|
||||||
0,
|
|
||||||
"Face Sets From Loose Parts",
|
|
||||||
"Create a Face Set per loose part in the mesh",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_MATERIALS,
|
|
||||||
"MATERIALS",
|
|
||||||
0,
|
|
||||||
"Face Sets From Material Slots",
|
|
||||||
"Create a Face Set per Material Slot",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_NORMALS,
|
|
||||||
"NORMALS",
|
|
||||||
0,
|
|
||||||
"Face Sets From Mesh Normals",
|
|
||||||
"Create Face Sets for Faces that have similar normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_UV_SEAMS,
|
|
||||||
"UV_SEAMS",
|
|
||||||
0,
|
|
||||||
"Face Sets From UV Seams",
|
|
||||||
"Create Face Sets using UV Seams as boundaries",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_CREASES,
|
|
||||||
"CREASES",
|
|
||||||
0,
|
|
||||||
"Face Sets From Edge Creases",
|
|
||||||
"Create Face Sets using Edge Creases as boundaries",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT,
|
|
||||||
"BEVEL_WEIGHT",
|
|
||||||
0,
|
|
||||||
"Face Sets From Bevel Weight",
|
|
||||||
"Create Face Sets using Bevel Weights as boundaries",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_SHARP_EDGES,
|
|
||||||
"SHARP_EDGES",
|
|
||||||
0,
|
|
||||||
"Face Sets From Sharp Edges",
|
|
||||||
"Create Face Sets using Sharp Edges as boundaries",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SETS_FROM_FACE_MAPS,
|
|
||||||
"FACE_MAPS",
|
|
||||||
0,
|
|
||||||
"Face Sets From Face Maps",
|
|
||||||
"Create a Face Set per Face Map",
|
|
||||||
},
|
|
||||||
{0, NULL, 0, NULL, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef bool (*face_sets_flood_fill_test)(
|
|
||||||
BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold);
|
|
||||||
|
|
||||||
static bool sculpt_face_sets_init_loose_parts_test(BMesh *UNUSED(bm),
|
|
||||||
BMFace *UNUSED(from_f),
|
|
||||||
BMEdge *UNUSED(from_e),
|
|
||||||
BMFace *UNUSED(to_f),
|
|
||||||
const float UNUSED(threshold))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sculpt_face_sets_init_normals_test(
|
|
||||||
BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold)
|
|
||||||
{
|
|
||||||
return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sculpt_face_sets_init_uv_seams_test(BMesh *UNUSED(bm),
|
|
||||||
BMFace *UNUSED(from_f),
|
|
||||||
BMEdge *from_e,
|
|
||||||
BMFace *UNUSED(to_f),
|
|
||||||
const float UNUSED(threshold))
|
|
||||||
{
|
|
||||||
return !BM_elem_flag_test(from_e, BM_ELEM_SEAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sculpt_face_sets_init_crease_test(
|
|
||||||
BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
|
|
||||||
{
|
|
||||||
return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sculpt_face_sets_init_bevel_weight_test(
|
|
||||||
BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
|
|
||||||
{
|
|
||||||
return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sculpt_face_sets_init_sharp_edges_test(BMesh *UNUSED(bm),
|
|
||||||
BMFace *UNUSED(from_f),
|
|
||||||
BMEdge *from_e,
|
|
||||||
BMFace *UNUSED(to_f),
|
|
||||||
const float UNUSED(threshold))
|
|
||||||
{
|
|
||||||
return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sculpt_face_sets_init_flood_fill(Object *ob,
|
|
||||||
face_sets_flood_fill_test test,
|
|
||||||
const float threshold)
|
|
||||||
{
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
Mesh *mesh = ob->data;
|
|
||||||
BMesh *bm;
|
|
||||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
|
||||||
bm = BM_mesh_create(&allocsize,
|
|
||||||
&((struct BMeshCreateParams){
|
|
||||||
.use_toolflags = true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
BM_mesh_bm_from_me(bm,
|
|
||||||
mesh,
|
|
||||||
(&(struct BMeshFromMeshParams){
|
|
||||||
.calc_face_normal = true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
bool *visited_faces = MEM_callocN(sizeof(bool) * mesh->totpoly, "visited faces");
|
|
||||||
const int totfaces = mesh->totpoly;
|
|
||||||
|
|
||||||
int *face_sets = ss->face_sets;
|
|
||||||
|
|
||||||
BM_mesh_elem_table_init(bm, BM_FACE);
|
|
||||||
BM_mesh_elem_table_ensure(bm, BM_FACE);
|
|
||||||
|
|
||||||
int next_face_set = 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < totfaces; i++) {
|
|
||||||
if (!visited_faces[i]) {
|
|
||||||
GSQueue *queue;
|
|
||||||
queue = BLI_gsqueue_new(sizeof(int));
|
|
||||||
|
|
||||||
face_sets[i] = next_face_set;
|
|
||||||
visited_faces[i] = true;
|
|
||||||
BLI_gsqueue_push(queue, &i);
|
|
||||||
|
|
||||||
while (!BLI_gsqueue_is_empty(queue)) {
|
|
||||||
int from_f;
|
|
||||||
BLI_gsqueue_pop(queue, &from_f);
|
|
||||||
|
|
||||||
BMFace *f, *f_neighbor;
|
|
||||||
BMEdge *ed;
|
|
||||||
BMIter iter_a, iter_b;
|
|
||||||
|
|
||||||
f = BM_face_at_index(bm, from_f);
|
|
||||||
|
|
||||||
BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) {
|
|
||||||
BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) {
|
|
||||||
if (f_neighbor != f) {
|
|
||||||
int neighbor_face_index = BM_elem_index_get(f_neighbor);
|
|
||||||
if (!visited_faces[neighbor_face_index]) {
|
|
||||||
if (test(bm, f, ed, f_neighbor, threshold)) {
|
|
||||||
face_sets[neighbor_face_index] = next_face_set;
|
|
||||||
visited_faces[neighbor_face_index] = true;
|
|
||||||
BLI_gsqueue_push(queue, &neighbor_face_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next_face_set += 1;
|
|
||||||
|
|
||||||
BLI_gsqueue_free(queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MEM_SAFE_FREE(visited_faces);
|
|
||||||
|
|
||||||
BM_mesh_free(bm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sculpt_face_sets_init_loop(Object *ob, const int mode)
|
|
||||||
{
|
|
||||||
Mesh *mesh = ob->data;
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
BMesh *bm;
|
|
||||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
|
||||||
bm = BM_mesh_create(&allocsize,
|
|
||||||
&((struct BMeshCreateParams){
|
|
||||||
.use_toolflags = true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
BM_mesh_bm_from_me(bm,
|
|
||||||
mesh,
|
|
||||||
(&(struct BMeshFromMeshParams){
|
|
||||||
.calc_face_normal = true,
|
|
||||||
}));
|
|
||||||
BMIter iter;
|
|
||||||
BMFace *f;
|
|
||||||
|
|
||||||
const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
|
|
||||||
|
|
||||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
|
||||||
if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
|
|
||||||
ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
|
|
||||||
}
|
|
||||||
else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
|
|
||||||
if (cd_fmaps_offset != -1) {
|
|
||||||
ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ss->face_sets[BM_elem_index_get(f)] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BM_mesh_free(bm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sculpt_face_set_init_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
|
||||||
{
|
|
||||||
Object *ob = CTX_data_active_object(C);
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
ARegion *region = CTX_wm_region(C);
|
|
||||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
||||||
|
|
||||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
|
||||||
|
|
||||||
/* Dyntopo not supported. */
|
|
||||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
|
|
||||||
|
|
||||||
PBVH *pbvh = ob->sculpt->pbvh;
|
|
||||||
PBVHNode **nodes;
|
|
||||||
int totnode;
|
|
||||||
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
|
||||||
|
|
||||||
if (!nodes) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCULPT_undo_push_begin("face set change");
|
|
||||||
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
|
||||||
|
|
||||||
const float threshold = RNA_float_get(op->ptr, "threshold");
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case SCULPT_FACE_SETS_FROM_LOOSE_PARTS:
|
|
||||||
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_MATERIALS:
|
|
||||||
sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_NORMALS:
|
|
||||||
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_UV_SEAMS:
|
|
||||||
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_CREASES:
|
|
||||||
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_SHARP_EDGES:
|
|
||||||
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT:
|
|
||||||
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold);
|
|
||||||
break;
|
|
||||||
case SCULPT_FACE_SETS_FROM_FACE_MAPS:
|
|
||||||
sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCULPT_undo_push_end();
|
|
||||||
|
|
||||||
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
|
|
||||||
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
|
|
||||||
|
|
||||||
for (int i = 0; i < totnode; i++) {
|
|
||||||
BKE_pbvh_node_mark_update_visibility(nodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
|
|
||||||
|
|
||||||
MEM_SAFE_FREE(nodes);
|
|
||||||
|
|
||||||
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
|
|
||||||
BKE_mesh_flush_hidden_from_verts(ob->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ED_region_tag_redraw(region);
|
|
||||||
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
|
||||||
|
|
||||||
View3D *v3d = CTX_wm_view3d(C);
|
|
||||||
if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
|
|
||||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SCULPT_OT_face_sets_init(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Init Face Sets";
|
|
||||||
ot->idname = "SCULPT_OT_face_sets_init";
|
|
||||||
ot->description = "Initializes all Face Sets in the mesh";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->invoke = sculpt_face_set_init_invoke;
|
|
||||||
ot->poll = SCULPT_mode_poll;
|
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
|
|
||||||
RNA_def_enum(
|
|
||||||
ot->srna, "mode", prop_sculpt_face_sets_init_types, SCULPT_FACE_SET_MASKED, "Mode", "");
|
|
||||||
RNA_def_float(
|
|
||||||
ot->srna,
|
|
||||||
"threshold",
|
|
||||||
0.5f,
|
|
||||||
0.0f,
|
|
||||||
1.0f,
|
|
||||||
"Threshold",
|
|
||||||
"Minimum value to consider a certain atribute a boundary when creating the Face Sets",
|
|
||||||
0.0f,
|
|
||||||
1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum eSculptFaceGroupVisibilityModes {
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0,
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1,
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2,
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_INVERT = 3,
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4,
|
|
||||||
} eSculptFaceGroupVisibilityModes;
|
|
||||||
|
|
||||||
static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = {
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_TOGGLE,
|
|
||||||
"TOGGLE",
|
|
||||||
0,
|
|
||||||
"Toggle Visibility",
|
|
||||||
"Hide all Face Sets except for the active one",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE,
|
|
||||||
"SHOW_ACTIVE",
|
|
||||||
0,
|
|
||||||
"Show Active Face Set",
|
|
||||||
"Show Active Face Set",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE,
|
|
||||||
"HIDE_ACTIVE",
|
|
||||||
0,
|
|
||||||
"Hide Active Face Sets",
|
|
||||||
"Hide Active Face Sets",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_INVERT,
|
|
||||||
"INVERT",
|
|
||||||
0,
|
|
||||||
"Invert Face Set Visibility",
|
|
||||||
"Invert Face Set Visibility",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_SHOW_ALL,
|
|
||||||
"SHOW_ALL",
|
|
||||||
0,
|
|
||||||
"Show All Face Sets",
|
|
||||||
"Show All Face Sets",
|
|
||||||
},
|
|
||||||
{0, NULL, 0, NULL, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static int sculpt_face_sets_change_visibility_invoke(bContext *C,
|
|
||||||
wmOperator *op,
|
|
||||||
const wmEvent *UNUSED(event))
|
|
||||||
{
|
|
||||||
Object *ob = CTX_data_active_object(C);
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
ARegion *region = CTX_wm_region(C);
|
|
||||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
||||||
|
|
||||||
/* Dyntopo not supported. */
|
|
||||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
|
|
||||||
|
|
||||||
const int tot_vert = SCULPT_vertex_count_get(ss);
|
|
||||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
|
||||||
const int active_face_set = SCULPT_active_face_set_get(ss);
|
|
||||||
|
|
||||||
SCULPT_undo_push_begin("Hide area");
|
|
||||||
|
|
||||||
PBVH *pbvh = ob->sculpt->pbvh;
|
|
||||||
PBVHNode **nodes;
|
|
||||||
int totnode;
|
|
||||||
|
|
||||||
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
|
||||||
|
|
||||||
if (totnode == 0) {
|
|
||||||
MEM_SAFE_FREE(nodes);
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) {
|
|
||||||
bool hidden_vertex = false;
|
|
||||||
|
|
||||||
/* This can fail with regular meshes with non-manifold geometry as the visibility state can't
|
|
||||||
* be synced from face sets to non-manifold vertices. */
|
|
||||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
|
||||||
for (int i = 0; i < tot_vert; i++) {
|
|
||||||
if (!SCULPT_vertex_visible_get(ss, i)) {
|
|
||||||
hidden_vertex = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ss->totfaces; i++) {
|
|
||||||
if (ss->face_sets[i] <= 0) {
|
|
||||||
hidden_vertex = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hidden_vertex) {
|
|
||||||
SCULPT_face_sets_visibility_all_set(ss, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SCULPT_face_sets_visibility_all_set(ss, false);
|
|
||||||
SCULPT_face_set_visibility_set(ss, active_face_set, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
|
|
||||||
SCULPT_face_sets_visibility_all_set(ss, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
|
|
||||||
SCULPT_face_sets_visibility_all_set(ss, false);
|
|
||||||
SCULPT_face_set_visibility_set(ss, active_face_set, true);
|
|
||||||
for (int i = 0; i < tot_vert; i++) {
|
|
||||||
SCULPT_vertex_visible_set(ss,
|
|
||||||
i,
|
|
||||||
SCULPT_vertex_visible_get(ss, i) &&
|
|
||||||
SCULPT_vertex_has_face_set(ss, i, active_face_set));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
|
|
||||||
SCULPT_face_set_visibility_set(ss, active_face_set, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
|
|
||||||
SCULPT_face_sets_visibility_invert(ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For modes that use the cursor active vertex, update the rotation origin for viewport
|
|
||||||
* navigation. */
|
|
||||||
if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) {
|
|
||||||
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
|
|
||||||
float location[3];
|
|
||||||
copy_v3_v3(location, SCULPT_active_vertex_co_get(ss));
|
|
||||||
mul_m4_v3(ob->obmat, location);
|
|
||||||
copy_v3_v3(ups->average_stroke_accum, location);
|
|
||||||
ups->average_stroke_counter = 1;
|
|
||||||
ups->last_stroke_valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sync face sets visibility and vertex visibility. */
|
|
||||||
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
|
|
||||||
|
|
||||||
SCULPT_undo_push_end();
|
|
||||||
|
|
||||||
for (int i = 0; i < totnode; i++) {
|
|
||||||
BKE_pbvh_node_mark_update_visibility(nodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
|
|
||||||
|
|
||||||
MEM_SAFE_FREE(nodes);
|
|
||||||
|
|
||||||
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
|
|
||||||
BKE_mesh_flush_hidden_from_verts(ob->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ED_region_tag_redraw(region);
|
|
||||||
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
|
||||||
|
|
||||||
View3D *v3d = CTX_wm_view3d(C);
|
|
||||||
if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
|
|
||||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
||||||
}
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
/* Identifiers. */
|
|
||||||
ot->name = "Face Sets Visibility";
|
|
||||||
ot->idname = "SCULPT_OT_face_set_change_visibility";
|
|
||||||
ot->description = "Change the visibility of the Face Sets of the sculpt";
|
|
||||||
|
|
||||||
/* Api callbacks. */
|
|
||||||
ot->invoke = sculpt_face_sets_change_visibility_invoke;
|
|
||||||
ot->poll = SCULPT_mode_poll;
|
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
|
|
||||||
RNA_def_enum(ot->srna,
|
|
||||||
"mode",
|
|
||||||
prop_sculpt_face_sets_change_visibility_types,
|
|
||||||
SCULPT_FACE_SET_VISIBILITY_TOGGLE,
|
|
||||||
"Mode",
|
|
||||||
"");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sculpt_face_sets_randomize_colors_invoke(bContext *C,
|
|
||||||
wmOperator *UNUSED(op),
|
|
||||||
const wmEvent *UNUSED(event))
|
|
||||||
{
|
|
||||||
|
|
||||||
Object *ob = CTX_data_active_object(C);
|
|
||||||
SculptSession *ss = ob->sculpt;
|
|
||||||
ARegion *region = CTX_wm_region(C);
|
|
||||||
|
|
||||||
/* Dyntopo and Multires not supported for now. */
|
|
||||||
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
PBVH *pbvh = ob->sculpt->pbvh;
|
|
||||||
PBVHNode **nodes;
|
|
||||||
int totnode;
|
|
||||||
Mesh *mesh = ob->data;
|
|
||||||
|
|
||||||
mesh->face_sets_color_seed += 1;
|
|
||||||
if (ss->face_sets) {
|
|
||||||
const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed),
|
|
||||||
0,
|
|
||||||
max_ii(0, ss->totfaces - 1));
|
|
||||||
mesh->face_sets_color_default = ss->face_sets[random_index];
|
|
||||||
}
|
|
||||||
BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
|
|
||||||
|
|
||||||
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
|
||||||
for (int i = 0; i < totnode; i++) {
|
|
||||||
BKE_pbvh_node_mark_redraw(nodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
MEM_SAFE_FREE(nodes);
|
|
||||||
|
|
||||||
View3D *v3d = CTX_wm_view3d(C);
|
|
||||||
if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
|
|
||||||
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
|
||||||
}
|
|
||||||
|
|
||||||
ED_region_tag_redraw(region);
|
|
||||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
/* Identifiers. */
|
|
||||||
ot->name = "Randomize Face Sets Colors";
|
|
||||||
ot->idname = "SCULPT_OT_face_sets_randomize_colors";
|
|
||||||
ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
|
|
||||||
|
|
||||||
/* Api callbacks. */
|
|
||||||
ot->invoke = sculpt_face_sets_randomize_colors_invoke;
|
|
||||||
ot->poll = SCULPT_mode_poll;
|
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ED_operatortypes_sculpt(void)
|
void ED_operatortypes_sculpt(void)
|
||||||
{
|
{
|
||||||
|
975
source/blender/editors/sculpt_paint/sculpt_face_set.c
Normal file
975
source/blender/editors/sculpt_paint/sculpt_face_set.c
Normal file
@@ -0,0 +1,975 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup edsculpt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_blenlib.h"
|
||||||
|
#include "BLI_math.h"
|
||||||
|
#include "BLI_hash.h"
|
||||||
|
#include "BLI_task.h"
|
||||||
|
|
||||||
|
#include "DNA_brush_types.h"
|
||||||
|
#include "DNA_customdata_types.h"
|
||||||
|
#include "DNA_mesh_types.h"
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
#include "DNA_object_types.h"
|
||||||
|
#include "DNA_scene_types.h"
|
||||||
|
|
||||||
|
#include "BKE_brush.h"
|
||||||
|
#include "BKE_ccg.h"
|
||||||
|
#include "BKE_colortools.h"
|
||||||
|
#include "BKE_context.h"
|
||||||
|
#include "BKE_customdata.h"
|
||||||
|
#include "BKE_mesh.h"
|
||||||
|
#include "BKE_mesh_mapping.h"
|
||||||
|
#include "BKE_multires.h"
|
||||||
|
#include "BKE_node.h"
|
||||||
|
#include "BKE_object.h"
|
||||||
|
#include "BKE_paint.h"
|
||||||
|
#include "BKE_pbvh.h"
|
||||||
|
#include "BKE_scene.h"
|
||||||
|
|
||||||
|
#include "DEG_depsgraph.h"
|
||||||
|
|
||||||
|
#include "WM_api.h"
|
||||||
|
#include "WM_message.h"
|
||||||
|
#include "WM_toolsystem.h"
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
#include "ED_object.h"
|
||||||
|
#include "ED_screen.h"
|
||||||
|
#include "ED_sculpt.h"
|
||||||
|
#include "ED_view3d.h"
|
||||||
|
#include "paint_intern.h"
|
||||||
|
#include "sculpt_intern.h"
|
||||||
|
|
||||||
|
#include "RNA_access.h"
|
||||||
|
#include "RNA_define.h"
|
||||||
|
|
||||||
|
#include "bmesh.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Draw Face Sets Brush. */
|
||||||
|
|
||||||
|
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
|
||||||
|
const int n,
|
||||||
|
const TaskParallelTLS *__restrict tls)
|
||||||
|
{
|
||||||
|
SculptThreadedTaskData *data = userdata;
|
||||||
|
SculptSession *ss = data->ob->sculpt;
|
||||||
|
const Brush *brush = data->brush;
|
||||||
|
const float bstrength = ss->cache->bstrength;
|
||||||
|
|
||||||
|
PBVHVertexIter vd;
|
||||||
|
|
||||||
|
SculptBrushTest test;
|
||||||
|
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||||
|
ss, &test, data->brush->falloff_shape);
|
||||||
|
|
||||||
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
||||||
|
{
|
||||||
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
|
||||||
|
MeshElemMap *vert_map = &ss->pmap[vd.index];
|
||||||
|
for (int j = 0; j < ss->pmap[vd.index].count; j++) {
|
||||||
|
const MPoly *p = &ss->mpoly[vert_map->indices[j]];
|
||||||
|
|
||||||
|
float poly_center[3];
|
||||||
|
BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], ss->mvert, poly_center);
|
||||||
|
|
||||||
|
if (sculpt_brush_test_sq_fn(&test, poly_center)) {
|
||||||
|
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||||
|
brush,
|
||||||
|
vd.co,
|
||||||
|
sqrtf(test.dist),
|
||||||
|
vd.no,
|
||||||
|
vd.fno,
|
||||||
|
vd.mask ? *vd.mask : 0.0f,
|
||||||
|
vd.index,
|
||||||
|
tls->thread_id);
|
||||||
|
|
||||||
|
if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
|
||||||
|
ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
||||||
|
{
|
||||||
|
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||||
|
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||||
|
brush,
|
||||||
|
vd.co,
|
||||||
|
sqrtf(test.dist),
|
||||||
|
vd.no,
|
||||||
|
vd.fno,
|
||||||
|
vd.mask ? *vd.mask : 0.0f,
|
||||||
|
vd.index,
|
||||||
|
tls->thread_id);
|
||||||
|
|
||||||
|
if (fade > 0.05f) {
|
||||||
|
SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BKE_pbvh_vertex_iter_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
|
||||||
|
const int n,
|
||||||
|
const TaskParallelTLS *__restrict tls)
|
||||||
|
{
|
||||||
|
SculptThreadedTaskData *data = userdata;
|
||||||
|
SculptSession *ss = data->ob->sculpt;
|
||||||
|
const Brush *brush = data->brush;
|
||||||
|
float bstrength = ss->cache->bstrength;
|
||||||
|
|
||||||
|
PBVHVertexIter vd;
|
||||||
|
|
||||||
|
SculptBrushTest test;
|
||||||
|
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||||
|
ss, &test, data->brush->falloff_shape);
|
||||||
|
|
||||||
|
const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
|
||||||
|
/* This operations needs a stregth tweak as the relax deformation is too weak by default. */
|
||||||
|
if (relax_face_sets) {
|
||||||
|
bstrength *= 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
||||||
|
{
|
||||||
|
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||||
|
if (relax_face_sets != SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
|
||||||
|
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
|
||||||
|
brush,
|
||||||
|
vd.co,
|
||||||
|
sqrtf(test.dist),
|
||||||
|
vd.no,
|
||||||
|
vd.fno,
|
||||||
|
vd.mask ? *vd.mask : 0.0f,
|
||||||
|
vd.index,
|
||||||
|
tls->thread_id);
|
||||||
|
|
||||||
|
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
|
||||||
|
if (vd.mvert) {
|
||||||
|
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BKE_pbvh_vertex_iter_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
|
||||||
|
{
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||||
|
|
||||||
|
if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0 &&
|
||||||
|
ss->cache->radial_symmetry_pass == 0) {
|
||||||
|
if (ss->cache->invert) {
|
||||||
|
/* When inverting the brush, pick the paint face mask ID from the mesh. */
|
||||||
|
ss->cache->paint_face_set = SCULPT_active_face_set_get(ss);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* By default create a new Face Sets. */
|
||||||
|
ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_curvemapping_initialize(brush->curve);
|
||||||
|
|
||||||
|
/* Threaded loop over nodes. */
|
||||||
|
SculptThreadedTaskData data = {
|
||||||
|
.sd = sd,
|
||||||
|
.ob = ob,
|
||||||
|
.brush = brush,
|
||||||
|
.nodes = nodes,
|
||||||
|
};
|
||||||
|
|
||||||
|
PBVHParallelSettings settings;
|
||||||
|
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
|
||||||
|
if (ss->cache->alt_smooth) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
BKE_pbvh_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BKE_pbvh_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Face Sets Operators */
|
||||||
|
|
||||||
|
typedef enum eSculptFaceGroupsCreateModes {
|
||||||
|
SCULPT_FACE_SET_MASKED = 0,
|
||||||
|
SCULPT_FACE_SET_VISIBLE = 1,
|
||||||
|
SCULPT_FACE_SET_ALL = 2,
|
||||||
|
SCULPT_FACE_SET_SELECTION = 3,
|
||||||
|
} eSculptFaceGroupsCreateModes;
|
||||||
|
|
||||||
|
static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_MASKED,
|
||||||
|
"MASKED",
|
||||||
|
0,
|
||||||
|
"Face Set From Masked",
|
||||||
|
"Create a new Face Set from the masked faces",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_VISIBLE,
|
||||||
|
"VISIBLE",
|
||||||
|
0,
|
||||||
|
"Face Set From Visible",
|
||||||
|
"Create a new Face Set from the visible vertices",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_ALL,
|
||||||
|
"ALL",
|
||||||
|
0,
|
||||||
|
"Face Set Full Mesh",
|
||||||
|
"Create an unique Face Set with all faces in the sculpt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_SELECTION,
|
||||||
|
"SELECTION",
|
||||||
|
0,
|
||||||
|
"Face Set From Edit Mode Selection",
|
||||||
|
"Create an Face Set corresponding to the Edit Mode face selection",
|
||||||
|
},
|
||||||
|
{0, NULL, 0, NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sculpt_face_set_create_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
||||||
|
{
|
||||||
|
Object *ob = CTX_data_active_object(C);
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
ARegion *region = CTX_wm_region(C);
|
||||||
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||||
|
|
||||||
|
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||||
|
|
||||||
|
/* Dyntopo not suported. */
|
||||||
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
|
||||||
|
|
||||||
|
const int tot_vert = SCULPT_vertex_count_get(ss);
|
||||||
|
float threshold = 0.5f;
|
||||||
|
|
||||||
|
PBVH *pbvh = ob->sculpt->pbvh;
|
||||||
|
PBVHNode **nodes;
|
||||||
|
int totnode;
|
||||||
|
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
||||||
|
|
||||||
|
if (!nodes) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCULPT_undo_push_begin("face set change");
|
||||||
|
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
||||||
|
|
||||||
|
const int next_face_set = SCULPT_face_set_next_available_get(ss);
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_MASKED) {
|
||||||
|
for (int i = 0; i < tot_vert; i++) {
|
||||||
|
if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
|
||||||
|
SCULPT_vertex_face_set_set(ss, i, next_face_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_VISIBLE) {
|
||||||
|
for (int i = 0; i < tot_vert; i++) {
|
||||||
|
if (SCULPT_vertex_visible_get(ss, i)) {
|
||||||
|
SCULPT_vertex_face_set_set(ss, i, next_face_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_ALL) {
|
||||||
|
for (int i = 0; i < tot_vert; i++) {
|
||||||
|
SCULPT_vertex_face_set_set(ss, i, next_face_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_SELECTION) {
|
||||||
|
Mesh *mesh = ob->data;
|
||||||
|
BMesh *bm;
|
||||||
|
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
||||||
|
bm = BM_mesh_create(&allocsize,
|
||||||
|
&((struct BMeshCreateParams){
|
||||||
|
.use_toolflags = true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
BM_mesh_bm_from_me(bm,
|
||||||
|
mesh,
|
||||||
|
(&(struct BMeshFromMeshParams){
|
||||||
|
.calc_face_normal = true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
BMIter iter;
|
||||||
|
BMFace *f;
|
||||||
|
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||||
|
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
||||||
|
ss->face_sets[BM_elem_index_get(f)] = next_face_set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BM_mesh_free(bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < totnode; i++) {
|
||||||
|
BKE_pbvh_node_mark_redraw(nodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_SAFE_FREE(nodes);
|
||||||
|
|
||||||
|
SCULPT_undo_push_end();
|
||||||
|
|
||||||
|
ED_region_tag_redraw(region);
|
||||||
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCULPT_OT_face_sets_create(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Create Face Set";
|
||||||
|
ot->idname = "SCULPT_OT_face_sets_create";
|
||||||
|
ot->description = "Create a new Face Set";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->invoke = sculpt_face_set_create_invoke;
|
||||||
|
ot->poll = SCULPT_mode_poll;
|
||||||
|
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
RNA_def_enum(
|
||||||
|
ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum eSculptFaceSetsInitMode {
|
||||||
|
SCULPT_FACE_SETS_FROM_LOOSE_PARTS = 0,
|
||||||
|
SCULPT_FACE_SETS_FROM_MATERIALS = 1,
|
||||||
|
SCULPT_FACE_SETS_FROM_NORMALS = 2,
|
||||||
|
SCULPT_FACE_SETS_FROM_UV_SEAMS = 3,
|
||||||
|
SCULPT_FACE_SETS_FROM_CREASES = 4,
|
||||||
|
SCULPT_FACE_SETS_FROM_SHARP_EDGES = 5,
|
||||||
|
SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT = 6,
|
||||||
|
SCULPT_FACE_SETS_FROM_FACE_MAPS = 7,
|
||||||
|
} eSculptFaceSetsInitMode;
|
||||||
|
|
||||||
|
static EnumPropertyItem prop_sculpt_face_sets_init_types[] = {
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_LOOSE_PARTS,
|
||||||
|
"LOOSE_PARTS",
|
||||||
|
0,
|
||||||
|
"Face Sets From Loose Parts",
|
||||||
|
"Create a Face Set per loose part in the mesh",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_MATERIALS,
|
||||||
|
"MATERIALS",
|
||||||
|
0,
|
||||||
|
"Face Sets From Material Slots",
|
||||||
|
"Create a Face Set per Material Slot",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_NORMALS,
|
||||||
|
"NORMALS",
|
||||||
|
0,
|
||||||
|
"Face Sets From Mesh Normals",
|
||||||
|
"Create Face Sets for Faces that have similar normal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_UV_SEAMS,
|
||||||
|
"UV_SEAMS",
|
||||||
|
0,
|
||||||
|
"Face Sets From UV Seams",
|
||||||
|
"Create Face Sets using UV Seams as boundaries",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_CREASES,
|
||||||
|
"CREASES",
|
||||||
|
0,
|
||||||
|
"Face Sets From Edge Creases",
|
||||||
|
"Create Face Sets using Edge Creases as boundaries",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT,
|
||||||
|
"BEVEL_WEIGHT",
|
||||||
|
0,
|
||||||
|
"Face Sets From Bevel Weight",
|
||||||
|
"Create Face Sets using Bevel Weights as boundaries",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_SHARP_EDGES,
|
||||||
|
"SHARP_EDGES",
|
||||||
|
0,
|
||||||
|
"Face Sets From Sharp Edges",
|
||||||
|
"Create Face Sets using Sharp Edges as boundaries",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SETS_FROM_FACE_MAPS,
|
||||||
|
"FACE_MAPS",
|
||||||
|
0,
|
||||||
|
"Face Sets From Face Maps",
|
||||||
|
"Create a Face Set per Face Map",
|
||||||
|
},
|
||||||
|
{0, NULL, 0, NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef bool (*face_sets_flood_fill_test)(
|
||||||
|
BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold);
|
||||||
|
|
||||||
|
static bool sculpt_face_sets_init_loose_parts_test(BMesh *UNUSED(bm),
|
||||||
|
BMFace *UNUSED(from_f),
|
||||||
|
BMEdge *UNUSED(from_e),
|
||||||
|
BMFace *UNUSED(to_f),
|
||||||
|
const float UNUSED(threshold))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sculpt_face_sets_init_normals_test(
|
||||||
|
BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold)
|
||||||
|
{
|
||||||
|
return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sculpt_face_sets_init_uv_seams_test(BMesh *UNUSED(bm),
|
||||||
|
BMFace *UNUSED(from_f),
|
||||||
|
BMEdge *from_e,
|
||||||
|
BMFace *UNUSED(to_f),
|
||||||
|
const float UNUSED(threshold))
|
||||||
|
{
|
||||||
|
return !BM_elem_flag_test(from_e, BM_ELEM_SEAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sculpt_face_sets_init_crease_test(
|
||||||
|
BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
|
||||||
|
{
|
||||||
|
return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sculpt_face_sets_init_bevel_weight_test(
|
||||||
|
BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
|
||||||
|
{
|
||||||
|
return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sculpt_face_sets_init_sharp_edges_test(BMesh *UNUSED(bm),
|
||||||
|
BMFace *UNUSED(from_f),
|
||||||
|
BMEdge *from_e,
|
||||||
|
BMFace *UNUSED(to_f),
|
||||||
|
const float UNUSED(threshold))
|
||||||
|
{
|
||||||
|
return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sculpt_face_sets_init_flood_fill(Object *ob,
|
||||||
|
face_sets_flood_fill_test test,
|
||||||
|
const float threshold)
|
||||||
|
{
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
Mesh *mesh = ob->data;
|
||||||
|
BMesh *bm;
|
||||||
|
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
||||||
|
bm = BM_mesh_create(&allocsize,
|
||||||
|
&((struct BMeshCreateParams){
|
||||||
|
.use_toolflags = true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
BM_mesh_bm_from_me(bm,
|
||||||
|
mesh,
|
||||||
|
(&(struct BMeshFromMeshParams){
|
||||||
|
.calc_face_normal = true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
bool *visited_faces = MEM_callocN(sizeof(bool) * mesh->totpoly, "visited faces");
|
||||||
|
const int totfaces = mesh->totpoly;
|
||||||
|
|
||||||
|
int *face_sets = ss->face_sets;
|
||||||
|
|
||||||
|
BM_mesh_elem_table_init(bm, BM_FACE);
|
||||||
|
BM_mesh_elem_table_ensure(bm, BM_FACE);
|
||||||
|
|
||||||
|
int next_face_set = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < totfaces; i++) {
|
||||||
|
if (!visited_faces[i]) {
|
||||||
|
GSQueue *queue;
|
||||||
|
queue = BLI_gsqueue_new(sizeof(int));
|
||||||
|
|
||||||
|
face_sets[i] = next_face_set;
|
||||||
|
visited_faces[i] = true;
|
||||||
|
BLI_gsqueue_push(queue, &i);
|
||||||
|
|
||||||
|
while (!BLI_gsqueue_is_empty(queue)) {
|
||||||
|
int from_f;
|
||||||
|
BLI_gsqueue_pop(queue, &from_f);
|
||||||
|
|
||||||
|
BMFace *f, *f_neighbor;
|
||||||
|
BMEdge *ed;
|
||||||
|
BMIter iter_a, iter_b;
|
||||||
|
|
||||||
|
f = BM_face_at_index(bm, from_f);
|
||||||
|
|
||||||
|
BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) {
|
||||||
|
BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) {
|
||||||
|
if (f_neighbor != f) {
|
||||||
|
int neighbor_face_index = BM_elem_index_get(f_neighbor);
|
||||||
|
if (!visited_faces[neighbor_face_index]) {
|
||||||
|
if (test(bm, f, ed, f_neighbor, threshold)) {
|
||||||
|
face_sets[neighbor_face_index] = next_face_set;
|
||||||
|
visited_faces[neighbor_face_index] = true;
|
||||||
|
BLI_gsqueue_push(queue, &neighbor_face_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_face_set += 1;
|
||||||
|
|
||||||
|
BLI_gsqueue_free(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_SAFE_FREE(visited_faces);
|
||||||
|
|
||||||
|
BM_mesh_free(bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sculpt_face_sets_init_loop(Object *ob, const int mode)
|
||||||
|
{
|
||||||
|
Mesh *mesh = ob->data;
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
BMesh *bm;
|
||||||
|
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
||||||
|
bm = BM_mesh_create(&allocsize,
|
||||||
|
&((struct BMeshCreateParams){
|
||||||
|
.use_toolflags = true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
BM_mesh_bm_from_me(bm,
|
||||||
|
mesh,
|
||||||
|
(&(struct BMeshFromMeshParams){
|
||||||
|
.calc_face_normal = true,
|
||||||
|
}));
|
||||||
|
BMIter iter;
|
||||||
|
BMFace *f;
|
||||||
|
|
||||||
|
const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
|
||||||
|
|
||||||
|
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||||
|
if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
|
||||||
|
ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
|
||||||
|
}
|
||||||
|
else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
|
||||||
|
if (cd_fmaps_offset != -1) {
|
||||||
|
ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ss->face_sets[BM_elem_index_get(f)] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BM_mesh_free(bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sculpt_face_set_init_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
||||||
|
{
|
||||||
|
Object *ob = CTX_data_active_object(C);
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
ARegion *region = CTX_wm_region(C);
|
||||||
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||||
|
|
||||||
|
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||||
|
|
||||||
|
/* Dyntopo not supported. */
|
||||||
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
|
||||||
|
|
||||||
|
PBVH *pbvh = ob->sculpt->pbvh;
|
||||||
|
PBVHNode **nodes;
|
||||||
|
int totnode;
|
||||||
|
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
||||||
|
|
||||||
|
if (!nodes) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCULPT_undo_push_begin("face set change");
|
||||||
|
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
||||||
|
|
||||||
|
const float threshold = RNA_float_get(op->ptr, "threshold");
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case SCULPT_FACE_SETS_FROM_LOOSE_PARTS:
|
||||||
|
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_MATERIALS:
|
||||||
|
sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_NORMALS:
|
||||||
|
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_UV_SEAMS:
|
||||||
|
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_CREASES:
|
||||||
|
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_SHARP_EDGES:
|
||||||
|
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT:
|
||||||
|
sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold);
|
||||||
|
break;
|
||||||
|
case SCULPT_FACE_SETS_FROM_FACE_MAPS:
|
||||||
|
sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCULPT_undo_push_end();
|
||||||
|
|
||||||
|
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
|
||||||
|
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
|
||||||
|
|
||||||
|
for (int i = 0; i < totnode; i++) {
|
||||||
|
BKE_pbvh_node_mark_update_visibility(nodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
|
||||||
|
|
||||||
|
MEM_SAFE_FREE(nodes);
|
||||||
|
|
||||||
|
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
|
||||||
|
BKE_mesh_flush_hidden_from_verts(ob->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ED_region_tag_redraw(region);
|
||||||
|
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
||||||
|
|
||||||
|
View3D *v3d = CTX_wm_view3d(C);
|
||||||
|
if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
|
||||||
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCULPT_OT_face_sets_init(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Init Face Sets";
|
||||||
|
ot->idname = "SCULPT_OT_face_sets_init";
|
||||||
|
ot->description = "Initializes all Face Sets in the mesh";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->invoke = sculpt_face_set_init_invoke;
|
||||||
|
ot->poll = SCULPT_mode_poll;
|
||||||
|
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
RNA_def_enum(
|
||||||
|
ot->srna, "mode", prop_sculpt_face_sets_init_types, SCULPT_FACE_SET_MASKED, "Mode", "");
|
||||||
|
RNA_def_float(
|
||||||
|
ot->srna,
|
||||||
|
"threshold",
|
||||||
|
0.5f,
|
||||||
|
0.0f,
|
||||||
|
1.0f,
|
||||||
|
"Threshold",
|
||||||
|
"Minimum value to consider a certain atribute a boundary when creating the Face Sets",
|
||||||
|
0.0f,
|
||||||
|
1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum eSculptFaceGroupVisibilityModes {
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0,
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1,
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2,
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_INVERT = 3,
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4,
|
||||||
|
} eSculptFaceGroupVisibilityModes;
|
||||||
|
|
||||||
|
static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = {
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_TOGGLE,
|
||||||
|
"TOGGLE",
|
||||||
|
0,
|
||||||
|
"Toggle Visibility",
|
||||||
|
"Hide all Face Sets except for the active one",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE,
|
||||||
|
"SHOW_ACTIVE",
|
||||||
|
0,
|
||||||
|
"Show Active Face Set",
|
||||||
|
"Show Active Face Set",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE,
|
||||||
|
"HIDE_ACTIVE",
|
||||||
|
0,
|
||||||
|
"Hide Active Face Sets",
|
||||||
|
"Hide Active Face Sets",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_INVERT,
|
||||||
|
"INVERT",
|
||||||
|
0,
|
||||||
|
"Invert Face Set Visibility",
|
||||||
|
"Invert Face Set Visibility",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_SHOW_ALL,
|
||||||
|
"SHOW_ALL",
|
||||||
|
0,
|
||||||
|
"Show All Face Sets",
|
||||||
|
"Show All Face Sets",
|
||||||
|
},
|
||||||
|
{0, NULL, 0, NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sculpt_face_sets_change_visibility_invoke(bContext *C,
|
||||||
|
wmOperator *op,
|
||||||
|
const wmEvent *UNUSED(event))
|
||||||
|
{
|
||||||
|
Object *ob = CTX_data_active_object(C);
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
ARegion *region = CTX_wm_region(C);
|
||||||
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||||
|
|
||||||
|
/* Dyntopo not supported. */
|
||||||
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
|
||||||
|
|
||||||
|
const int tot_vert = SCULPT_vertex_count_get(ss);
|
||||||
|
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||||
|
const int active_face_set = SCULPT_active_face_set_get(ss);
|
||||||
|
|
||||||
|
SCULPT_undo_push_begin("Hide area");
|
||||||
|
|
||||||
|
PBVH *pbvh = ob->sculpt->pbvh;
|
||||||
|
PBVHNode **nodes;
|
||||||
|
int totnode;
|
||||||
|
|
||||||
|
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
||||||
|
|
||||||
|
if (totnode == 0) {
|
||||||
|
MEM_SAFE_FREE(nodes);
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) {
|
||||||
|
bool hidden_vertex = false;
|
||||||
|
|
||||||
|
/* This can fail with regular meshes with non-manifold geometry as the visibility state can't
|
||||||
|
* be synced from face sets to non-manifold vertices. */
|
||||||
|
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
||||||
|
for (int i = 0; i < tot_vert; i++) {
|
||||||
|
if (!SCULPT_vertex_visible_get(ss, i)) {
|
||||||
|
hidden_vertex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ss->totfaces; i++) {
|
||||||
|
if (ss->face_sets[i] <= 0) {
|
||||||
|
hidden_vertex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden_vertex) {
|
||||||
|
SCULPT_face_sets_visibility_all_set(ss, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SCULPT_face_sets_visibility_all_set(ss, false);
|
||||||
|
SCULPT_face_set_visibility_set(ss, active_face_set, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
|
||||||
|
SCULPT_face_sets_visibility_all_set(ss, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
|
||||||
|
SCULPT_face_sets_visibility_all_set(ss, false);
|
||||||
|
SCULPT_face_set_visibility_set(ss, active_face_set, true);
|
||||||
|
for (int i = 0; i < tot_vert; i++) {
|
||||||
|
SCULPT_vertex_visible_set(ss,
|
||||||
|
i,
|
||||||
|
SCULPT_vertex_visible_get(ss, i) &&
|
||||||
|
SCULPT_vertex_has_face_set(ss, i, active_face_set));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
|
||||||
|
SCULPT_face_set_visibility_set(ss, active_face_set, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
|
||||||
|
SCULPT_face_sets_visibility_invert(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For modes that use the cursor active vertex, update the rotation origin for viewport
|
||||||
|
* navigation. */
|
||||||
|
if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) {
|
||||||
|
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
|
||||||
|
float location[3];
|
||||||
|
copy_v3_v3(location, SCULPT_active_vertex_co_get(ss));
|
||||||
|
mul_m4_v3(ob->obmat, location);
|
||||||
|
copy_v3_v3(ups->average_stroke_accum, location);
|
||||||
|
ups->average_stroke_counter = 1;
|
||||||
|
ups->last_stroke_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sync face sets visibility and vertex visibility. */
|
||||||
|
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
|
||||||
|
|
||||||
|
SCULPT_undo_push_end();
|
||||||
|
|
||||||
|
for (int i = 0; i < totnode; i++) {
|
||||||
|
BKE_pbvh_node_mark_update_visibility(nodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
|
||||||
|
|
||||||
|
MEM_SAFE_FREE(nodes);
|
||||||
|
|
||||||
|
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
|
||||||
|
BKE_mesh_flush_hidden_from_verts(ob->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ED_region_tag_redraw(region);
|
||||||
|
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
||||||
|
|
||||||
|
View3D *v3d = CTX_wm_view3d(C);
|
||||||
|
if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
|
||||||
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||||
|
}
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* Identifiers. */
|
||||||
|
ot->name = "Face Sets Visibility";
|
||||||
|
ot->idname = "SCULPT_OT_face_set_change_visibility";
|
||||||
|
ot->description = "Change the visibility of the Face Sets of the sculpt";
|
||||||
|
|
||||||
|
/* Api callbacks. */
|
||||||
|
ot->invoke = sculpt_face_sets_change_visibility_invoke;
|
||||||
|
ot->poll = SCULPT_mode_poll;
|
||||||
|
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
RNA_def_enum(ot->srna,
|
||||||
|
"mode",
|
||||||
|
prop_sculpt_face_sets_change_visibility_types,
|
||||||
|
SCULPT_FACE_SET_VISIBILITY_TOGGLE,
|
||||||
|
"Mode",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sculpt_face_sets_randomize_colors_invoke(bContext *C,
|
||||||
|
wmOperator *UNUSED(op),
|
||||||
|
const wmEvent *UNUSED(event))
|
||||||
|
{
|
||||||
|
|
||||||
|
Object *ob = CTX_data_active_object(C);
|
||||||
|
SculptSession *ss = ob->sculpt;
|
||||||
|
ARegion *region = CTX_wm_region(C);
|
||||||
|
|
||||||
|
/* Dyntopo and Multires not supported for now. */
|
||||||
|
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
PBVH *pbvh = ob->sculpt->pbvh;
|
||||||
|
PBVHNode **nodes;
|
||||||
|
int totnode;
|
||||||
|
Mesh *mesh = ob->data;
|
||||||
|
|
||||||
|
mesh->face_sets_color_seed += 1;
|
||||||
|
if (ss->face_sets) {
|
||||||
|
const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed),
|
||||||
|
0,
|
||||||
|
max_ii(0, ss->totfaces - 1));
|
||||||
|
mesh->face_sets_color_default = ss->face_sets[random_index];
|
||||||
|
}
|
||||||
|
BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
|
||||||
|
|
||||||
|
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
|
||||||
|
for (int i = 0; i < totnode; i++) {
|
||||||
|
BKE_pbvh_node_mark_redraw(nodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_SAFE_FREE(nodes);
|
||||||
|
|
||||||
|
View3D *v3d = CTX_wm_view3d(C);
|
||||||
|
if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
|
||||||
|
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
||||||
|
}
|
||||||
|
|
||||||
|
ED_region_tag_redraw(region);
|
||||||
|
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* Identifiers. */
|
||||||
|
ot->name = "Randomize Face Sets Colors";
|
||||||
|
ot->idname = "SCULPT_OT_face_sets_randomize_colors";
|
||||||
|
ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
|
||||||
|
|
||||||
|
/* Api callbacks. */
|
||||||
|
ot->invoke = sculpt_face_sets_randomize_colors_invoke;
|
||||||
|
ot->poll = SCULPT_mode_poll;
|
||||||
|
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
}
|
@@ -128,6 +128,33 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
|
|||||||
int SCULPT_active_vertex_get(SculptSession *ss);
|
int SCULPT_active_vertex_get(SculptSession *ss);
|
||||||
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
|
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
|
||||||
|
|
||||||
|
/* Sculpt Visibility API */
|
||||||
|
|
||||||
|
void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
|
||||||
|
bool SCULPT_vertex_visible_get(SculptSession *ss, int index);
|
||||||
|
|
||||||
|
void SCULPT_visibility_sync_all_face_sets_to_vertices(struct SculptSession *ss);
|
||||||
|
void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
|
||||||
|
|
||||||
|
/* Face Sets API */
|
||||||
|
|
||||||
|
int SCULPT_active_face_set_get(SculptSession *ss);
|
||||||
|
int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
|
||||||
|
void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set);
|
||||||
|
|
||||||
|
bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
|
||||||
|
bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
|
||||||
|
|
||||||
|
int SCULPT_face_set_next_available_get(SculptSession *ss);
|
||||||
|
|
||||||
|
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
|
||||||
|
bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index);
|
||||||
|
bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index);
|
||||||
|
|
||||||
|
void SCULPT_face_sets_visibility_invert(SculptSession *ss);
|
||||||
|
void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
|
||||||
|
|
||||||
|
|
||||||
/* Sculpt Original Data */
|
/* Sculpt Original Data */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct BMLog *bm_log;
|
struct BMLog *bm_log;
|
||||||
@@ -146,11 +173,6 @@ typedef struct {
|
|||||||
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
|
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
|
||||||
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
|
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
|
||||||
|
|
||||||
/* Face Sets */
|
|
||||||
int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
|
|
||||||
bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
|
|
||||||
bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
|
|
||||||
|
|
||||||
/* Dynamic topology */
|
/* Dynamic topology */
|
||||||
void sculpt_pbvh_clear(Object *ob);
|
void sculpt_pbvh_clear(Object *ob);
|
||||||
void sculpt_dyntopo_node_layers_add(struct SculptSession *ss);
|
void sculpt_dyntopo_node_layers_add(struct SculptSession *ss);
|
||||||
@@ -274,6 +296,8 @@ void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
|
|||||||
SculptSession *ss,
|
SculptSession *ss,
|
||||||
const float outline_col[3],
|
const float outline_col[3],
|
||||||
const float outline_alpha);
|
const float outline_alpha);
|
||||||
|
/* Draw Face Sets Brush. */
|
||||||
|
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
|
||||||
|
|
||||||
/* Slide/Relax */
|
/* Slide/Relax */
|
||||||
void SCULPT_relax_vertex(struct SculptSession *ss,
|
void SCULPT_relax_vertex(struct SculptSession *ss,
|
||||||
@@ -282,9 +306,8 @@ void SCULPT_relax_vertex(struct SculptSession *ss,
|
|||||||
bool filter_boundary_face_sets,
|
bool filter_boundary_face_sets,
|
||||||
float *r_final_pos);
|
float *r_final_pos);
|
||||||
|
|
||||||
/* Sculpt Visibility API */
|
|
||||||
void SCULPT_visibility_sync_all_face_sets_to_vertices(struct SculptSession *ss);
|
|
||||||
void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
|
|
||||||
|
|
||||||
/* Undo */
|
/* Undo */
|
||||||
|
|
||||||
@@ -737,4 +760,14 @@ bool SCULPT_get_redraw_rect(struct ARegion *region,
|
|||||||
Object *ob,
|
Object *ob,
|
||||||
rcti *rect);
|
rcti *rect);
|
||||||
|
|
||||||
|
|
||||||
|
/* Operators */
|
||||||
|
|
||||||
|
/* Face Sets */
|
||||||
|
void SCULPT_OT_face_sets_randomize_colors(struct wmOperatorType *ot);
|
||||||
|
void SCULPT_OT_face_sets_change_visibility(struct wmOperatorType *ot);
|
||||||
|
void SCULPT_OT_face_sets_init(struct wmOperatorType *ot);
|
||||||
|
void SCULPT_OT_face_sets_create(struct wmOperatorType *ot);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user