Refactor: Sculpt: Deduplicate symmetry vertex search
Currently code to find symmetrical vertices is duplicated a couple times-- in the expand operator and in the flood fill API. This commit removes the symmetry processing from the flood fill API, which arguably shouldn't need to know about symmetry options in the first place. Pull Request: https://projects.blender.org/blender/blender/pulls/127804
This commit is contained in:
@@ -1106,7 +1106,7 @@ static void fill_topology_automasking_factors_mesh(const Depsgraph &depsgraph,
|
||||
const int active_vert = std::get<int>(ss.active_vert());
|
||||
flood_fill::FillDataMesh flood = flood_fill::FillDataMesh(vert_positions.size());
|
||||
|
||||
flood.add_initial_with_symmetry(depsgraph, ob, *bke::object::pbvh_get(ob), active_vert, radius);
|
||||
flood.add_initial(find_symm_verts_mesh(depsgraph, ob, active_vert, radius));
|
||||
|
||||
const bool use_radius = ss.cache && is_constrained_by_radius(brush);
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
||||
@@ -1131,19 +1131,19 @@ static void fill_topology_automasking_factors_grids(const Sculpt &sd,
|
||||
const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
|
||||
|
||||
const float radius = ss.cache ? ss.cache->radius : std::numeric_limits<float>::max();
|
||||
const SubdivCCGCoord active_vert = std::get<SubdivCCGCoord>(ss.active_vert());
|
||||
const int active_vert = ss.active_vert_index();
|
||||
|
||||
const Span<float3> positions = subdiv_ccg.positions;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
flood_fill::FillDataGrids flood = flood_fill::FillDataGrids(positions.size());
|
||||
|
||||
flood.add_initial_with_symmetry(ob, *bke::object::pbvh_get(ob), subdiv_ccg, active_vert, radius);
|
||||
flood.add_initial(key, find_symm_verts_grids(ob, active_vert, radius));
|
||||
|
||||
const bool use_radius = ss.cache && is_constrained_by_radius(brush);
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
||||
|
||||
float3 location = positions[active_vert.to_index(key)];
|
||||
float3 location = positions[active_vert];
|
||||
|
||||
flood.execute(
|
||||
ob, subdiv_ccg, [&](SubdivCCGCoord from_v, SubdivCCGCoord to_v, bool /*is_duplicate*/) {
|
||||
@@ -1167,7 +1167,7 @@ static void fill_topology_automasking_factors_bmesh(const Sculpt &sd,
|
||||
const int num_verts = BM_mesh_elem_count(&bm, BM_VERT);
|
||||
flood_fill::FillDataBMesh flood = flood_fill::FillDataBMesh(num_verts);
|
||||
|
||||
flood.add_initial_with_symmetry(ob, *bke::object::pbvh_get(ob), active_vert, radius);
|
||||
flood.add_initial(*ss.bm, find_symm_verts_bmesh(ob, BM_elem_index_get(active_vert), radius));
|
||||
|
||||
const bool use_radius = ss.cache && is_constrained_by_radius(brush);
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
||||
|
||||
@@ -550,87 +550,134 @@ static void check_topology_islands(Object &ob, FalloffType falloff_type)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::expand
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
/* Functions implementing different algorithms for initializing falloff values. */
|
||||
|
||||
/**
|
||||
* Utility function to get the closest vertices after flipping an original vertex position for
|
||||
* all symmetry passes.
|
||||
*/
|
||||
static Vector<int> calc_symmetry_vert_indices(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
const ePaintSymmetryFlags symm,
|
||||
const int original_vert)
|
||||
Vector<int> find_symm_verts_mesh(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
const int original_vert,
|
||||
const float max_distance)
|
||||
{
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const float max_distance = FLT_MAX;
|
||||
const bool use_original = false;
|
||||
|
||||
Vector<int> symm_verts;
|
||||
symm_verts.append(original_vert);
|
||||
switch (pbvh.type()) {
|
||||
case bke::pbvh::Type::Mesh: {
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
||||
const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
|
||||
|
||||
const float3 location = positions[original_vert];
|
||||
for (char symm_it = 1; symm_it <= symm; symm_it++) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
|
||||
continue;
|
||||
}
|
||||
const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
|
||||
const std::optional<int> nearest = nearest_vert_calc_mesh(
|
||||
pbvh, positions, hide_vert, symm_location, max_distance, use_original);
|
||||
if (!nearest) {
|
||||
continue;
|
||||
}
|
||||
symm_verts.append(*nearest);
|
||||
}
|
||||
break;
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
||||
const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
|
||||
|
||||
const float3 location = positions[original_vert];
|
||||
for (char symm_it = 1; symm_it <= symm; symm_it++) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
|
||||
continue;
|
||||
}
|
||||
case bke::pbvh::Type::Grids: {
|
||||
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<float3> positions = subdiv_ccg.positions;
|
||||
const float3 location = positions[original_vert];
|
||||
for (char symm_it = 1; symm_it <= symm; symm_it++) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
|
||||
continue;
|
||||
}
|
||||
const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
|
||||
const std::optional<SubdivCCGCoord> nearest = nearest_vert_calc_grids(
|
||||
pbvh, subdiv_ccg, symm_location, max_distance, use_original);
|
||||
if (!nearest) {
|
||||
continue;
|
||||
}
|
||||
symm_verts.append(nearest->to_index(key));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case bke::pbvh::Type::BMesh: {
|
||||
BMesh &bm = *ss.bm;
|
||||
const BMVert *original_bm_vert = BM_vert_at_index(&bm, original_vert);
|
||||
const float3 location = original_bm_vert->co;
|
||||
for (char symm_it = 1; symm_it <= symm; symm_it++) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
|
||||
continue;
|
||||
}
|
||||
const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
|
||||
const std::optional<BMVert *> nearest = nearest_vert_calc_bmesh(
|
||||
pbvh, symm_location, max_distance, use_original);
|
||||
if (!nearest) {
|
||||
continue;
|
||||
}
|
||||
symm_verts.append(BM_elem_index_get(*nearest));
|
||||
}
|
||||
break;
|
||||
const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
|
||||
const std::optional<int> nearest = nearest_vert_calc_mesh(
|
||||
pbvh, positions, hide_vert, symm_location, max_distance, use_original);
|
||||
if (!nearest) {
|
||||
continue;
|
||||
}
|
||||
symm_verts.append(*nearest);
|
||||
}
|
||||
|
||||
std::sort(symm_verts.begin(), symm_verts.end());
|
||||
return symm_verts;
|
||||
}
|
||||
|
||||
Vector<int> find_symm_verts_grids(const Object &object,
|
||||
const int original_vert,
|
||||
const float max_distance)
|
||||
{
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const bool use_original = false;
|
||||
|
||||
Vector<int> symm_verts;
|
||||
symm_verts.append(original_vert);
|
||||
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
const Span<float3> positions = subdiv_ccg.positions;
|
||||
const float3 location = positions[original_vert];
|
||||
for (char symm_it = 1; symm_it <= symm; symm_it++) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
|
||||
continue;
|
||||
}
|
||||
const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
|
||||
const std::optional<SubdivCCGCoord> nearest = nearest_vert_calc_grids(
|
||||
pbvh, subdiv_ccg, symm_location, max_distance, use_original);
|
||||
if (!nearest) {
|
||||
continue;
|
||||
}
|
||||
symm_verts.append(nearest->to_index(key));
|
||||
}
|
||||
|
||||
std::sort(symm_verts.begin(), symm_verts.end());
|
||||
return symm_verts;
|
||||
}
|
||||
|
||||
Vector<int> find_symm_verts_bmesh(const Object &object,
|
||||
const int original_vert,
|
||||
const float max_distance)
|
||||
{
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const bool use_original = false;
|
||||
|
||||
Vector<int> symm_verts;
|
||||
symm_verts.append(original_vert);
|
||||
|
||||
const SculptSession &ss = *object.sculpt;
|
||||
BMesh &bm = *ss.bm;
|
||||
const BMVert *original_bm_vert = BM_vert_at_index(&bm, original_vert);
|
||||
const float3 location = original_bm_vert->co;
|
||||
for (char symm_it = 1; symm_it <= symm; symm_it++) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
|
||||
continue;
|
||||
}
|
||||
const float3 symm_location = symmetry_flip(location, ePaintSymmetryFlags(symm_it));
|
||||
const std::optional<BMVert *> nearest = nearest_vert_calc_bmesh(
|
||||
pbvh, symm_location, max_distance, use_original);
|
||||
if (!nearest) {
|
||||
continue;
|
||||
}
|
||||
symm_verts.append(BM_elem_index_get(*nearest));
|
||||
}
|
||||
|
||||
std::sort(symm_verts.begin(), symm_verts.end());
|
||||
return symm_verts;
|
||||
}
|
||||
|
||||
Vector<int> find_symm_verts(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
const int original_vert,
|
||||
const float max_distance)
|
||||
{
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
switch (pbvh.type()) {
|
||||
case bke::pbvh::Type::Mesh:
|
||||
return find_symm_verts_mesh(depsgraph, object, original_vert, max_distance);
|
||||
case bke::pbvh::Type::Grids:
|
||||
return find_symm_verts_grids(object, original_vert, max_distance);
|
||||
case bke::pbvh::Type::BMesh:
|
||||
return find_symm_verts_bmesh(object, original_vert, max_distance);
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
namespace blender::ed::sculpt_paint::expand {
|
||||
|
||||
/**
|
||||
* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking
|
||||
* symmetry into account.
|
||||
@@ -675,8 +722,7 @@ static Array<float> geodesic_falloff_create(const Depsgraph &depsgraph,
|
||||
Object &ob,
|
||||
const int initial_vert)
|
||||
{
|
||||
const Vector<int> symm_verts = calc_symmetry_vert_indices(
|
||||
depsgraph, ob, SCULPT_mesh_symmetry_xyz_get(ob), initial_vert);
|
||||
const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, initial_vert);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask::from_indices(symm_verts.as_span(), memory);
|
||||
@@ -753,8 +799,7 @@ static Array<float> topology_falloff_create(const Depsgraph &depsgraph,
|
||||
Object &ob,
|
||||
const int initial_vert)
|
||||
{
|
||||
const Vector<int> symm_verts = calc_symmetry_vert_indices(
|
||||
depsgraph, ob, SCULPT_mesh_symmetry_xyz_get(ob), initial_vert);
|
||||
const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, initial_vert);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask::from_indices(symm_verts.as_span(), memory);
|
||||
@@ -789,7 +834,7 @@ static Array<float> normals_falloff_create(const Depsgraph &depsgraph,
|
||||
|
||||
const float3 orig_normal = vert_normals[vert];
|
||||
flood_fill::FillDataMesh flood(totvert);
|
||||
flood.add_initial_with_symmetry(depsgraph, ob, pbvh, vert, FLT_MAX);
|
||||
flood.add_initial(find_symm_verts(depsgraph, ob, vert));
|
||||
flood.execute(ob, vert_to_face_map, [&](const int from_vert, const int to_vert) {
|
||||
const float3 &from_normal = vert_normals[from_vert];
|
||||
const float3 &to_normal = vert_normals[to_vert];
|
||||
@@ -808,8 +853,7 @@ static Array<float> normals_falloff_create(const Depsgraph &depsgraph,
|
||||
const Span<float3> normals = subdiv_ccg.normals;
|
||||
const float3 orig_normal = normals[vert];
|
||||
flood_fill::FillDataGrids flood(totvert);
|
||||
flood.add_initial_with_symmetry(
|
||||
ob, pbvh, subdiv_ccg, SubdivCCGCoord::from_index(key, vert), FLT_MAX);
|
||||
flood.add_initial(key, find_symm_verts(depsgraph, ob, vert));
|
||||
flood.execute(
|
||||
ob,
|
||||
subdiv_ccg,
|
||||
@@ -837,7 +881,7 @@ static Array<float> normals_falloff_create(const Depsgraph &depsgraph,
|
||||
flood_fill::FillDataBMesh flood(totvert);
|
||||
BMVert *orig_vert = BM_vert_at_index(ss.bm, vert);
|
||||
const float3 orig_normal = orig_vert->no;
|
||||
flood.add_initial_with_symmetry(ob, pbvh, orig_vert, FLT_MAX);
|
||||
flood.add_initial(*ss.bm, find_symm_verts(depsgraph, ob, vert));
|
||||
flood.execute(ob, [&](BMVert *from_bm_vert, BMVert *to_bm_vert) {
|
||||
const float3 &from_normal = from_bm_vert->no;
|
||||
const float3 &to_normal = to_bm_vert->no;
|
||||
@@ -875,8 +919,7 @@ static Array<float> spherical_falloff_create(const Depsgraph &depsgraph,
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
Array<float> dists(SCULPT_vertex_count_get(object));
|
||||
|
||||
const Vector<int> symm_verts = calc_symmetry_vert_indices(
|
||||
depsgraph, object, SCULPT_mesh_symmetry_xyz_get(object), vert);
|
||||
const Vector<int> symm_verts = find_symm_verts(depsgraph, object, vert);
|
||||
|
||||
switch (pbvh.type()) {
|
||||
case bke::pbvh::Type::Mesh: {
|
||||
@@ -950,8 +993,7 @@ static Array<float> boundary_topology_falloff_create(const Depsgraph &depsgraph,
|
||||
Object &ob,
|
||||
const int inititial_vert)
|
||||
{
|
||||
const Vector<int> symm_verts = calc_symmetry_vert_indices(
|
||||
depsgraph, ob, SCULPT_mesh_symmetry_xyz_get(ob), inititial_vert);
|
||||
const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, inititial_vert);
|
||||
|
||||
BitVector<> boundary_verts(SCULPT_vertex_count_get(ob));
|
||||
for (const int vert : symm_verts) {
|
||||
@@ -996,8 +1038,7 @@ static Array<float> diagonals_falloff_create(const Depsgraph &depsgraph,
|
||||
return dists;
|
||||
}
|
||||
|
||||
const Vector<int> symm_verts = calc_symmetry_vert_indices(
|
||||
depsgraph, ob, SCULPT_mesh_symmetry_xyz_get(ob), vert);
|
||||
const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, vert);
|
||||
|
||||
/* Search and mask as visited the initial vertices using the enabled symmetry passes. */
|
||||
BitVector<> visited_verts(totvert);
|
||||
@@ -2122,7 +2163,7 @@ static void find_active_connected_components_from_vert(const Depsgraph &depsgrap
|
||||
|
||||
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
||||
|
||||
const Vector<int> symm_verts = calc_symmetry_vert_indices(depsgraph, ob, symm, initial_vertex);
|
||||
const Vector<int> symm_verts = find_symm_verts(depsgraph, ob, initial_vertex);
|
||||
|
||||
int valid_index = 0;
|
||||
for (char symm_it = 0; symm_it <= symm; symm_it++) {
|
||||
|
||||
@@ -24,16 +24,37 @@ void FillDataMesh::add_initial(const int vertex)
|
||||
this->queue.push(vertex);
|
||||
}
|
||||
|
||||
void FillDataMesh::add_initial(const Span<int> verts)
|
||||
{
|
||||
for (const int vert : verts) {
|
||||
this->add_initial(vert);
|
||||
}
|
||||
}
|
||||
|
||||
void FillDataGrids::add_initial(const SubdivCCGCoord vertex)
|
||||
{
|
||||
this->queue.push(vertex);
|
||||
}
|
||||
|
||||
void FillDataGrids::add_initial(const CCGKey &key, const Span<int> verts)
|
||||
{
|
||||
for (const int vert : verts) {
|
||||
this->add_initial(SubdivCCGCoord::from_index(key, vert));
|
||||
}
|
||||
}
|
||||
|
||||
void FillDataBMesh::add_initial(BMVert *vertex)
|
||||
{
|
||||
this->queue.push(vertex);
|
||||
}
|
||||
|
||||
void FillDataBMesh::add_initial(BMesh &bm, const Span<int> verts)
|
||||
{
|
||||
for (const int vert : verts) {
|
||||
this->add_initial(BM_vert_at_index(&bm, vert));
|
||||
}
|
||||
}
|
||||
|
||||
void FillDataMesh::add_and_skip_initial(const int vertex)
|
||||
{
|
||||
this->queue.push(vertex);
|
||||
@@ -52,116 +73,6 @@ void FillDataBMesh::add_and_skip_initial(BMVert *vertex, const int index)
|
||||
this->visited_verts[index].set();
|
||||
}
|
||||
|
||||
void FillDataMesh::add_initial_with_symmetry(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
const bke::pbvh::Tree &pbvh,
|
||||
const int vertex,
|
||||
const float radius)
|
||||
{
|
||||
if (radius <= 0.0f) {
|
||||
this->add_initial(vertex);
|
||||
return;
|
||||
}
|
||||
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
||||
const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, object);
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
VArraySpan<bool> hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
|
||||
|
||||
const char symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
for (char i = 0; i <= symm; ++i) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<int> vert_to_add;
|
||||
if (i == 0) {
|
||||
vert_to_add = vertex;
|
||||
}
|
||||
else {
|
||||
BLI_assert(radius > 0.0f);
|
||||
const float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
|
||||
float3 location = symmetry_flip(vert_positions[vertex], ePaintSymmetryFlags(i));
|
||||
vert_to_add = nearest_vert_calc_mesh(
|
||||
pbvh, vert_positions, hide_vert, location, radius_squared, false);
|
||||
}
|
||||
|
||||
if (vert_to_add) {
|
||||
this->add_initial(*vert_to_add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillDataGrids::add_initial_with_symmetry(const Object &object,
|
||||
const bke::pbvh::Tree &pbvh,
|
||||
const SubdivCCG &subdiv_ccg,
|
||||
const SubdivCCGCoord vertex,
|
||||
const float radius)
|
||||
{
|
||||
if (radius <= 0.0f) {
|
||||
this->add_initial(vertex);
|
||||
return;
|
||||
}
|
||||
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
|
||||
const char symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
for (char i = 0; i <= symm; ++i) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<SubdivCCGCoord> vert_to_add;
|
||||
if (i == 0) {
|
||||
vert_to_add = vertex;
|
||||
}
|
||||
else {
|
||||
BLI_assert(radius > 0.0f);
|
||||
const float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
|
||||
float3 location = symmetry_flip(subdiv_ccg.positions[vertex.to_index(key)],
|
||||
ePaintSymmetryFlags(i));
|
||||
vert_to_add = nearest_vert_calc_grids(pbvh, subdiv_ccg, location, radius_squared, false);
|
||||
}
|
||||
|
||||
if (vert_to_add) {
|
||||
this->add_initial(*vert_to_add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillDataBMesh::add_initial_with_symmetry(const Object &object,
|
||||
const bke::pbvh::Tree &pbvh,
|
||||
BMVert *vertex,
|
||||
const float radius)
|
||||
{
|
||||
if (radius <= 0.0f) {
|
||||
this->add_initial(vertex);
|
||||
return;
|
||||
}
|
||||
|
||||
const char symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
for (char i = 0; i <= symm; ++i) {
|
||||
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<BMVert *> vert_to_add;
|
||||
if (i == 0) {
|
||||
vert_to_add = vertex;
|
||||
}
|
||||
else {
|
||||
BLI_assert(radius > 0.0f);
|
||||
const float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
|
||||
float3 location = symmetry_flip(vertex->co, ePaintSymmetryFlags(i));
|
||||
vert_to_add = nearest_vert_calc_bmesh(pbvh, location, radius_squared, false);
|
||||
}
|
||||
|
||||
if (vert_to_add) {
|
||||
this->add_initial(*vert_to_add);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillDataMesh::execute(Object &object,
|
||||
const GroupedSpan<int> vert_to_face_map,
|
||||
FunctionRef<bool(int from_v, int to_v)> func)
|
||||
|
||||
@@ -30,12 +30,8 @@ struct FillDataMesh {
|
||||
FillDataMesh(int size) : visited_verts(size) {}
|
||||
|
||||
void add_initial(int vertex);
|
||||
void add_initial(Span<int> verts);
|
||||
void add_and_skip_initial(int vertex);
|
||||
void add_initial_with_symmetry(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
const bke::pbvh::Tree &pbvh,
|
||||
int vertex,
|
||||
float radius);
|
||||
void execute(Object &object,
|
||||
GroupedSpan<int> vert_to_face_map,
|
||||
FunctionRef<bool(int from_v, int to_v)> func);
|
||||
@@ -48,12 +44,8 @@ struct FillDataGrids {
|
||||
FillDataGrids(int size) : visited_verts(size) {}
|
||||
|
||||
void add_initial(SubdivCCGCoord vertex);
|
||||
void add_initial(const CCGKey &key, Span<int> verts);
|
||||
void add_and_skip_initial(SubdivCCGCoord vertex, int index);
|
||||
void add_initial_with_symmetry(const Object &object,
|
||||
const bke::pbvh::Tree &pbvh,
|
||||
const SubdivCCG &subdiv_ccg,
|
||||
SubdivCCGCoord vertex,
|
||||
float radius);
|
||||
void execute(
|
||||
Object &object,
|
||||
const SubdivCCG &subdiv_ccg,
|
||||
@@ -67,11 +59,8 @@ struct FillDataBMesh {
|
||||
FillDataBMesh(int size) : visited_verts(size) {}
|
||||
|
||||
void add_initial(BMVert *vertex);
|
||||
void add_initial(BMesh &bm, Span<int> verts);
|
||||
void add_and_skip_initial(BMVert *vertex, int index);
|
||||
void add_initial_with_symmetry(const Object &object,
|
||||
const bke::pbvh::Tree &pbvh,
|
||||
BMVert *vertex,
|
||||
float radius);
|
||||
void execute(Object &object, FunctionRef<bool(BMVert *from_v, BMVert *to_v)> func);
|
||||
};
|
||||
|
||||
|
||||
@@ -622,6 +622,25 @@ void SCULPT_flip_quat_by_symm_area(float quat[4],
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
/**
|
||||
* Utility functions to get the closest vertices after flipping an original vertex position for
|
||||
* all symmetry passes. The returned vector is sorted.
|
||||
*/
|
||||
Vector<int> find_symm_verts_mesh(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
int original_vert,
|
||||
float max_distance = std::numeric_limits<float>::max());
|
||||
Vector<int> find_symm_verts_grids(const Object &object,
|
||||
int original_vert,
|
||||
float max_distance = std::numeric_limits<float>::max());
|
||||
Vector<int> find_symm_verts_bmesh(const Object &object,
|
||||
int original_vert,
|
||||
float max_distance = std::numeric_limits<float>::max());
|
||||
Vector<int> find_symm_verts(const Depsgraph &depsgraph,
|
||||
const Object &object,
|
||||
int original_vert,
|
||||
float max_distance = std::numeric_limits<float>::max());
|
||||
|
||||
bool node_fully_masked_or_hidden(const bke::pbvh::Node &node);
|
||||
bool node_in_sphere(const bke::pbvh::Node &node,
|
||||
const float3 &location,
|
||||
|
||||
@@ -655,12 +655,10 @@ static void calc_pose_origin_and_factor_mesh(const Depsgraph &depsgraph,
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
||||
const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
|
||||
const Span<float3> positions_eval = bke::pbvh::vert_positions_eval(depsgraph, object);
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
|
||||
/* Calculate the pose rotation point based on the boundaries of the brush factor. */
|
||||
flood_fill::FillDataMesh flood(positions_eval.size());
|
||||
flood.add_initial_with_symmetry(
|
||||
depsgraph, object, pbvh, std::get<int>(ss.active_vert()), radius);
|
||||
flood.add_initial(find_symm_verts_mesh(depsgraph, object, ss.active_vert_index(), radius));
|
||||
|
||||
const int symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
|
||||
@@ -707,14 +705,12 @@ static void calc_pose_origin_and_factor_grids(Object &object,
|
||||
BLI_assert(!r_pose_factor.is_empty());
|
||||
|
||||
const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
|
||||
const Span<float3> positions = subdiv_ccg.positions;
|
||||
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
||||
/* Calculate the pose rotation point based on the boundaries of the brush factor. */
|
||||
flood_fill::FillDataGrids flood(positions.size());
|
||||
flood.add_initial_with_symmetry(
|
||||
object, pbvh, subdiv_ccg, std::get<SubdivCCGCoord>(ss.active_vert()), radius);
|
||||
flood.add_initial(key, find_symm_verts_grids(object, ss.active_vert_index(), radius));
|
||||
|
||||
const int symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
|
||||
@@ -766,11 +762,9 @@ static void calc_pose_origin_and_factor_bmesh(Object &object,
|
||||
BLI_assert(!r_pose_factor.is_empty());
|
||||
SCULPT_vertex_random_access_ensure(object);
|
||||
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
|
||||
/* Calculate the pose rotation point based on the boundaries of the brush factor. */
|
||||
flood_fill::FillDataBMesh flood(BM_mesh_elem_count(ss.bm, BM_VERT));
|
||||
flood.add_initial_with_symmetry(object, pbvh, std::get<BMVert *>(ss.active_vert()), radius);
|
||||
flood.add_initial(*ss.bm, find_symm_verts_bmesh(object, ss.active_vert_index(), radius));
|
||||
|
||||
const int symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
|
||||
@@ -1034,15 +1028,13 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_mesh(const Depsgraph &de
|
||||
|
||||
SegmentData current_data = {std::get<int>(ss.active_vert()), SCULPT_FACE_SET_NONE};
|
||||
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const int symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
Vector<int> neighbors;
|
||||
for (const int i : ik_chain->segments.index_range()) {
|
||||
const bool is_first_iteration = i == 0;
|
||||
|
||||
flood_fill::FillDataMesh flood_fill(vert_positions.size());
|
||||
flood_fill.add_initial_with_symmetry(
|
||||
depsgraph, object, pbvh, current_data.vert, std::numeric_limits<float>::max());
|
||||
flood_fill.add_initial(find_symm_verts_mesh(depsgraph, object, current_data.vert, radius));
|
||||
|
||||
visited_face_sets.add(current_data.face_set);
|
||||
|
||||
@@ -1174,7 +1166,6 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_grids(Object &object,
|
||||
SubdivCCGCoord vert;
|
||||
int face_set;
|
||||
};
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
|
||||
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
||||
const OffsetIndices<int> faces = mesh.faces();
|
||||
@@ -1203,8 +1194,8 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_grids(Object &object,
|
||||
const bool is_first_iteration = i == 0;
|
||||
|
||||
flood_fill::FillDataGrids flood_fill(grids_num);
|
||||
flood_fill.add_initial_with_symmetry(
|
||||
object, pbvh, subdiv_ccg, current_data.vert, std::numeric_limits<float>::max());
|
||||
flood_fill.add_initial(key,
|
||||
find_symm_verts_grids(object, current_data.vert.to_index(key), radius));
|
||||
|
||||
visited_face_sets.add(current_data.face_set);
|
||||
|
||||
@@ -1358,15 +1349,14 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_bmesh(Object &object,
|
||||
|
||||
SegmentData current_data = {std::get<BMVert *>(ss.active_vert()), SCULPT_FACE_SET_NONE};
|
||||
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const int symm = SCULPT_mesh_symmetry_xyz_get(object);
|
||||
Vector<BMVert *, 64> neighbors;
|
||||
for (const int i : ik_chain->segments.index_range()) {
|
||||
const bool is_first_iteration = i == 0;
|
||||
|
||||
flood_fill::FillDataBMesh flood_fill(verts_num);
|
||||
flood_fill.add_initial_with_symmetry(
|
||||
object, pbvh, current_data.vert, std::numeric_limits<float>::max());
|
||||
flood_fill.add_initial(
|
||||
*ss.bm, find_symm_verts_bmesh(object, BM_elem_index_get(current_data.vert), radius));
|
||||
|
||||
visited_face_sets.add(current_data.face_set);
|
||||
|
||||
@@ -1599,7 +1589,6 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_fk_mesh(const Depsgraph
|
||||
std::unique_ptr<IKChain> ik_chain = ik_chain_new(1, mesh.verts_num);
|
||||
|
||||
const int active_vert = std::get<int>(ss.active_vert());
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
|
||||
const int active_face_set = face_set::active_face_set_get(object);
|
||||
|
||||
@@ -1652,7 +1641,7 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_fk_mesh(const Depsgraph
|
||||
ik_chain->grab_delta_offset = ik_chain->segments[0].head - initial_location;
|
||||
|
||||
flood_fill::FillDataMesh weight_floodfill(mesh.verts_num);
|
||||
weight_floodfill.add_initial_with_symmetry(depsgraph, object, pbvh, active_vert, radius);
|
||||
weight_floodfill.add_initial(find_symm_verts_mesh(depsgraph, object, active_vert, radius));
|
||||
MutableSpan<float> fk_weights = ik_chain->segments[0].weights;
|
||||
weight_floodfill.execute(object, vert_to_face_map, [&](int /*from_v*/, int to_v) {
|
||||
fk_weights[to_v] = 1.0f;
|
||||
@@ -1685,7 +1674,6 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_fk_grids(const Depsgraph
|
||||
std::unique_ptr<IKChain> ik_chain = ik_chain_new(1, grids_num);
|
||||
|
||||
const SubdivCCGCoord active_vert = std::get<SubdivCCGCoord>(ss.active_vert());
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const int active_vert_index = ss.active_vert_index();
|
||||
|
||||
const int active_face_set = face_set::active_face_set_get(object);
|
||||
@@ -1751,7 +1739,8 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_fk_grids(const Depsgraph
|
||||
ik_chain->grab_delta_offset = ik_chain->segments[0].head - initial_location;
|
||||
|
||||
flood_fill::FillDataGrids weight_floodfill(grids_num);
|
||||
weight_floodfill.add_initial_with_symmetry(object, pbvh, subdiv_ccg, active_vert, radius);
|
||||
weight_floodfill.add_initial(key,
|
||||
find_symm_verts_grids(object, active_vert.to_index(key), radius));
|
||||
MutableSpan<float> fk_weights = ik_chain->segments[0].weights;
|
||||
weight_floodfill.execute(
|
||||
object,
|
||||
@@ -1784,7 +1773,6 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_fk_bmesh(const Depsgraph
|
||||
std::unique_ptr<IKChain> ik_chain = ik_chain_new(1, verts_num);
|
||||
|
||||
BMVert *active_vert = std::get<BMVert *>(ss.active_vert());
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
||||
const int active_vert_index = BM_elem_index_get(active_vert);
|
||||
|
||||
const int active_face_set = face_set::active_face_set_get(object);
|
||||
@@ -1841,7 +1829,8 @@ static std::unique_ptr<IKChain> ik_chain_init_face_sets_fk_bmesh(const Depsgraph
|
||||
ik_chain->grab_delta_offset = ik_chain->segments[0].head - initial_location;
|
||||
|
||||
flood_fill::FillDataBMesh weight_floodfill(verts_num);
|
||||
weight_floodfill.add_initial_with_symmetry(object, pbvh, active_vert, radius);
|
||||
weight_floodfill.add_initial(
|
||||
*ss.bm, find_symm_verts_bmesh(object, BM_elem_index_get(active_vert), radius));
|
||||
MutableSpan<float> fk_weights = ik_chain->segments[0].weights;
|
||||
weight_floodfill.execute(object, [&](BMVert * /*from_v*/, BMVert *to_v) {
|
||||
int to_v_i = BM_elem_index_get(to_v);
|
||||
|
||||
Reference in New Issue
Block a user