Revert "UI: Use full word for face set operator name, tweak description"
This reverts commit 15d85c54c3.
Included a separate change/new file by mistake. Sorry for the noise.
This commit is contained in:
@@ -1,1505 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2020 Blender Foundation. All rights reserved. */
|
||||
|
||||
/** \file
|
||||
* \ingroup edsculpt
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_math.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_fair.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>
|
||||
|
||||
/* Utils. */
|
||||
|
||||
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
|
||||
{
|
||||
const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
|
||||
if (!face_sets) {
|
||||
return SCULPT_FACE_SET_NONE;
|
||||
}
|
||||
|
||||
int next_face_set_id = 0;
|
||||
for (int i = 0; i < mesh->totpoly; i++) {
|
||||
next_face_set_id = max_ii(next_face_set_id, face_sets[i]);
|
||||
}
|
||||
next_face_set_id++;
|
||||
|
||||
return next_face_set_id;
|
||||
}
|
||||
|
||||
void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id)
|
||||
{
|
||||
int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
|
||||
if (!face_sets) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh->totpoly; i++) {
|
||||
if (face_sets[i] == SCULPT_FACE_SET_NONE) {
|
||||
face_sets[i] = new_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const float mval[2])
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
if (!ss) {
|
||||
return SCULPT_FACE_SET_NONE;
|
||||
}
|
||||
|
||||
SculptCursorGeometryInfo gi;
|
||||
if (!SCULPT_cursor_geometry_info_update(C, &gi, mval, false)) {
|
||||
return SCULPT_FACE_SET_NONE;
|
||||
}
|
||||
|
||||
return SCULPT_active_face_set_get(ss);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
const int thread_id = BLI_task_parallel_thread_id(tls);
|
||||
|
||||
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
|
||||
|
||||
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], mvert, poly_center);
|
||||
|
||||
if (!sculpt_brush_test_sq_fn(&test, poly_center)) {
|
||||
continue;
|
||||
}
|
||||
const bool face_hidden = ss->hide_poly && ss->hide_poly[vert_map->indices[j]];
|
||||
if (face_hidden) {
|
||||
continue;
|
||||
}
|
||||
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.vertex,
|
||||
thread_id);
|
||||
|
||||
if (fade > 0.05f) {
|
||||
ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
||||
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
continue;
|
||||
}
|
||||
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.vertex,
|
||||
thread_id);
|
||||
|
||||
if (fade > 0.05f) {
|
||||
SCULPT_vertex_face_set_set(ss, vd.vertex, 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 strength tweak as the relax deformation is too weak by default. */
|
||||
if (relax_face_sets) {
|
||||
bstrength *= 2.0f;
|
||||
}
|
||||
|
||||
const int thread_id = BLI_task_parallel_thread_id(tls);
|
||||
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
continue;
|
||||
}
|
||||
if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.vertex,
|
||||
thread_id);
|
||||
|
||||
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
|
||||
if (vd.mvert) {
|
||||
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
BKE_curvemapping_init(brush->curve);
|
||||
|
||||
/* Threaded loop over nodes. */
|
||||
SculptThreadedTaskData data = {
|
||||
.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
};
|
||||
|
||||
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_relax_face_sets_brush_task_cb_ex, &settings);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_task_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, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
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;
|
||||
}
|
||||
|
||||
Mesh *mesh = ob->data;
|
||||
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
|
||||
|
||||
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, nullptr, nullptr, &nodes, &totnode);
|
||||
|
||||
if (!nodes) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
SCULPT_undo_push_begin(ob, op);
|
||||
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++) {
|
||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
if (SCULPT_vertex_mask_get(ss, vertex) >= threshold &&
|
||||
SCULPT_vertex_visible_get(ss, vertex)) {
|
||||
SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_VISIBLE) {
|
||||
|
||||
/* If all vertices in the sculpt are visible, create the new face set and update the default
|
||||
* color. This way the new face set will be white, which is a quick way of disabling all face
|
||||
* sets and the performance hit of rendering the overlay. */
|
||||
bool all_visible = true;
|
||||
for (int i = 0; i < tot_vert; i++) {
|
||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
if (!SCULPT_vertex_visible_get(ss, vertex)) {
|
||||
all_visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_visible) {
|
||||
mesh->face_sets_color_default = next_face_set;
|
||||
BKE_pbvh_face_sets_color_set(
|
||||
ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
|
||||
}
|
||||
|
||||
for (int i = 0; i < tot_vert; i++) {
|
||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
if (SCULPT_vertex_visible_get(ss, vertex)) {
|
||||
SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_ALL) {
|
||||
for (int i = 0; i < tot_vert; i++) {
|
||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_SELECTION) {
|
||||
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,
|
||||
.calc_vert_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(ob);
|
||||
|
||||
SCULPT_tag_update_overlays(C);
|
||||
|
||||
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->exec = sculpt_face_set_create_exec;
|
||||
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,
|
||||
SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES = 8,
|
||||
} 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",
|
||||
},
|
||||
{
|
||||
SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES,
|
||||
"FACE_SET_BOUNDARIES",
|
||||
0,
|
||||
"Face Sets from Face Set Boundaries",
|
||||
"Create a Face Set per isolated Face Set",
|
||||
},
|
||||
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
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 *from_f,
|
||||
BMEdge *UNUSED(from_e),
|
||||
BMFace *to_f,
|
||||
const float UNUSED(threshold))
|
||||
{
|
||||
return BM_elem_flag_test(from_f, BM_ELEM_HIDDEN) == BM_elem_flag_test(to_f, BM_ELEM_HIDDEN);
|
||||
}
|
||||
|
||||
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 bool sculpt_face_sets_init_face_set_boundary_test(
|
||||
BMesh *bm, BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float UNUSED(threshold))
|
||||
{
|
||||
const int cd_face_sets_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
|
||||
return BM_ELEM_CD_GET_INT(from_f, cd_face_sets_offset) ==
|
||||
BM_ELEM_CD_GET_INT(to_f, cd_face_sets_offset);
|
||||
}
|
||||
|
||||
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,
|
||||
.calc_vert_normal = true,
|
||||
}));
|
||||
|
||||
BLI_bitmap *visited_faces = BLI_BITMAP_NEW(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 (BLI_BITMAP_TEST(visited_faces, i)) {
|
||||
continue;
|
||||
}
|
||||
GSQueue *queue;
|
||||
queue = BLI_gsqueue_new(sizeof(int));
|
||||
|
||||
face_sets[i] = next_face_set;
|
||||
BLI_BITMAP_ENABLE(visited_faces, i);
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
int neighbor_face_index = BM_elem_index_get(f_neighbor);
|
||||
if (BLI_BITMAP_TEST(visited_faces, neighbor_face_index)) {
|
||||
continue;
|
||||
}
|
||||
if (!test(bm, f, ed, f_neighbor, threshold)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
face_sets[neighbor_face_index] = next_face_set;
|
||||
BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index);
|
||||
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,
|
||||
.calc_vert_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_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
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, false);
|
||||
|
||||
PBVH *pbvh = ob->sculpt->pbvh;
|
||||
PBVHNode **nodes;
|
||||
int totnode;
|
||||
BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
|
||||
|
||||
if (!nodes) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
SCULPT_undo_push_begin(ob, op);
|
||||
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
||||
|
||||
const float threshold = RNA_float_get(op->ptr, "threshold");
|
||||
|
||||
Mesh *mesh = ob->data;
|
||||
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
|
||||
|
||||
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_SET_BOUNDARIES:
|
||||
sculpt_face_sets_init_flood_fill(
|
||||
ob, sculpt_face_sets_init_face_set_boundary_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(ob);
|
||||
|
||||
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
|
||||
SCULPT_visibility_sync_all_from_faces(ob);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
SCULPT_tag_update_overlays(C);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void SCULPT_OT_face_sets_init(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Initialize Face Sets";
|
||||
ot->idname = "SCULPT_OT_face_sets_init";
|
||||
ot->description = "Set the value for all faces sets on the mesh";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = sculpt_face_set_init_exec;
|
||||
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 attribute 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, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
|
||||
/* Dyntopo not supported. */
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (!ss->face_sets) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
Mesh *mesh = BKE_object_get_original_mesh(ob);
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
|
||||
|
||||
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(ob, op);
|
||||
|
||||
PBVH *pbvh = ob->sculpt->pbvh;
|
||||
PBVHNode **nodes;
|
||||
int totnode;
|
||||
|
||||
BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &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++) {
|
||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
if (!SCULPT_vertex_visible_get(ss, vertex)) {
|
||||
hidden_vertex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ss->hide_poly) {
|
||||
for (int i = 0; i < ss->totfaces; i++) {
|
||||
if (ss->hide_poly[i]) {
|
||||
hidden_vertex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
|
||||
|
||||
if (hidden_vertex) {
|
||||
SCULPT_face_visibility_all_set(ss, true);
|
||||
}
|
||||
else {
|
||||
SCULPT_face_visibility_all_set(ss, false);
|
||||
SCULPT_face_set_visibility_set(ss, active_face_set, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
|
||||
/* As an optimization, free the hide attribute when making all geometry visible. This allows
|
||||
* reduced memory usage without manually clearing it later, and allows sculpt operations to
|
||||
* avoid checking element's hide status. */
|
||||
CustomData_free_layer_named(&mesh->pdata, ".hide_poly", mesh->totpoly);
|
||||
ss->hide_poly = nullptr;
|
||||
BKE_pbvh_update_hide_attributes_from_mesh(pbvh);
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
|
||||
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
|
||||
SCULPT_face_visibility_all_set(ss, false);
|
||||
SCULPT_face_set_visibility_set(ss, active_face_set, true);
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
|
||||
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
|
||||
SCULPT_face_set_visibility_set(ss, active_face_set, false);
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
|
||||
ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
|
||||
SCULPT_face_visibility_all_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_from_faces(ob);
|
||||
|
||||
SCULPT_undo_push_end(ob);
|
||||
|
||||
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);
|
||||
|
||||
SCULPT_tag_update_overlays(C);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int sculpt_face_sets_change_visibility_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
/* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
|
||||
* cursor updates. */
|
||||
SculptCursorGeometryInfo sgi;
|
||||
const float mval_fl[2] = {UNPACK2(event->mval)};
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
|
||||
|
||||
return sculpt_face_sets_change_visibility_exec(C, op);
|
||||
}
|
||||
|
||||
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->exec = sculpt_face_sets_change_visibility_exec;
|
||||
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_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
/* Dyntopo not supported. */
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (!ss->face_sets) {
|
||||
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, nullptr, nullptr, &nodes, &totnode);
|
||||
for (int i = 0; i < totnode; i++) {
|
||||
BKE_pbvh_node_mark_redraw(nodes[i]);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(nodes);
|
||||
|
||||
SCULPT_tag_update_overlays(C);
|
||||
|
||||
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->exec = sculpt_face_sets_randomize_colors_exec;
|
||||
ot->poll = SCULPT_mode_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
typedef enum eSculptFaceSetEditMode {
|
||||
SCULPT_FACE_SET_EDIT_GROW = 0,
|
||||
SCULPT_FACE_SET_EDIT_SHRINK = 1,
|
||||
SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2,
|
||||
SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3,
|
||||
SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4,
|
||||
} eSculptFaceSetEditMode;
|
||||
|
||||
static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
|
||||
{
|
||||
SCULPT_FACE_SET_EDIT_GROW,
|
||||
"GROW",
|
||||
0,
|
||||
"Grow Face Set",
|
||||
"Grows the Face Sets boundary by one face based on mesh topology",
|
||||
},
|
||||
{
|
||||
SCULPT_FACE_SET_EDIT_SHRINK,
|
||||
"SHRINK",
|
||||
0,
|
||||
"Shrink Face Set",
|
||||
"Shrinks the Face Sets boundary by one face based on mesh topology",
|
||||
},
|
||||
{
|
||||
SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY,
|
||||
"DELETE_GEOMETRY",
|
||||
0,
|
||||
"Delete Geometry",
|
||||
"Deletes the faces that are assigned to the Face Set",
|
||||
},
|
||||
{
|
||||
SCULPT_FACE_SET_EDIT_FAIR_POSITIONS,
|
||||
"FAIR_POSITIONS",
|
||||
0,
|
||||
"Fair Positions",
|
||||
"Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
|
||||
"vertex positions",
|
||||
},
|
||||
{
|
||||
SCULPT_FACE_SET_EDIT_FAIR_TANGENCY,
|
||||
"FAIR_TANGENCY",
|
||||
0,
|
||||
"Fair Tangency",
|
||||
"Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
|
||||
"vertex tangents",
|
||||
},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static void sculpt_face_set_grow(Object *ob,
|
||||
SculptSession *ss,
|
||||
const int *prev_face_sets,
|
||||
const int active_face_set_id,
|
||||
const bool modify_hidden)
|
||||
{
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
const MPoly *polys = BKE_mesh_polys(mesh);
|
||||
const MLoop *loops = BKE_mesh_loops(mesh);
|
||||
|
||||
for (int p = 0; p < mesh->totpoly; p++) {
|
||||
if (!modify_hidden && prev_face_sets[p] <= 0) {
|
||||
continue;
|
||||
}
|
||||
const MPoly *c_poly = &polys[p];
|
||||
for (int l = 0; l < c_poly->totloop; l++) {
|
||||
const MLoop *c_loop = &loops[c_poly->loopstart + l];
|
||||
const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
|
||||
for (int i = 0; i < vert_map->count; i++) {
|
||||
const int neighbor_face_index = vert_map->indices[i];
|
||||
if (neighbor_face_index == p) {
|
||||
continue;
|
||||
}
|
||||
if (abs(prev_face_sets[neighbor_face_index]) == active_face_set_id) {
|
||||
ss->face_sets[p] = active_face_set_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sculpt_face_set_shrink(Object *ob,
|
||||
SculptSession *ss,
|
||||
const int *prev_face_sets,
|
||||
const int active_face_set_id,
|
||||
const bool modify_hidden)
|
||||
{
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
const MPoly *polys = BKE_mesh_polys(mesh);
|
||||
const MLoop *loops = BKE_mesh_loops(mesh);
|
||||
for (int p = 0; p < mesh->totpoly; p++) {
|
||||
if (!modify_hidden && prev_face_sets[p] <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (abs(prev_face_sets[p]) == active_face_set_id) {
|
||||
const MPoly *c_poly = &polys[p];
|
||||
for (int l = 0; l < c_poly->totloop; l++) {
|
||||
const MLoop *c_loop = &loops[c_poly->loopstart + l];
|
||||
const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
|
||||
for (int i = 0; i < vert_map->count; i++) {
|
||||
const int neighbor_face_index = vert_map->indices[i];
|
||||
if (neighbor_face_index == p) {
|
||||
continue;
|
||||
}
|
||||
if (abs(prev_face_sets[neighbor_face_index]) != active_face_set_id) {
|
||||
ss->face_sets[p] = prev_face_sets[neighbor_face_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only)
|
||||
{
|
||||
if (face_sets == nullptr) {
|
||||
return true;
|
||||
}
|
||||
int first_face_set = SCULPT_FACE_SET_NONE;
|
||||
if (check_visible_only) {
|
||||
for (int f = 0; f < ss->totfaces; f++) {
|
||||
if (ss->hide_poly && ss->hide_poly[f]) {
|
||||
continue;
|
||||
}
|
||||
first_face_set = face_sets[f];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
first_face_set = face_sets[0];
|
||||
}
|
||||
|
||||
if (first_face_set == SCULPT_FACE_SET_NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int f = 0; f < ss->totfaces; f++) {
|
||||
if (check_visible_only && ss->hide_poly && ss->hide_poly[f]) {
|
||||
continue;
|
||||
}
|
||||
if (face_sets[f] != first_face_set) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sculpt_face_set_delete_geometry(Object *ob,
|
||||
SculptSession *ss,
|
||||
const int active_face_set_id,
|
||||
const bool modify_hidden)
|
||||
{
|
||||
|
||||
Mesh *mesh = ob->data;
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
|
||||
BMesh *bm = BM_mesh_create(&allocsize,
|
||||
&((struct BMeshCreateParams){
|
||||
.use_toolflags = true,
|
||||
}));
|
||||
|
||||
BM_mesh_bm_from_me(bm,
|
||||
mesh,
|
||||
(&(struct BMeshFromMeshParams){
|
||||
.calc_face_normal = true,
|
||||
.calc_vert_normal = true,
|
||||
}));
|
||||
|
||||
BM_mesh_elem_table_init(bm, BM_FACE);
|
||||
BM_mesh_elem_table_ensure(bm, BM_FACE);
|
||||
BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
const int face_index = BM_elem_index_get(f);
|
||||
if (!modify_hidden && ss->hide_poly && ss->hide_poly[face_index]) {
|
||||
continue;
|
||||
}
|
||||
BM_elem_flag_set(f, BM_ELEM_TAG, ss->face_sets[face_index] == active_face_set_id);
|
||||
}
|
||||
BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES);
|
||||
BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
|
||||
|
||||
BM_mesh_bm_to_me(nullptr,
|
||||
bm,
|
||||
ob->data,
|
||||
(&(struct BMeshToMeshParams){
|
||||
.calc_object_remap = false,
|
||||
}));
|
||||
|
||||
BM_mesh_free(bm);
|
||||
}
|
||||
|
||||
static void sculpt_face_set_edit_fair_face_set(Object *ob,
|
||||
const int active_face_set_id,
|
||||
const int fair_order)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const int totvert = SCULPT_vertex_count_get(ss);
|
||||
|
||||
Mesh *mesh = ob->data;
|
||||
bool *fair_verts = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices");
|
||||
|
||||
SCULPT_boundary_info_ensure(ob);
|
||||
|
||||
for (int i = 0; i < totvert; i++) {
|
||||
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
|
||||
|
||||
fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) &&
|
||||
SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) &&
|
||||
SCULPT_vertex_has_unique_face_set(ss, vertex);
|
||||
}
|
||||
|
||||
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
|
||||
BKE_mesh_prefair_and_fair_verts(mesh, mvert, fair_verts, fair_order);
|
||||
MEM_freeN(fair_verts);
|
||||
}
|
||||
|
||||
static void sculpt_face_set_apply_edit(Object *ob,
|
||||
const int active_face_set_id,
|
||||
const int mode,
|
||||
const bool modify_hidden)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
switch (mode) {
|
||||
case SCULPT_FACE_SET_EDIT_GROW: {
|
||||
int *prev_face_sets = MEM_dupallocN(ss->face_sets);
|
||||
sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
|
||||
MEM_SAFE_FREE(prev_face_sets);
|
||||
break;
|
||||
}
|
||||
case SCULPT_FACE_SET_EDIT_SHRINK: {
|
||||
int *prev_face_sets = MEM_dupallocN(ss->face_sets);
|
||||
sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
|
||||
MEM_SAFE_FREE(prev_face_sets);
|
||||
break;
|
||||
}
|
||||
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
|
||||
sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden);
|
||||
break;
|
||||
case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
|
||||
sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION);
|
||||
break;
|
||||
case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
|
||||
sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss,
|
||||
const eSculptFaceSetEditMode mode,
|
||||
const bool modify_hidden)
|
||||
{
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
|
||||
/* Dyntopo is not supported. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) {
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
||||
/* Modification of base mesh geometry requires special remapping of multires displacement,
|
||||
* which does not happen here.
|
||||
* Disable delete operation. It can be supported in the future by doing similar displacement
|
||||
* data remapping as what happens in the mesh edit mode. */
|
||||
return false;
|
||||
}
|
||||
if (check_single_face_set(ss, ss->face_sets, !modify_hidden)) {
|
||||
/* Cancel the operator if the mesh only contains one Face Set to avoid deleting the
|
||||
* entire object. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ELEM(mode, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY)) {
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
|
||||
/* TODO: Multires topology representation using grids and duplicates can't be used directly
|
||||
* by the fair algorithm. Multires topology needs to be exposed in a different way or
|
||||
* converted to a mesh for this operation. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sculpt_face_set_edit_modify_geometry(bContext *C,
|
||||
Object *ob,
|
||||
const int active_face_set,
|
||||
const eSculptFaceSetEditMode mode,
|
||||
const bool modify_hidden,
|
||||
wmOperator *op)
|
||||
{
|
||||
ED_sculpt_undo_geometry_begin(ob, op);
|
||||
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden);
|
||||
ED_sculpt_undo_geometry_end(ob);
|
||||
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
||||
}
|
||||
|
||||
static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **nodes, int totnode)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
PBVH *pbvh = ss->pbvh;
|
||||
|
||||
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
|
||||
SCULPT_visibility_sync_all_from_faces(ob);
|
||||
|
||||
for (int i = 0; i < totnode; i++) {
|
||||
BKE_pbvh_node_mark_update_visibility(nodes[i]);
|
||||
}
|
||||
|
||||
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
|
||||
|
||||
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
|
||||
BKE_mesh_flush_hidden_from_verts(ob->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void sculpt_face_set_edit_modify_face_sets(Object *ob,
|
||||
const int active_face_set,
|
||||
const eSculptFaceSetEditMode mode,
|
||||
const bool modify_hidden,
|
||||
wmOperator *op)
|
||||
{
|
||||
PBVH *pbvh = ob->sculpt->pbvh;
|
||||
PBVHNode **nodes;
|
||||
int totnode;
|
||||
BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
|
||||
|
||||
if (!nodes) {
|
||||
return;
|
||||
}
|
||||
SCULPT_undo_push_begin(ob, op);
|
||||
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
|
||||
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden);
|
||||
SCULPT_undo_push_end(ob);
|
||||
face_set_edit_do_post_visibility_updates(ob, nodes, totnode);
|
||||
MEM_freeN(nodes);
|
||||
}
|
||||
|
||||
static void sculpt_face_set_edit_modify_coordinates(bContext *C,
|
||||
Object *ob,
|
||||
const int active_face_set,
|
||||
const eSculptFaceSetEditMode mode,
|
||||
wmOperator *op)
|
||||
{
|
||||
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
PBVH *pbvh = ss->pbvh;
|
||||
PBVHNode **nodes;
|
||||
int totnode;
|
||||
BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
|
||||
SCULPT_undo_push_begin(ob, op);
|
||||
for (int i = 0; i < totnode; i++) {
|
||||
BKE_pbvh_node_mark_update(nodes[i]);
|
||||
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
|
||||
}
|
||||
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false);
|
||||
|
||||
if (ss->deform_modifiers_active || ss->shapekey_active) {
|
||||
SCULPT_flush_stroke_deform(sd, ob, true);
|
||||
}
|
||||
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
|
||||
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
|
||||
SCULPT_undo_push_end(ob);
|
||||
MEM_freeN(nodes);
|
||||
}
|
||||
|
||||
static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
|
||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
|
||||
|
||||
if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
||||
|
||||
/* Update the current active Face Set and Vertex as the operator can be used directly from the
|
||||
* tool without brush cursor. */
|
||||
SculptCursorGeometryInfo sgi;
|
||||
const float mval_fl[2] = {UNPACK2(event->mval)};
|
||||
if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
|
||||
/* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const int active_face_set = SCULPT_active_face_set_get(ss);
|
||||
|
||||
switch (mode) {
|
||||
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
|
||||
sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden, op);
|
||||
break;
|
||||
case SCULPT_FACE_SET_EDIT_GROW:
|
||||
case SCULPT_FACE_SET_EDIT_SHRINK:
|
||||
sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden, op);
|
||||
break;
|
||||
case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
|
||||
case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
|
||||
sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode, op);
|
||||
break;
|
||||
}
|
||||
|
||||
SCULPT_tag_update_overlays(C);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Edit Face Set";
|
||||
ot->idname = "SCULPT_OT_face_set_edit";
|
||||
ot->description = "Edits the current active Face Set";
|
||||
|
||||
/* Api callbacks. */
|
||||
ot->invoke = sculpt_face_set_edit_invoke;
|
||||
ot->poll = SCULPT_mode_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_enum(
|
||||
ot->srna, "mode", prop_sculpt_face_sets_edit_types, SCULPT_FACE_SET_EDIT_GROW, "Mode", "");
|
||||
ot->prop = RNA_def_boolean(ot->srna,
|
||||
"modify_hidden",
|
||||
true,
|
||||
"Modify Hidden",
|
||||
"Apply the edit operation to hidden Face Sets");
|
||||
}
|
||||
Reference in New Issue
Block a user