diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 54 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 15 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_pose.c | 276 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 8 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 21 |
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); |