Cleanup: Restructure and add doxygen blocks to sculpt_boundary.cc

Part of #118145

This commit restructures sculpt_boundary.cc to improve readability and
aid in iteratively removing usages of PBVHVertRef.

Pull Request: https://projects.blender.org/blender/blender/pulls/124958
This commit is contained in:
Sean Kim
2024-07-18 18:12:14 +02:00
committed by Sean Kim
parent 2fa7e7eda3
commit cb4aecca80

View File

@@ -33,6 +33,117 @@
namespace blender::ed::sculpt_paint::boundary {
/**
* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside
* the given radius, if it exists.
*/
static PBVHVertRef get_closest_boundary_vert(SculptSession &ss,
const PBVHVertRef initial_vert,
const float radius);
/**
* This function is used to check where the propagation should stop when calculating the boundary,
* as well as to check if the initial vertex is valid.
*/
static bool is_vert_in_editable_boundary(SculptSession &ss, const PBVHVertRef initial_vert);
/**
* Determines the indices of a boundary.
*/
static void indices_init(SculptSession &ss,
SculptBoundary &boundary,
const bool init_boundary_distances,
const PBVHVertRef initial_boundary_vert);
/**
* This functions initializes all data needed to calculate falloffs and deformation from the
* boundary into the mesh into a #SculptBoundaryEditInfo array. This includes how many steps are
* needed to go from a boundary vertex to an interior vertex and which vertex of the boundary is
* the closest one.
*/
static void edit_data_init(SculptSession &ss,
SculptBoundary &boundary,
const PBVHVertRef initial_vert,
const float radius);
std::unique_ptr<SculptBoundary> data_init(Object &object,
const Brush *brush,
const PBVHVertRef initial_vert,
const float radius)
{
SculptSession &ss = *object.sculpt;
if (initial_vert.i == PBVH_REF_NONE) {
return nullptr;
}
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(object);
const PBVHVertRef boundary_initial_vert = get_closest_boundary_vert(ss, initial_vert, radius);
if (boundary_initial_vert.i == BOUNDARY_VERTEX_NONE) {
return nullptr;
}
/* Starting from a vertex that is the limit of a boundary is ambiguous, so return nullptr instead
* of forcing a random active boundary from a corner. */
if (!is_vert_in_editable_boundary(ss, initial_vert)) {
return nullptr;
}
std::unique_ptr<SculptBoundary> boundary = std::make_unique<SculptBoundary>();
*boundary = {};
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
false;
indices_init(ss, *boundary, init_boundary_distances, boundary_initial_vert);
const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius;
edit_data_init(ss, *boundary, boundary_initial_vert, boundary_radius);
return boundary;
}
static bool is_vert_in_editable_boundary(SculptSession &ss, const PBVHVertRef initial_vert)
{
if (!hide::vert_visible_get(ss, initial_vert)) {
return false;
}
int neighbor_count = 0;
int boundary_vertex_count = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vert, ni) {
if (hide::vert_visible_get(ss, ni.vertex)) {
neighbor_count++;
if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
boundary_vertex_count++;
}
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Corners are ambiguous as it can't be decide which boundary should be active. The flood fill
* should also stop at corners. */
if (neighbor_count <= 2) {
return false;
}
/* Non manifold geometry in the mesh boundary.
* The deformation result will be unpredictable and not very useful. */
if (boundary_vertex_count > 2) {
return false;
}
return true;
}
/* -------------------------------------------------------------------- */
/** \name Nearest Boundary Vert
* \{ */
struct BoundaryInitialVertexFloodFillData {
PBVHVertRef initial_vert;
int initial_vert_i;
@@ -76,8 +187,6 @@ static bool initial_vert_floodfill_fn(SculptSession &ss,
return len_sq < data->radius_sq;
}
/* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside
* the given radius, if it exists. */
static PBVHVertRef get_closest_boundary_vert(SculptSession &ss,
const PBVHVertRef initial_vert,
const float radius)
@@ -106,6 +215,12 @@ static PBVHVertRef get_closest_boundary_vert(SculptSession &ss,
return fdata.boundary_initial_vert;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Boundary Index Calculation
* \{ */
/* Used to allocate the memory of the boundary index arrays. This was decided considered the most
* common use cases for the brush deformers, taking into account how many vertices those
* deformations usually need in the boundary. */
@@ -127,44 +242,6 @@ static void add_index(SculptBoundary &boundary,
}
};
/**
* This function is used to check where the propagation should stop when calculating the boundary,
* as well as to check if the initial vertex is valid.
*/
static bool is_vert_in_editable_boundary(SculptSession &ss, const PBVHVertRef initial_vert)
{
if (!hide::vert_visible_get(ss, initial_vert)) {
return false;
}
int neighbor_count = 0;
int boundary_vertex_count = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vert, ni) {
if (hide::vert_visible_get(ss, ni.vertex)) {
neighbor_count++;
if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
boundary_vertex_count++;
}
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Corners are ambiguous as it can't be decide which boundary should be active. The flood fill
* should also stop at corners. */
if (neighbor_count <= 2) {
return false;
}
/* Non manifold geometry in the mesh boundary.
* The deformation result will be unpredictable and not very useful. */
if (boundary_vertex_count > 2) {
return false;
}
return true;
}
/* Flood fill that adds to the boundary data all the vertices from a boundary and its duplicates.
*/
@@ -258,12 +335,12 @@ static void indices_init(SculptSession &ss,
BLI_gset_free(included_verts, nullptr);
}
/**
* This functions initializes all data needed to calculate falloffs and deformation from the
* boundary into the mesh into a #SculptBoundaryEditInfo array. This includes how many steps are
* needed to go from a boundary vertex to an interior vertex and which vertex of the boundary is
* the closest one.
*/
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit Data Calculation
* \{ */
static void edit_data_init(SculptSession &ss,
SculptBoundary &boundary,
const PBVHVertRef initial_vert,
@@ -386,156 +463,13 @@ static void edit_data_init(SculptSession &ss,
}
}
/* This functions assigns a falloff factor to each one of the SculptBoundaryEditInfo structs based
* on the brush curve and its propagation steps. The falloff goes from the boundary into the mesh.
*/
static void init_falloff(SculptSession &ss,
SculptBoundary &boundary,
const Brush &brush,
const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
BKE_curvemapping_init(brush.curve);
/** \} */
for (int i = 0; i < totvert; i++) {
if (boundary.edit_info[i].propagation_steps_num != -1) {
boundary.edit_info[i].strength_factor = BKE_brush_curve_strength(
&brush, boundary.edit_info[i].propagation_steps_num, boundary.max_propagation_steps);
}
if (boundary.edit_info[i].original_vertex_i ==
BKE_pbvh_vertex_to_index(*ss.pbvh, boundary.initial_vert))
{
/* All vertices that are propagated from the original vertex won't be affected by the
* boundary falloff, so there is no need to calculate anything else. */
continue;
}
if (boundary.distance.is_empty()) {
/* There are falloff modes that do not require to modify the previously calculated falloff
* based on boundary distances. */
continue;
}
const float boundary_distance = boundary.distance[boundary.edit_info[i].original_vertex_i];
float falloff_distance = 0.0f;
float direction = 1.0f;
switch (brush.boundary_falloff_type) {
case BRUSH_BOUNDARY_FALLOFF_RADIUS:
falloff_distance = boundary_distance;
break;
case BRUSH_BOUNDARY_FALLOFF_LOOP: {
const int div = boundary_distance / radius;
const float mod = fmodf(boundary_distance, radius);
falloff_distance = div % 2 == 0 ? mod : radius - mod;
break;
}
case BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT: {
const int div = boundary_distance / radius;
const float mod = fmodf(boundary_distance, radius);
falloff_distance = div % 2 == 0 ? mod : radius - mod;
/* Inverts the falloff in the intervals 1 2 5 6 9 10 ... etc. */
if (((div - 1) & 2) == 0) {
direction = -1.0f;
}
break;
}
case BRUSH_BOUNDARY_FALLOFF_CONSTANT:
/* For constant falloff distances are not allocated, so this should never happen. */
BLI_assert(false);
}
boundary.edit_info[i].strength_factor *= direction * BKE_brush_curve_strength(
&brush, falloff_distance, radius);
}
}
std::unique_ptr<SculptBoundary> data_init(Object &object,
const Brush *brush,
const PBVHVertRef initial_vert,
const float radius)
{
SculptSession &ss = *object.sculpt;
if (initial_vert.i == PBVH_REF_NONE) {
return nullptr;
}
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(object);
const PBVHVertRef boundary_initial_vert = get_closest_boundary_vert(ss, initial_vert, radius);
if (boundary_initial_vert.i == BOUNDARY_VERTEX_NONE) {
return nullptr;
}
/* Starting from a vertex that is the limit of a boundary is ambiguous, so return nullptr instead
* of forcing a random active boundary from a corner. */
if (!is_vert_in_editable_boundary(ss, initial_vert)) {
return nullptr;
}
std::unique_ptr<SculptBoundary> boundary = std::make_unique<SculptBoundary>();
*boundary = {};
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
false;
indices_init(ss, *boundary, init_boundary_distances, boundary_initial_vert);
const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius;
edit_data_init(ss, *boundary, boundary_initial_vert, boundary_radius);
return boundary;
}
std::unique_ptr<SculptBoundaryPreview> preview_data_init(Object &object,
const Brush *brush,
const PBVHVertRef initial_vert,
const float radius)
{
SculptSession &ss = *object.sculpt;
if (initial_vert.i == PBVH_REF_NONE) {
return nullptr;
}
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(object);
const PBVHVertRef boundary_initial_vert = get_closest_boundary_vert(ss, initial_vert, radius);
if (boundary_initial_vert.i == BOUNDARY_VERTEX_NONE) {
return nullptr;
}
/* Starting from a vertex that is the limit of a boundary is ambiguous, so return nullptr instead
* of forcing a random active boundary from a corner. */
if (!is_vert_in_editable_boundary(ss, initial_vert)) {
return nullptr;
}
SculptBoundary boundary;
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
false;
indices_init(ss, boundary, init_boundary_distances, boundary_initial_vert);
const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius;
edit_data_init(ss, boundary, boundary_initial_vert, boundary_radius);
std::unique_ptr<SculptBoundaryPreview> preview = std::make_unique<SculptBoundaryPreview>();
preview->edges = boundary.edges;
preview->pivot_position = boundary.pivot_position;
preview->initial_vert_position = boundary.initial_vert_position;
return preview;
}
/* -------------------------------------------------------------------- */
/** \name Specialized Initialization
*
* Methods for initializing specialized data inside SculptBoundary
* \{ */
/* These functions initialize the required vectors for the desired deformation using the
* SculptBoundaryEditInfo. They calculate the data using the vertices that have the
@@ -626,6 +560,14 @@ static void twist_data_init(SculptSession &ss, SculptBoundary &boundary)
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Brush Actions
*
* Actual functions related to modifying vertices.
* \{ */
static float displacement_from_grab_delta_get(SculptSession &ss, SculptBoundary &boundary)
{
float plane[4];
@@ -900,6 +842,71 @@ static void brush_smooth_task(Object &ob, const Brush &brush, PBVHNode *node)
BKE_pbvh_vertex_iter_end;
}
/* This functions assigns a falloff factor to each one of the SculptBoundaryEditInfo structs based
* on the brush curve and its propagation steps. The falloff goes from the boundary into the mesh.
*/
static void init_falloff(SculptSession &ss,
SculptBoundary &boundary,
const Brush &brush,
const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
BKE_curvemapping_init(brush.curve);
for (int i = 0; i < totvert; i++) {
if (boundary.edit_info[i].propagation_steps_num != -1) {
boundary.edit_info[i].strength_factor = BKE_brush_curve_strength(
&brush, boundary.edit_info[i].propagation_steps_num, boundary.max_propagation_steps);
}
if (boundary.edit_info[i].original_vertex_i ==
BKE_pbvh_vertex_to_index(*ss.pbvh, boundary.initial_vert))
{
/* All vertices that are propagated from the original vertex won't be affected by the
* boundary falloff, so there is no need to calculate anything else. */
continue;
}
if (boundary.distance.is_empty()) {
/* There are falloff modes that do not require to modify the previously calculated falloff
* based on boundary distances. */
continue;
}
const float boundary_distance = boundary.distance[boundary.edit_info[i].original_vertex_i];
float falloff_distance = 0.0f;
float direction = 1.0f;
switch (brush.boundary_falloff_type) {
case BRUSH_BOUNDARY_FALLOFF_RADIUS:
falloff_distance = boundary_distance;
break;
case BRUSH_BOUNDARY_FALLOFF_LOOP: {
const int div = boundary_distance / radius;
const float mod = fmodf(boundary_distance, radius);
falloff_distance = div % 2 == 0 ? mod : radius - mod;
break;
}
case BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT: {
const int div = boundary_distance / radius;
const float mod = fmodf(boundary_distance, radius);
falloff_distance = div % 2 == 0 ? mod : radius - mod;
/* Inverts the falloff in the intervals 1 2 5 6 9 10 ... etc. */
if (((div - 1) & 2) == 0) {
direction = -1.0f;
}
break;
}
case BRUSH_BOUNDARY_FALLOFF_CONSTANT:
/* For constant falloff distances are not allocated, so this should never happen. */
BLI_assert(false);
}
boundary.edit_info[i].strength_factor *= direction * BKE_brush_curve_strength(
&brush, falloff_distance, radius);
}
}
void do_boundary_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
{
SculptSession &ss = *ob.sculpt;
@@ -994,6 +1001,59 @@ void do_boundary_brush(const Sculpt &sd, Object &ob, Span<PBVHNode *> nodes)
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Boundary Drawing
*
* Helper methods to draw boundary information.
* \{ */
std::unique_ptr<SculptBoundaryPreview> preview_data_init(Object &object,
const Brush *brush,
const PBVHVertRef initial_vert,
const float radius)
{
SculptSession &ss = *object.sculpt;
if (initial_vert.i == PBVH_REF_NONE) {
return nullptr;
}
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(object);
const PBVHVertRef boundary_initial_vert = get_closest_boundary_vert(ss, initial_vert, radius);
if (boundary_initial_vert.i == BOUNDARY_VERTEX_NONE) {
return nullptr;
}
/* Starting from a vertex that is the limit of a boundary is ambiguous, so return nullptr instead
* of forcing a random active boundary from a corner. */
if (!is_vert_in_editable_boundary(ss, initial_vert)) {
return nullptr;
}
SculptBoundary boundary;
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
false;
indices_init(ss, boundary, init_boundary_distances, boundary_initial_vert);
const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius;
edit_data_init(ss, boundary, boundary_initial_vert, boundary_radius);
std::unique_ptr<SculptBoundaryPreview> preview = std::make_unique<SculptBoundaryPreview>();
preview->edges = boundary.edges;
preview->pivot_position = boundary.pivot_position;
preview->initial_vert_position = boundary.initial_vert_position;
return preview;
}
void edges_preview_draw(const uint gpuattr,
SculptSession &ss,
const float outline_col[3],
@@ -1025,4 +1085,6 @@ void pivot_line_preview_draw(const uint gpuattr, SculptSession &ss)
immEnd();
}
/** \} */
} // namespace blender::ed::sculpt_paint::boundary