Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c54
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h15
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c276
-rw-r--r--source/blender/makesdna/DNA_brush_types.h8
-rw-r--r--source/blender/makesrna/intern/rna_brush.c21
5 files changed, 324 insertions, 50 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index fe4aa971b55..6ccd197c908 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -196,21 +196,15 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index)
return 0.0f;
}
-static int SCULPT_active_vertex_get(SculptSession *ss)
+int SCULPT_active_vertex_get(SculptSession *ss)
{
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- return ss->active_vertex_index;
- case PBVH_BMESH:
- return ss->active_vertex_index;
- case PBVH_GRIDS:
- return ss->active_vertex_index;
+ if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) {
+ return ss->active_vertex_index;
}
-
return 0;
}
-static const float *SCULPT_active_vertex_co_get(SculptSession *ss)
+const float *SCULPT_active_vertex_co_get(SculptSession *ss)
{
return SCULPT_vertex_co_get(ss, SCULPT_active_vertex_get(ss));
}
@@ -373,7 +367,7 @@ static void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_se
}
}
-static int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
+int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -394,7 +388,7 @@ static int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
return 0;
}
-static bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
+bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -464,7 +458,7 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
}
}
-static bool sculpt_vertex_has_unique_face_set(SculptSession *ss, int index)
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -819,11 +813,35 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood)
flood->visited_vertices = MEM_callocN(vertex_count * sizeof(char), "visited vertices");
}
-static void sculpt_floodfill_add_initial(SculptFloodFill *flood, int index)
+void sculpt_floodfill_add_initial(SculptFloodFill *flood, int index)
{
BLI_gsqueue_push(flood->queue, &index);
}
+void SCULPT_floodfill_add_initial_with_symmetry(
+ Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius)
+{
+ /* Add active vertex and symmetric vertices to the queue. */
+ const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ for (char i = 0; i <= symm; ++i) {
+ if (SCULPT_is_symmetry_iteration_valid(i, symm)) {
+ int v = -1;
+ if (i == 0) {
+ v = index;
+ }
+ else if (radius > 0.0f) {
+ float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
+ float location[3];
+ flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i);
+ v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false);
+ }
+ if (v != -1) {
+ sculpt_floodfill_add_initial(flood, v);
+ }
+ }
+ }
+}
+
void SCULPT_floodfill_add_active(
Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, float radius)
{
@@ -3639,7 +3657,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
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)) {
+ 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,
@@ -3856,7 +3874,7 @@ void SCULPT_relax_vertex(SculptSession *ss,
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
if (!filter_boundary_face_sets ||
- (filter_boundary_face_sets && !sculpt_vertex_has_unique_face_set(ss, ni.index))) {
+ (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) {
add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
count++;
}
@@ -9478,7 +9496,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
/* Skip the edges of the face set when relaxing or smoothing. There is a relax face set
* option to relax the boindaries independently. */
if (filter_type == MESH_FILTER_RELAX) {
- if (!sculpt_vertex_has_unique_face_set(ss, vd.index)) {
+ if (!SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
continue;
}
}
@@ -9492,7 +9510,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
}
if (filter_type == MESH_FILTER_RELAX_FACE_SETS) {
- if (relax_face_sets == sculpt_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
continue;
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 7f93355e269..fe56283dbcb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -125,6 +125,9 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
} \
((void)0)
+int SCULPT_active_vertex_get(SculptSession *ss);
+const float *SCULPT_active_vertex_co_get(SculptSession *ss);
+
/* Sculpt Original Data */
typedef struct {
struct BMLog *bm_log;
@@ -143,6 +146,11 @@ typedef struct {
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
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 */
void sculpt_pbvh_clear(Object *ob);
void sculpt_dyntopo_node_layers_add(struct SculptSession *ss);
@@ -196,6 +204,13 @@ void SCULPT_floodfill_add_active(struct Sculpt *sd,
struct SculptSession *ss,
SculptFloodFill *flood,
float radius);
+void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd,
+ struct Object *ob,
+ struct SculptSession *ss,
+ SculptFloodFill *flood,
+ int index,
+ float radius);
+void sculpt_floodfill_add_initial(SculptFloodFill *flood, int index);
void SCULPT_floodfill_execute(
struct SculptSession *ss,
SculptFloodFill *flood,
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 8b842c07e79..8342c6c9d1b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -351,9 +351,29 @@ typedef struct PoseFloodFillData {
float *pose_factor;
float pose_origin[3];
int tot_co;
+
+ int current_face_set;
+ int next_face_set;
+ int prev_face_set;
+ int next_vertex;
+
+ bool next_face_set_found;
+
+ /* Store the visited face sets to avoid going back when calculating the chain. */
+ GSet *visited_face_sets;
+
+ /* In face sets origin mode, each vertex can only be assigned to one face set. */
+ bool *is_weighted;
+
+ bool is_first_iteration;
+
+ /* Fallback origin. If we can't find any face set to continue, use the position of all vertices
+ * that have the current face set. */
+ float fallback_origin[3];
+ int fallback_count;
} PoseFloodFillData;
-static bool pose_floodfill_cb(
+static bool pose_topology_floodfill_cb(
SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
{
PoseFloodFillData *data = userdata;
@@ -377,6 +397,100 @@ static bool pose_floodfill_cb(
return false;
}
+static bool pose_face_sets_floodfill_cb(
+ SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+{
+ PoseFloodFillData *data = userdata;
+
+ const int index = to_v;
+ bool visit_next = false;
+
+ const float *co = SCULPT_vertex_co_get(ss, index);
+ const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry(
+ co, data->pose_initial_co, data->symm) &&
+ !is_duplicate;
+
+ /* First iteration. Continue expanding using topology until a vertex is outside the brush radius
+ * to determine the first face set. */
+ if (data->current_face_set == SCULPT_FACE_SET_NONE) {
+
+ data->pose_factor[index] = 1.0f;
+ data->is_weighted[index] = true;
+
+ if (sculpt_pose_brush_is_vertex_inside_brush_radius(
+ co, data->pose_initial_co, data->radius, data->symm)) {
+ const int visited_face_set = SCULPT_vertex_face_set_get(ss, index);
+ BLI_gset_add(data->visited_face_sets, visited_face_set);
+ }
+ else if (symmetry_check) {
+ data->current_face_set = SCULPT_vertex_face_set_get(ss, index);
+ BLI_gset_add(data->visited_face_sets, data->current_face_set);
+ }
+ return true;
+ }
+
+ /* We already have a current face set, so we can start checking the face sets of the vertices. */
+ /* In the first iteration we need to check all face sets we already visited as the flood fill may
+ * still not be finished in some of them. */
+ bool is_vertex_valid = false;
+ if (data->is_first_iteration) {
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, data->visited_face_sets) {
+ const int visited_face_set = BLI_gsetIterator_getKey(&gs_iter);
+ is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set);
+ }
+ }
+ else {
+ is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set);
+ }
+
+ if (is_vertex_valid) {
+
+ if (!data->is_weighted[index]) {
+ data->pose_factor[index] = 1.0f;
+ data->is_weighted[index] = true;
+ visit_next = true;
+ }
+
+ /* Fallback origin accumulation. */
+ if (symmetry_check) {
+ add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index));
+ data->fallback_count++;
+ }
+
+ if (symmetry_check && !SCULPT_vertex_has_unique_face_set(ss, index)) {
+
+ /* We only add coordiates for calculating the origin when it is possible to go from this
+ * vertex to another vertex in a valid face set for the next iteration. */
+ bool count_as_boundary = false;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index);
+
+ /* Check if we can get a valid face set for the next iteration from this neighbor. */
+ if (SCULPT_vertex_has_unique_face_set(ss, ni.index) &&
+ !BLI_gset_haskey(data->visited_face_sets, next_face_set_candidate)) {
+ if (!data->next_face_set_found) {
+ data->next_face_set = next_face_set_candidate;
+ data->next_vertex = ni.index;
+ data->next_face_set_found = true;
+ }
+ count_as_boundary = true;
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ /* Origin accumulation. */
+ if (count_as_boundary) {
+ add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index));
+ data->tot_co++;
+ }
+ }
+ }
+ return visit_next;
+}
+
/* Public functions. */
/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush
@@ -407,7 +521,7 @@ void SCULPT_pose_calc_pose_data(Sculpt *sd,
};
zero_v3(fdata.pose_origin);
copy_v3_v3(fdata.pose_initial_co, initial_location);
- SCULPT_floodfill_execute(ss, &flood, pose_floodfill_cb, &fdata);
+ SCULPT_floodfill_execute(ss, &flood, pose_topology_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
if (fdata.tot_co > 0) {
@@ -454,12 +568,47 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
-SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
- Object *ob,
- SculptSession *ss,
- Brush *br,
- const float initial_location[3],
- const float radius)
+/* Init the IK chain with empty weights. */
+static SculptPoseIKChain *pose_ik_chain_new(const int totsegments, const int totverts)
+{
+ SculptPoseIKChain *ik_chain = MEM_callocN(sizeof(SculptPoseIKChain), "Pose IK Chain");
+ ik_chain->tot_segments = totsegments;
+ ik_chain->segments = MEM_callocN(totsegments * sizeof(SculptPoseIKChainSegment),
+ "Pose IK Chain Segments");
+ for (int i = 0; i < totsegments; i++) {
+ ik_chain->segments[i].weights = MEM_callocN(totverts * sizeof(float), "Pose IK weights");
+ }
+ return ik_chain;
+}
+
+/* Init the origin/head pairs of all the segments from the calculated origins. */
+static void pose_ik_chain_origin_heads_init(SculptPoseIKChain *ik_chain,
+ const float initial_location[3])
+{
+ float origin[3];
+ float head[3];
+ for (int i = 0; i < ik_chain->tot_segments; i++) {
+ if (i == 0) {
+ copy_v3_v3(head, initial_location);
+ copy_v3_v3(origin, ik_chain->segments[i].orig);
+ }
+ else {
+ copy_v3_v3(head, ik_chain->segments[i - 1].orig);
+ copy_v3_v3(origin, ik_chain->segments[i].orig);
+ }
+ copy_v3_v3(ik_chain->segments[i].orig, origin);
+ copy_v3_v3(ik_chain->segments[i].initial_orig, origin);
+ copy_v3_v3(ik_chain->segments[i].initial_head, head);
+ ik_chain->segments[i].len = len_v3v3(head, origin);
+ }
+}
+
+SculptPoseIKChain *SCULPT_pose_ik_chain_init_topology(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ Brush *br,
+ const float initial_location[3],
+ const float radius)
{
const float chain_segment_len = radius * (1.0f + br->pose_offset);
@@ -480,14 +629,7 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
pose_factor_grow[nearest_vertex_index] = 1.0f;
- /* Init the IK chain with empty weights. */
- SculptPoseIKChain *ik_chain = MEM_callocN(sizeof(SculptPoseIKChain), "Pose IK Chain");
- ik_chain->tot_segments = br->pose_ik_segments;
- ik_chain->segments = MEM_callocN(ik_chain->tot_segments * sizeof(SculptPoseIKChainSegment),
- "Pose IK Chain Segments");
- for (int i = 0; i < br->pose_ik_segments; i++) {
- ik_chain->segments[i].weights = MEM_callocN(totvert * sizeof(float), "Pose IK weights");
- }
+ SculptPoseIKChain *ik_chain = pose_ik_chain_new(br->pose_ik_segments, totvert);
/* Calculate the first segment in the chain using the brush radius and the pose origin offset. */
copy_v3_v3(next_chain_segment_target, initial_location);
@@ -532,30 +674,102 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
}
}
- /* Init the origin/head pairs of all the segments from the calculated origins. */
- float origin[3];
- float head[3];
- for (int i = 0; i < ik_chain->tot_segments; i++) {
- if (i == 0) {
- copy_v3_v3(head, initial_location);
- copy_v3_v3(origin, ik_chain->segments[i].orig);
+ pose_ik_chain_origin_heads_init(ik_chain, initial_location);
+
+ MEM_freeN(pose_factor_grow);
+ MEM_freeN(pose_factor_grow_prev);
+
+ return ik_chain;
+}
+
+SculptPoseIKChain *SCULPT_pose_ik_chain_init_face_sets(
+ Sculpt *sd, Object *ob, SculptSession *ss, Brush *br, const float radius)
+{
+
+ int totvert = SCULPT_vertex_count_get(ss);
+
+ SculptPoseIKChain *ik_chain = pose_ik_chain_new(br->pose_ik_segments, totvert);
+
+ GSet *visited_face_sets = BLI_gset_int_new_ex("visited_face_sets", ik_chain->tot_segments);
+
+ bool *is_weighted = MEM_callocN(sizeof(bool) * totvert, "weighted");
+
+ int current_face_set = SCULPT_FACE_SET_NONE;
+ int prev_face_set = SCULPT_FACE_SET_NONE;
+
+ int current_vertex = SCULPT_active_vertex_get(ss);
+
+ for (int s = 0; s < ik_chain->tot_segments; s++) {
+
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, current_vertex, FLT_MAX);
+
+ BLI_gset_add(visited_face_sets, current_face_set);
+
+ PoseFloodFillData fdata = {
+ .radius = radius,
+ .symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL,
+ .pose_factor = ik_chain->segments[s].weights,
+ .tot_co = 0,
+ .fallback_count = 0,
+ .current_face_set = current_face_set,
+ .prev_face_set = prev_face_set,
+ .visited_face_sets = visited_face_sets,
+ .is_weighted = is_weighted,
+ .next_face_set_found = false,
+ .is_first_iteration = s == 0,
+ };
+ zero_v3(fdata.pose_origin);
+ zero_v3(fdata.fallback_origin);
+ copy_v3_v3(fdata.pose_initial_co, SCULPT_vertex_co_get(ss, current_vertex));
+ SCULPT_floodfill_execute(ss, &flood, pose_face_sets_floodfill_cb, &fdata);
+ SCULPT_floodfill_free(&flood);
+
+ if (fdata.tot_co > 0) {
+ mul_v3_fl(fdata.pose_origin, 1.0f / (float)fdata.tot_co);
+ copy_v3_v3(ik_chain->segments[s].orig, fdata.pose_origin);
+ }
+ else if (fdata.fallback_count > 0) {
+ mul_v3_fl(fdata.fallback_origin, 1.0f / (float)fdata.fallback_count);
+ copy_v3_v3(ik_chain->segments[s].orig, fdata.fallback_origin);
}
else {
- copy_v3_v3(head, ik_chain->segments[i - 1].orig);
- copy_v3_v3(origin, ik_chain->segments[i].orig);
+ zero_v3(ik_chain->segments[s].orig);
}
- copy_v3_v3(ik_chain->segments[i].orig, origin);
- copy_v3_v3(ik_chain->segments[i].initial_orig, origin);
- copy_v3_v3(ik_chain->segments[i].initial_head, head);
- ik_chain->segments[i].len = len_v3v3(head, origin);
+
+ prev_face_set = fdata.current_face_set;
+ current_face_set = fdata.next_face_set;
+ current_vertex = fdata.next_vertex;
}
- MEM_freeN(pose_factor_grow);
- MEM_freeN(pose_factor_grow_prev);
+ BLI_gset_free(visited_face_sets, NULL);
+
+ pose_ik_chain_origin_heads_init(ik_chain, SCULPT_active_vertex_co_get(ss));
+
+ MEM_SAFE_FREE(is_weighted);
return ik_chain;
}
+SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ Brush *br,
+ const float initial_location[3],
+ const float radius)
+{
+ switch (br->pose_origin_type) {
+ case BRUSH_POSE_ORIGIN_TOPOLOGY:
+ return SCULPT_pose_ik_chain_init_topology(sd, ob, ss, br, initial_location, radius);
+ break;
+ case BRUSH_POSE_ORIGIN_FACE_SETS:
+ return SCULPT_pose_ik_chain_init_face_sets(sd, ob, ss, br, radius);
+ break;
+ }
+ return NULL;
+}
+
void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br)
{
PBVHNode **nodes;
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 59e9d3be58d..95d9216f550 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -291,6 +291,11 @@ typedef enum eBrushClothForceFalloffType {
BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1,
} eBrushClothForceFalloffType;
+typedef enum eBrushPoseOriginType {
+ BRUSH_POSE_ORIGIN_TOPOLOGY = 0,
+ BRUSH_POSE_ORIGIN_FACE_SETS = 1,
+} eBrushPoseOriginType;
+
/* Gpencilsettings.Vertex_mode */
typedef enum eGp_Vertex_Mode {
/* Affect to Stroke only. */
@@ -406,7 +411,7 @@ typedef struct Brush {
/** Source for fill tool color gradient application. */
char gradient_fill_mode;
- char _pad0[5];
+ char _pad0[1];
/** Projection shape (sphere, circle). */
char falloff_shape;
@@ -467,6 +472,7 @@ typedef struct Brush {
float pose_offset;
int pose_smooth_iterations;
int pose_ik_segments;
+ int pose_origin_type;
/* cloth */
int cloth_deform_type;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 2a777c53cab..9615ac47738 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1805,6 +1805,20 @@ static void rna_def_brush(BlenderRNA *brna)
0,
"Surface",
"Smooths the surface of the mesh, preserving the volue"},
+ };
+
+ static const EnumPropertyItem brush_pose_origin_type_items[] = {
+ {BRUSH_POSE_ORIGIN_TOPOLOGY,
+ "TOPOLOGY",
+ 0,
+ "Topology",
+ "Sets the rotation origin automatically using the topology and shape of the mesh as a "
+ "guide"},
+ {BRUSH_POSE_ORIGIN_FACE_SETS,
+ "FACE_SETS",
+ 0,
+ "Face Sets",
+ "Creates a pose segment per face sets, starting from the active face set"},
{0, NULL, 0, NULL, NULL},
};
@@ -1928,6 +1942,13 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "pose_origin_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, brush_pose_origin_type_items);
+ RNA_def_property_ui_text(prop,
+ "Rotation Origins",
+ "Method to set the rotation origins for the segments of the brush");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "jitter_unit", PROP_ENUM, PROP_NONE); /* as an enum */
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, brush_jitter_unit_items);