Sculpt: Grab brush surface falloff

This commit is contained in:
Pablo Dobarro 2021-01-13 20:27:51 +01:00
parent bf632990e0
commit 142aba9485
8 changed files with 449 additions and 106 deletions

View File

@ -703,6 +703,7 @@ def brush_settings(layout, context, brush, popover=False):
elif sculpt_tool == 'GRAB':
layout.prop(brush, "use_grab_active_vertex")
layout.prop(brush, "use_grab_silhouette")
layout.prop(brush, "use_surface_falloff")
elif sculpt_tool == 'PAINT':
row = layout.row(align=True)

View File

@ -474,6 +474,12 @@ typedef struct SculptSession {
struct MeshElemMap *pmap;
int *pmap_mem;
struct MeshElemMap *epmap;
int *epmap_mem;
struct MeshElemMap *vemap;
int *vemap_mem;
/* Mesh Face Sets */
/* Total number of polys of the base mesh. */
int totfaces;

View File

@ -1416,6 +1416,12 @@ static void sculptsession_free_pbvh(Object *object)
MEM_SAFE_FREE(ss->pmap);
MEM_SAFE_FREE(ss->pmap_mem);
MEM_SAFE_FREE(ss->epmap);
MEM_SAFE_FREE(ss->epmap_mem);
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
MEM_SAFE_FREE(ss->persistent_base);
MEM_SAFE_FREE(ss->preview_vert_index_list);
@ -1465,6 +1471,10 @@ void BKE_sculptsession_free(Object *ob)
MEM_SAFE_FREE(ss->pmap);
MEM_SAFE_FREE(ss->pmap_mem);
MEM_SAFE_FREE(ss->epmap);
MEM_SAFE_FREE(ss->epmap_mem);
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
if (ss->bm_log) {
BM_log_free(ss->bm_log);
}

View File

@ -1367,7 +1367,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon
pcontext->prev_active_vertex_index = ss->active_vertex_index;
if (!ups->stroke_active) {
pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update(
C, &gi, mouse, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE));
C, &gi, mouse, pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE);
copy_v3_v3(pcontext->location, gi.location);
copy_v3_v3(pcontext->normal, gi.normal);
}

View File

@ -29,6 +29,7 @@
#include "BLI_ghash.h"
#include "BLI_gsqueue.h"
#include "BLI_hash.h"
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_task.h"
@ -3574,93 +3575,107 @@ static void sculpt_stroke_cache_snap_context_init(bContext *C, Object *ob)
cache->depsgraph = depsgraph;
}
static void sculpt_scene_project_view_ray_init(Object *ob, const int vertex_index, float r_ray_normal[3], float r_ray_origin[3]) {
SculptSession *ss = ob->sculpt;
float world_space_vertex_co[3];
mul_v3_m4v3(world_space_vertex_co, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
sub_v3_v3v3(r_ray_normal, world_space_vertex_co, ss->cache->view_origin);
normalize_v3(r_ray_normal);
copy_v3_v3(r_ray_origin, ss->cache->view_origin);
static void sculpt_scene_project_view_ray_init(Object *ob,
const int vertex_index,
float r_ray_normal[3],
float r_ray_origin[3])
{
SculptSession *ss = ob->sculpt;
float world_space_vertex_co[3];
mul_v3_m4v3(world_space_vertex_co, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
sub_v3_v3v3(r_ray_normal, world_space_vertex_co, ss->cache->view_origin);
normalize_v3(r_ray_normal);
copy_v3_v3(r_ray_origin, ss->cache->view_origin);
}
static void sculpt_scene_project_vertex_normal_ray_init(Object *ob, const int vertex_index, float r_ray_normal[3], float r_ray_origin[3]) {
SculptSession *ss = ob->sculpt;
float vertex_normal[3];
SCULPT_vertex_normal_get(ss, vertex_index, vertex_normal);
mul_v3_m4v3(r_ray_normal, ob->obmat, vertex_normal);
normalize_v3(r_ray_normal);
static void sculpt_scene_project_vertex_normal_ray_init(Object *ob,
const int vertex_index,
float r_ray_normal[3],
float r_ray_origin[3])
{
SculptSession *ss = ob->sculpt;
float vertex_normal[3];
SCULPT_vertex_normal_get(ss, vertex_index, vertex_normal);
mul_v3_m4v3(r_ray_normal, ob->obmat, vertex_normal);
normalize_v3(r_ray_normal);
mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
}
static void sculpt_scene_project_brush_normal_ray_init(Object *ob, const int vertex_index, float r_ray_normal[3], float r_ray_origin[3]) {
SculptSession *ss = ob->sculpt;
mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
mul_v3_m4v3(r_ray_normal, ob->obmat, ss->cache->sculpt_normal);
normalize_v3(r_ray_normal);
static void sculpt_scene_project_brush_normal_ray_init(Object *ob,
const int vertex_index,
float r_ray_normal[3],
float r_ray_origin[3])
{
SculptSession *ss = ob->sculpt;
mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
mul_v3_m4v3(r_ray_normal, ob->obmat, ss->cache->sculpt_normal);
normalize_v3(r_ray_normal);
}
static bool sculpt_scene_project_raycast(SculptSession *ss, const float ray_normal[3], const float ray_origin[3], const bool use_both_directions, float r_loc[3]) {
float hit_co[2][3];
float hit_len_squared[2];
bool any_hit = false;
bool hit = false;
hit = ED_transform_snap_object_project_ray(ss->cache->snap_context,
ss->cache->depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
.use_object_edit_cage = true,
},
ray_origin,
ray_normal,
NULL,
hit_co[0],
NULL);
if (hit) {
hit_len_squared[0] = len_squared_v3v3(hit_co[0], ray_origin);
any_hit |= hit;
}
else {
hit_len_squared[0] = FLT_MAX;
}
static bool sculpt_scene_project_raycast(SculptSession *ss,
const float ray_normal[3],
const float ray_origin[3],
const bool use_both_directions,
float r_loc[3])
{
float hit_co[2][3];
float hit_len_squared[2];
bool any_hit = false;
bool hit = false;
hit = ED_transform_snap_object_project_ray(ss->cache->snap_context,
ss->cache->depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
.use_object_edit_cage = true,
},
ray_origin,
ray_normal,
NULL,
hit_co[0],
NULL);
if (hit) {
hit_len_squared[0] = len_squared_v3v3(hit_co[0], ray_origin);
any_hit |= hit;
}
else {
hit_len_squared[0] = FLT_MAX;
}
if (!use_both_directions) {
copy_v3_v3(r_loc, hit_co[0]);
return any_hit;
}
if (!use_both_directions) {
copy_v3_v3(r_loc, hit_co[0]);
return any_hit;
}
float ray_normal_flip[3];
mul_v3_v3fl(ray_normal_flip, ray_normal, -1.0f);
float ray_normal_flip[3];
mul_v3_v3fl(ray_normal_flip, ray_normal, -1.0f);
hit = ED_transform_snap_object_project_ray(ss->cache->snap_context,
ss->cache->depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
.use_object_edit_cage = true,
},
ray_origin,
ray_normal_flip,
NULL,
hit_co[1],
NULL);
if (hit) {
hit_len_squared[1] = len_squared_v3v3(hit_co[1], ray_origin);
any_hit |= hit;
}
else {
hit_len_squared[1] = FLT_MAX;
}
if (hit_len_squared[0] <= hit_len_squared[1]) {
copy_v3_v3(r_loc, hit_co[0]);
}
else {
copy_v3_v3(r_loc, hit_co[1]);
}
return any_hit;
hit = ED_transform_snap_object_project_ray(ss->cache->snap_context,
ss->cache->depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
.use_object_edit_cage = true,
},
ray_origin,
ray_normal_flip,
NULL,
hit_co[1],
NULL);
if (hit) {
hit_len_squared[1] = len_squared_v3v3(hit_co[1], ray_origin);
any_hit |= hit;
}
else {
hit_len_squared[1] = FLT_MAX;
}
if (hit_len_squared[0] <= hit_len_squared[1]) {
copy_v3_v3(r_loc, hit_co[0]);
}
else {
copy_v3_v3(r_loc, hit_co[1]);
}
return any_hit;
}
static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
@ -3703,14 +3718,14 @@ static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
float ray_origin[3];
bool use_both_directions = false;
switch (brush->scene_project_direction_type) {
case BRUSH_SCENE_PROJECT_DIRECTION_VIEW:
case BRUSH_SCENE_PROJECT_DIRECTION_VIEW:
sculpt_scene_project_view_ray_init(data->ob, vd.index, ray_normal, ray_origin);
break;
case BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL:
case BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL:
sculpt_scene_project_vertex_normal_ray_init(data->ob, vd.index, ray_normal, ray_origin);
use_both_directions = true;
break;
case BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL:
case BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL:
sculpt_scene_project_brush_normal_ray_init(data->ob, vd.index, ray_normal, ray_origin);
use_both_directions = true;
break;
@ -3718,7 +3733,8 @@ static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
float world_space_hit_co[3];
float hit_co[3];
const bool hit = sculpt_scene_project_raycast(ss, ray_normal, ray_origin, use_both_directions, world_space_hit_co);
const bool hit = sculpt_scene_project_raycast(
ss, ray_normal, ray_origin, use_both_directions, world_space_hit_co);
if (!hit) {
continue;
}
@ -4331,38 +4347,49 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE;
const bool use_geodesic_dists = brush->flag2 & BRUSH_USE_SURFACE_FALLOFF;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (sculpt_brush_test_sq_fn(&test, orig_data.co)) {
float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
sqrtf(test.dist),
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
if (grab_silhouette) {
float silhouette_test_dir[3];
normalize_v3_v3(silhouette_test_dir, grab_delta);
if (dot_v3v3(ss->cache->initial_normal, ss->cache->grab_delta_symmetry) < 0.0f) {
mul_v3_fl(silhouette_test_dir, -1.0f);
}
float vno[3];
normal_short_to_float_v3(vno, orig_data.no);
fade *= max_ff(dot_v3v3(vno, silhouette_test_dir), 0.0f);
float dist;
if (use_geodesic_dists) {
dist = ss->cache->geodesic_dists[ss->cache->mirror_symmetry_pass][vd.index];
}
else {
dist = sqrtf(test.dist);
}
float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
dist,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
if (grab_silhouette) {
float silhouette_test_dir[3];
normalize_v3_v3(silhouette_test_dir, grab_delta);
if (dot_v3v3(ss->cache->initial_normal, ss->cache->grab_delta_symmetry) < 0.0f) {
mul_v3_fl(silhouette_test_dir, -1.0f);
}
float vno[3];
normal_short_to_float_v3(vno, orig_data.no);
fade *= max_ff(dot_v3v3(vno, silhouette_test_dir), 0.0f);
}
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
@ -4380,6 +4407,17 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta);
}
if (brush->flag2 & BRUSH_USE_SURFACE_FALLOFF) {
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
const int symm_pass = ss->cache->mirror_symmetry_pass;
float location[3];
flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), symm_pass);
int v = SCULPT_nearest_vertex_get(sd, ob, location, ss->cache->radius, false);
ss->cache->geodesic_dists[symm_pass] = SCULPT_geodesic_from_vertex(
ob, v, ss->cache->initial_radius);
}
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
@ -7078,6 +7116,9 @@ void SCULPT_cache_free(StrokeCache *cache)
if (cache->boundaries[i]) {
SCULPT_boundary_data_free(cache->boundaries[i]);
}
if (cache->geodesic_dists[i]) {
MEM_SAFE_FREE(cache->geodesic_dists[i]);
}
}
if (cache->cloth_sim) {
@ -10229,6 +10270,268 @@ static void SCULPT_OT_mask_init(wmOperatorType *ot)
"");
}
#define SCULPT_GEODESIC_VERTEX_NONE -1
static bool sculpt_geodesic_mesh_test_dist_add(
MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices)
{
if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) {
return false;
}
BLI_assert(dists[v1] != FLT_MAX);
if (dists[v0] <= dists[v1]) {
return false;
}
float dist0;
if (v2 != SCULPT_GEODESIC_VERTEX_NONE) {
BLI_assert(dists[v2] != FLT_MAX);
if (dists[v0] <= dists[v2]) {
return false;
}
dist0 = geodesic_distance_propagate_across_triangle(
mvert[v0].co, mvert[v1].co, mvert[v2].co, dists[v1], dists[v2]);
}
else {
float vec[3];
sub_v3_v3v3(vec, mvert[v1].co, mvert[v0].co);
dist0 = dists[v1] + len_v3(vec);
}
if (dist0 < dists[v0]) {
dists[v0] = dist0;
return true;
}
return false;
}
static float *SCULPT_geodesic_mesh_create(Object *ob,
GSet *initial_vertices,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
Mesh *mesh = BKE_object_get_original_mesh(ob);
const int totvert = mesh->totvert;
const int totedge = mesh->totedge;
const float limit_radius_sq = limit_radius * limit_radius;
MEdge *edges = mesh->medge;
MVert *verts = SCULPT_mesh_deformed_mverts_get(ss);
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag");
if (!ss->epmap) {
BKE_mesh_edge_poly_map_create(&ss->epmap,
&ss->epmap_mem,
mesh->medge,
mesh->totedge,
mesh->mpoly,
mesh->totpoly,
mesh->mloop,
mesh->totloop);
}
if (!ss->vemap) {
BKE_mesh_vert_edge_map_create(
&ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge);
}
BLI_LINKSTACK_DECLARE(queue, int);
BLI_LINKSTACK_DECLARE(queue_next, int);
BLI_LINKSTACK_INIT(queue);
BLI_LINKSTACK_INIT(queue_next);
for (int i = 0; i < totvert; i++) {
if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) {
dists[i] = 0.0f;
}
else {
dists[i] = FLT_MAX;
}
}
BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex");
GSetIterator gs_iter;
GSET_ITER (gs_iter, initial_vertices) {
const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
float *v_co = verts[v].co;
for (int i = 0; i < totvert; i++) {
if (len_squared_v3v3(v_co, verts[i].co) <= limit_radius_sq) {
BLI_BITMAP_ENABLE(affected_vertex, i);
}
}
}
for (int i = 0; i < totedge; i++) {
const int v1 = edges[i].v1;
const int v2 = edges[i].v2;
if (!BLI_BITMAP_TEST(affected_vertex, v1) && !BLI_BITMAP_TEST(affected_vertex, v2)) {
continue;
}
if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) {
BLI_LINKSTACK_PUSH(queue, i);
}
}
do {
int e;
while (e = BLI_LINKSTACK_POP(queue)) {
int v1 = edges[e].v1;
int v2 = edges[e].v2;
if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) {
if (dists[v1] > dists[v2]) {
SWAP(int, v1, v2);
}
sculpt_geodesic_mesh_test_dist_add(
verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices);
}
if (ss->epmap[e].count != 0) {
for (int pi = 0; pi < ss->epmap[e].count; pi++) {
const int poly = ss->epmap[e].indices[pi];
if (ss->face_sets[poly] <= 0) {
continue;
}
const MPoly *mpoly = &mesh->mpoly[poly];
for (int li = 0; li < mpoly->totloop; li++) {
const MLoop *mloop = &mesh->mloop[li + mpoly->loopstart];
const int v_other = mloop->v;
if (ELEM(v_other, v1, v2)) {
continue;
}
if (sculpt_geodesic_mesh_test_dist_add(
verts, v_other, v1, v2, dists, initial_vertices)) {
for (int ei = 0; ei < ss->vemap[v_other].count; ei++) {
const int e_other = ss->vemap[v_other].indices[ei];
int ev_other;
if (edges[e_other].v1 == v_other) {
ev_other = edges[e_other].v2;
}
else {
ev_other = edges[e_other].v1;
}
if (e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other) &&
(ss->epmap[e_other].count == 0 || dists[ev_other] != FLT_MAX)) {
if (BLI_BITMAP_TEST(affected_vertex, v_other) ||
BLI_BITMAP_TEST(affected_vertex, ev_other)) {
BLI_BITMAP_ENABLE(edge_tag, e_other);
BLI_LINKSTACK_PUSH(queue_next, e_other);
}
}
}
}
}
}
}
}
for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
const int e = POINTER_AS_INT(lnk->link);
BLI_BITMAP_DISABLE(edge_tag, e);
}
BLI_LINKSTACK_SWAP(queue, queue_next);
} while (BLI_LINKSTACK_SIZE(queue));
BLI_LINKSTACK_FREE(queue);
BLI_LINKSTACK_FREE(queue_next);
MEM_SAFE_FREE(edge_tag);
MEM_SAFE_FREE(affected_vertex);
return dists;
}
static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices)
{
SculptSession *ss = ob->sculpt;
Mesh *mesh = BKE_object_get_original_mesh(ob);
const int totvert = mesh->totvert;
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
int first_affected = SCULPT_GEODESIC_VERTEX_NONE;
GSetIterator gs_iter;
GSET_ITER (gs_iter, initial_vertices) {
first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
break;
}
if (first_affected == SCULPT_GEODESIC_VERTEX_NONE) {
return dists;
}
float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected);
for (int i = 0; i < totvert; i++) {
dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i));
}
return dists;
}
float *SCULPT_geodesic_distances_create(Object *ob,
GSet *initial_vertices,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius);
case PBVH_BMESH:
case PBVH_GRIDS:
return SCULPT_geodesic_fallback_create(ob, initial_vertices);
}
BLI_assert(false);
return NULL;
}
float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
Object *ob,
const int vertex,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char i = 0; i <= symm; ++i) {
if (SCULPT_is_symmetry_iteration_valid(i, symm)) {
int v = -1;
if (i == 0) {
v = vertex;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
if (v != -1) {
BLI_gset_add(initial_vertices, POINTER_FROM_INT(v));
}
}
}
float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
BLI_gset_free(initial_vertices, NULL);
return dists;
}
float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius)
{
GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex));
float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
BLI_gset_free(initial_vertices, NULL);
return dists;
}
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@ -10248,7 +10551,6 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_create);
WM_operatortype_append(SCULPT_OT_face_sets_change_visibility);
WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors);
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_cloth_filter);
WM_operatortype_append(SCULPT_OT_face_sets_edit);
WM_operatortype_append(SCULPT_OT_face_set_lasso_gesture);

View File

@ -364,6 +364,16 @@ float *SCULPT_boundary_automasking_init(Object *ob,
int propagation_steps,
float *automask_factor);
/* Geodesic distances. */
float *SCULPT_geodesic_distances_create(struct Object *ob,
struct GSet *initial_vertices,
const float limit_radius);
float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd,
struct Object *ob,
const int vertex,
const float limit_radius);
float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius);
/* Filters. */
void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type);
void SCULPT_filter_cache_free(SculptSession *ss);
@ -968,6 +978,9 @@ typedef struct StrokeCache {
bool is_rake_rotation_valid;
struct SculptRakeData rake_data;
/* Geodesic distances. */
float *geodesic_dists[PAINT_SYMM_AREAS];
/* Face Sets */
int paint_face_set;

View File

@ -411,6 +411,7 @@ typedef enum eBrushFlags2 {
BRUSH_CLOTH_USE_COLLISION = (1 << 6),
BRUSH_AREA_RADIUS_PRESSURE = (1 << 7),
BRUSH_GRAB_SILHOUETTE = (1 << 8),
BRUSH_USE_SURFACE_FALLOFF = (1 << 9),
} eBrushFlags2;
typedef enum {

View File

@ -2404,7 +2404,10 @@ static void rna_def_brush(BlenderRNA *brna)
prop = RNA_def_property(srna, "scene_project_direction_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_scene_project_direction_type_items);
RNA_def_property_ui_text(prop, "Project Direction", "Direction that is going to be used to project the vertices into the scene");
RNA_def_property_ui_text(
prop,
"Project Direction",
"Direction that is going to be used to project the vertices into the scene");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "jitter_unit", PROP_ENUM, PROP_NONE); /* as an enum */
@ -3039,6 +3042,13 @@ static void rna_def_brush(BlenderRNA *brna)
prop, "Grab Silhouette", "Grabs trying to automask the silhouette of the object");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_surface_falloff", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_USE_SURFACE_FALLOFF);
RNA_def_property_ui_text(prop,
"Use Surface Falloff",
"Propagate the falloff of the brush trough the surface of the mesh");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_paint_antialiasing", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "sampling_flag", BRUSH_PAINT_ANTIALIASING);
RNA_def_property_ui_text(prop, "Anti-Aliasing", "Smooths the edges of the strokes");