Sculpt Expand: Cleanup, comments

This commit is contained in:
Pablo Dobarro 2021-02-16 20:38:06 +01:00
parent 1a11ac5e34
commit acd99f12e7
3 changed files with 258 additions and 176 deletions

View File

@ -10471,8 +10471,6 @@ static void SCULPT_OT_mask_init(wmOperatorType *ot)
"");
}
static int sculpt_reset_brushes_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);

View File

@ -75,8 +75,36 @@
#include <math.h>
#include <stdlib.h>
/* Sculpt Expand. */
/* Operator for creating selections and patterns in Scupt Mode. Expand can create masks, face sets
* and fill vertex colors. */
/* The main functinality of the operator
* - The operator initializes a value per vertex, called "falloff". There are multiple algorithms
* to generate these falloff values which will create different patterns in the result when using
* the operator. These falloff values require algorithms that rely on mesh connectivity, so they
* are only valid on parts of the mesh that are in the same connected component as the given
* initial vertices. If needed, these falloff values are propagated from vertex or grids into the
* base mesh faces.
* - On each modal callback, the operator gets the active vertex and face and gets its falloff
* value from its precalculated falloff. This is now the active falloff value.
* - Using the active falloff value and the settings of the expand operation (which can be modified
* during execution using the modal keymap), the operator loops over all elements in the mesh to
* check if they are enabled of not.
* - Based on each element state after evaluating the settings, the desired mesh data (mask, face
* sets, colors...) is updated.
*/
/* Used for defining an invalid vertex state (for example, when the cursor is not over the mesh).
*/
#define SCULPT_EXPAND_VERTEX_NONE -1
/* Defines how much each time the texture distortion is increased/decreased when using the modal
* keymap. */
#define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP 0.01f
#define SCULPT_EXPAND_LOOP_THRESHOLD 0.00001f
/* Expand Modal Keymap. */
enum {
SCULPT_EXPAND_MODAL_CONFIRM = 1,
SCULPT_EXPAND_MODAL_CANCEL,
@ -118,9 +146,10 @@ static EnumPropertyItem prop_sculpt_expand_target_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
#define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP 0.01f
#define SCULPT_EXPAND_LOOP_THRESHOLD 0.00001f
/* Functions for getting the state of mesh elements (vertices and base mesh faces). */
/* Returns true if the vertex is in a connected component with correctly initialized falloff
* values. */
static bool sculpt_expand_is_vert_in_active_compoment(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
@ -133,6 +162,8 @@ static bool sculpt_expand_is_vert_in_active_compoment(SculptSession *ss,
return false;
}
/* Returns true if the face is in a connected component with correctly initialized falloff values.
*/
static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
const int f)
@ -141,49 +172,55 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
return sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, loop->v);
}
/* Returns the falloff value of a vertex. This function includes texture distortion, which is not
* precomputed into the initial falloff values. */
static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
ExpandCache *expand_cache,
const int i)
const int v)
{
if (expand_cache->texture_distortion_strength == 0.0f) {
return expand_cache->falloff_factor[i];
return expand_cache->falloff[v];
}
if (!expand_cache->brush->mtex.tex) {
return expand_cache->falloff_factor[i];
return expand_cache->falloff[v];
}
float rgba[4];
const float *vertex_co = SCULPT_vertex_co_get(ss, i);
const float *vertex_co = SCULPT_vertex_co_get(ss, v);
const float avg = BKE_brush_sample_tex_3d(
expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool);
const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength *
expand_cache->max_falloff_factor;
return expand_cache->falloff_factor[i] + distortion;
expand_cache->max_falloff;
return expand_cache->falloff[v] + distortion;
}
static float sculpt_expand_max_vertex_falloff_factor_get(ExpandCache *expand_cache)
/* Returns the maximum valid falloff value stored in the falloff array, taking the maximum possible
* texture distortion into account. */
static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache)
{
if (expand_cache->texture_distortion_strength == 0.0f) {
return expand_cache->max_falloff_factor;
return expand_cache->max_falloff;
}
if (!expand_cache->brush->mtex.tex) {
return expand_cache->max_falloff_factor;
return expand_cache->max_falloff;
}
return expand_cache->max_falloff_factor +
(0.5f * expand_cache->texture_distortion_strength * expand_cache->max_falloff_factor);
return expand_cache->max_falloff +
(0.5f * expand_cache->texture_distortion_strength * expand_cache->max_falloff);
}
static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int i)
/* Main function to get the state of a vertex for the current state and settings of a ExpandCache.
*/
static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v)
{
if (!SCULPT_vertex_visible_get(ss, i)) {
if (!SCULPT_vertex_visible_get(ss, v)) {
return false;
}
if (!sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, i)) {
if (!sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, v)) {
return false;
}
@ -194,17 +231,17 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
bool enabled = false;
if (expand_cache->snap) {
const int face_set = SCULPT_vertex_face_set_get(ss, i);
const int face_set = SCULPT_vertex_face_set_get(ss, v);
enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set));
}
else {
const float max_falloff_factor = sculpt_expand_max_vertex_falloff_factor_get(expand_cache);
const float max_falloff_factor = sculpt_expand_max_vertex_falloff_get(expand_cache);
const float loop_len = (max_falloff_factor / expand_cache->loop_count) +
SCULPT_EXPAND_LOOP_THRESHOLD;
const float vertex_falloff_factor = sculpt_expand_falloff_value_vertex_get(
ss, expand_cache, i);
const float active_factor = fmod(expand_cache->active_factor, loop_len);
ss, expand_cache, v);
const float active_factor = fmod(expand_cache->active_falloff, loop_len);
const float falloff_factor = fmod(vertex_falloff_factor, loop_len);
enabled = falloff_factor < active_factor;
@ -216,6 +253,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
return enabled;
}
/* Main function to get the state of a face for the current state and settings of a ExpandCache. */
static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f)
{
if (ss->face_sets[f] <= 0) {
@ -237,15 +275,15 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set));
}
else {
const float loop_len = (expand_cache->max_face_falloff_factor / expand_cache->loop_count) +
const float loop_len = (expand_cache->max_face_falloff / expand_cache->loop_count) +
SCULPT_EXPAND_LOOP_THRESHOLD;
const float active_factor = fmod(expand_cache->active_factor, loop_len);
const float falloff_factor = fmod(expand_cache->face_falloff_factor[f], loop_len);
const float active_factor = fmod(expand_cache->active_falloff, loop_len);
const float falloff_factor = fmod(expand_cache->face_falloff[f], loop_len);
enabled = falloff_factor < active_factor;
}
if (expand_cache->falloff_factor_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) {
if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) {
if (ss->face_sets[f] == expand_cache->initial_active_face_set) {
enabled = false;
}
@ -258,20 +296,22 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
return enabled;
}
static float sculpt_expand_gradient_falloff_get(SculptSession *ss,
ExpandCache *expand_cache,
const int i)
/* For target modes that support gradients, this function returns the corresponding gradient value
* for a enabled vertex. */
static float sculpt_expand_gradient_value_get(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
{
if (!expand_cache->falloff_gradient) {
return 1.0f;
}
const float max_falloff_factor = sculpt_expand_max_vertex_falloff_factor_get(expand_cache);
const float max_falloff_factor = sculpt_expand_max_vertex_falloff_get(expand_cache);
const float loop_len = (max_falloff_factor / expand_cache->loop_count) +
SCULPT_EXPAND_LOOP_THRESHOLD;
const float vertex_falloff_factor = sculpt_expand_falloff_value_vertex_get(ss, expand_cache, i);
const float active_factor = fmod(expand_cache->active_factor, loop_len);
const float vertex_falloff_factor = sculpt_expand_falloff_value_vertex_get(ss, expand_cache, v);
const float active_factor = fmod(expand_cache->active_falloff, loop_len);
const float falloff_factor = fmod(vertex_falloff_factor, loop_len);
float linear_falloff;
@ -290,11 +330,17 @@ static float sculpt_expand_gradient_falloff_get(SculptSession *ss,
return BKE_brush_curve_strength(expand_cache->brush, linear_falloff, 1.0f);
}
static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int vertex)
/* Functions implementing differnt algorithms for initializing the falloff values. */
/* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking
* symmetry into account. */
static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v)
{
return SCULPT_geodesic_from_vertex_and_symm(sd, ob, vertex, FLT_MAX);
return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX);
}
/* Topology: Initializes the falloff using a floodfill operation, increasing the falloff value by 1
* when visiting a new vertex. */
typedef struct ExpandFloodFillData {
float original_normal[3];
float edge_sensitivity;
@ -316,7 +362,7 @@ static bool expand_topology_floodfill_cb(
return true;
}
static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int vertex)
static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@ -324,7 +370,7 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, vertex, FLT_MAX);
SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, v, FLT_MAX);
ExpandFloodFillData fdata;
fdata.dists = dists;
@ -335,6 +381,8 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons
return dists;
}
/* Normals: */
static bool mask_expand_normal_floodfill_cb(
SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
{
@ -405,7 +453,10 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
return dists;
}
static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, const int vertex)
/* Spherical: Initialices the falloff based on the distance from a vertex, taking symmetry into
* account. */
static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@ -420,17 +471,17 @@ static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, con
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
int v = SCULPT_EXPAND_VERTEX_NONE;
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
v = vertex;
symm_vertex = v;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), symm_it);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
flip_v3_v3(location, SCULPT_vertex_co_get(ss, v), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
if (v != -1) {
const float *co = SCULPT_vertex_co_get(ss, v);
if (symm_vertex != -1) {
const float *co = SCULPT_vertex_co_get(ss, symm_vertex);
for (int i = 0; i < totvert; i++) {
dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i)));
}
@ -440,31 +491,34 @@ static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, con
return dists;
}
static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd,
Object *ob,
const int vertex)
/* Boundary: This falloff mode uses the code from sculpt_boundary to initialize the closest mesh
* boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it
* stays parallalel to the boundary, increasing the falloff value by 1 on each step. */
static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd, Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
GSQueue *queue = BLI_gsqueue_new(sizeof(int));
/* Search and initialize a boundary per symmetry pass, them mark those vertices as visited. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char symm_it = 0; symm_it <= symm; symm_it++) {
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
int v = SCULPT_EXPAND_VERTEX_NONE;
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
v = vertex;
symm_vertex = v;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), symm_it);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
flip_v3_v3(location, SCULPT_vertex_co_get(ss, v), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, v, FLT_MAX);
SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX);
for (int i = 0; i < boundary->num_vertices; i++) {
BLI_gsqueue_push(queue, &boundary->vertices[i]);
BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]);
@ -472,10 +526,12 @@ static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd,
SCULPT_boundary_data_free(boundary);
}
/* If there are no boundaries, return a falloff with all values set to 0. */
if (BLI_gsqueue_is_empty(queue)) {
return dists;
}
/* Propagate the values from the boundaries to the rest of the mesh. */
while (!BLI_gsqueue_is_empty(queue)) {
int v;
BLI_gsqueue_pop(queue, &v);
@ -492,28 +548,28 @@ static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd,
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
}
for (int i = 0; i < totvert; i++) {
if (BLI_BITMAP_TEST(visited_vertices, i)) {
continue;
}
dists[i] = FLT_MAX;
}
BLI_gsqueue_free(queue);
MEM_freeN(visited_vertices);
return dists;
}
static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, const int vertex)
/* Topology diagonals. This falloff is similar to topology, but it also considers the diagonals of
* the base mesh faces when checking a vertex neighbor. For this reason, this is not implement
* using the general flodfill and sculpt neighbors accessors. */
static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, const int v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
/* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialize for
* Multires. It also does not make sense to implement it for dyntopo as the result will be the
* same as Topology falloff. */
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
return dists;
}
/* Search and mask as visited the initial vertices using the enabled symmetry passes. */
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
GSQueue *queue = BLI_gsqueue_new(sizeof(int));
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@ -521,24 +577,25 @@ static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, con
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
int v = SCULPT_EXPAND_VERTEX_NONE;
int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
if (symm_it == 0) {
v = vertex;
symm_vertex = v;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), symm_it);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
flip_v3_v3(location, SCULPT_vertex_co_get(ss, v), symm_it);
symm_vertex = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
BLI_gsqueue_push(queue, &v);
BLI_BITMAP_ENABLE(visited_vertices, v);
BLI_gsqueue_push(queue, &symm_vertex);
BLI_BITMAP_ENABLE(visited_vertices, symm_vertex);
}
if (BLI_gsqueue_is_empty(queue)) {
return dists;
}
/* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */
Mesh *mesh = ob->data;
while (!BLI_gsqueue_is_empty(queue)) {
int v;
@ -557,38 +614,42 @@ static float *sculpt_expand_diagonals_falloff_create(Sculpt *sd, Object *ob, con
}
}
for (int i = 0; i < totvert; i++) {
if (BLI_BITMAP_TEST(visited_vertices, i)) {
continue;
}
dists[i] = FLT_MAX;
}
BLI_gsqueue_free(queue);
MEM_freeN(visited_vertices);
return dists;
}
static void sculpt_expand_update_max_falloff_factor(SculptSession *ss, ExpandCache *expand_cache)
/* Functions to update the max_falloff value in the ExpandCache. This funcions are called after
* initializing a new falloff to make sure that this value is always updated. */
/* Updates the max_falloff value for vertices in a ExpandCache based on the current values of the
* falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */
static void sculpt_expand_update_max_falloff_value(SculptSession *ss, ExpandCache *expand_cache)
{
const int totvert = SCULPT_vertex_count_get(ss);
expand_cache->max_falloff_factor = -FLT_MAX;
expand_cache->max_falloff = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
if (expand_cache->falloff_factor[i] == FLT_MAX) {
if (expand_cache->falloff[i] == FLT_MAX) {
continue;
}
expand_cache->max_falloff_factor = max_ff(expand_cache->max_falloff_factor,
expand_cache->falloff_factor[i]);
if (!sculpt_expand_is_vert_in_active_compoment(ss, expand_cache, i)) {
continue;
}
expand_cache->max_falloff = max_ff(expand_cache->max_falloff, expand_cache->falloff[i]);
}
}
/* Updates the max_falloff value for faces in a ExpandCache based on the current values of the
* falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */
static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss,
ExpandCache *expand_cache)
{
const int totface = ss->totfaces;
expand_cache->max_face_falloff_factor = -FLT_MAX;
expand_cache->max_face_falloff = -FLT_MAX;
for (int i = 0; i < totface; i++) {
if (expand_cache->face_falloff_factor[i] == FLT_MAX) {
if (expand_cache->face_falloff[i] == FLT_MAX) {
continue;
}
@ -596,11 +657,15 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss,
continue;
}
expand_cache->max_face_falloff_factor = max_ff(expand_cache->max_face_falloff_factor,
expand_cache->face_falloff_factor[i]);
expand_cache->max_face_falloff = max_ff(expand_cache->max_face_falloff,
expand_cache->face_falloff[i]);
}
}
/* Functions to get falloff values for faces from the values from the vertices. This is used for
* expanding Face Sets. Depending on the data type of the ScultpSession, this needs to get the per
* face falloff value from the connected vertices of each face or from the grids stored per loops
* for each face. */
static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss,
Mesh *mesh,
ExpandCache *expand_cache)
@ -614,10 +679,10 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss,
for (int l = 0; l < poly->totloop; l++) {
const int grid_loop_index = (poly->loopstart + l) * key->grid_area;
for (int g = 0; g < key->grid_area; g++) {
accum += expand_cache->falloff_factor[grid_loop_index + g];
accum += expand_cache->falloff[grid_loop_index + g];
}
}
expand_cache->face_falloff_factor[p] = accum / (poly->totloop * key->grid_area);
expand_cache->face_falloff[p] = accum / (poly->totloop * key->grid_area);
}
}
@ -628,21 +693,23 @@ static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expan
float accum = 0.0f;
for (int l = 0; l < poly->totloop; l++) {
MLoop *loop = &mesh->mloop[l + poly->loopstart];
accum += expand_cache->falloff_factor[loop->v];
accum += expand_cache->falloff[loop->v];
}
expand_cache->face_falloff_factor[p] = accum / poly->totloop;
expand_cache->face_falloff[p] = accum / poly->totloop;
}
}
/* Main function to update the faces falloff from a already calculated vertex falloff. */
static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *ss,
Mesh *mesh,
ExpandCache *expand_cache)
{
if (expand_cache->face_falloff_factor) {
MEM_freeN(expand_cache->face_falloff_factor);
BLI_assert(expand_cache->falloff != NULL);
if (!expand_cache->face_falloff) {
expand_cache->face_falloff = MEM_malloc_arrayN(
mesh->totpoly, sizeof(float), "face falloff factors");
}
expand_cache->face_falloff_factor = MEM_malloc_arrayN(
mesh->totpoly, sizeof(float), "face falloff factors");
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache);
@ -655,6 +722,10 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s
}
}
/* Utility functions for getting all vertices state during expand. */
/* Returns a bitmap indexed by vertex index which contains if the vertex was enabled or not for a
* give expand_cache state. */
static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCache *expand_cache)
{
const int totvert = SCULPT_vertex_count_get(ss);
@ -666,6 +737,9 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa
return enabled_vertices;
}
/* Returns a bitmap indexed by vertex index which contains if the vertex is in the boundary of the
* enabled vertices. This is defined as vertices that are enabled and at least have one connected
* vertex that is not enabled. */
static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
BLI_bitmap *enabled_vertices,
const bool use_mesh_boundary)
@ -696,6 +770,8 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
return boundary_vertices;
}
/* Recursions:
static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
ExpandCache *expand_cache,
BLI_bitmap *enabled_vertices)
@ -714,10 +790,10 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
}
MEM_freeN(boundary_vertices);
MEM_SAFE_FREE(expand_cache->falloff_factor);
MEM_SAFE_FREE(expand_cache->face_falloff_factor);
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
expand_cache->falloff_factor = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX);
expand_cache->falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX);
BLI_gset_free(initial_vertices, NULL);
}
@ -725,8 +801,8 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
ExpandCache *expand_cache,
BLI_bitmap *enabled_vertices)
{
MEM_SAFE_FREE(expand_cache->falloff_factor);
MEM_SAFE_FREE(expand_cache->face_falloff_factor);
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@ -749,7 +825,7 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
SCULPT_floodfill_execute(ss, &flood, expand_topology_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
expand_cache->falloff_factor = dists;
expand_cache->falloff = dists;
}
static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
@ -786,17 +862,17 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
SCULPT_vertex_has_unique_face_set(ss, i))) {
continue;
}
expand_cache->falloff_factor[i] *= -1.0f;
expand_cache->falloff[i] *= -1.0f;
}
float min_factor = FLT_MAX;
for (int i = 0; i < totvert; i++) {
min_factor = min_ff(expand_cache->falloff_factor[i], min_factor);
min_factor = min_ff(expand_cache->falloff[i], min_factor);
}
const float increase_factor = fabsf(min_factor);
for (int i = 0; i < totvert; i++) {
expand_cache->falloff_factor[i] += increase_factor;
expand_cache->falloff[i] += increase_factor;
}
}
else {
@ -804,7 +880,7 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
continue;
}
expand_cache->falloff_factor[i] = 0.0f;
expand_cache->falloff[i] = 0.0f;
}
}
}
@ -859,35 +935,34 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
const int vertex,
eSculptExpandFalloffType falloff_type)
{
MEM_SAFE_FREE(expand_cache->falloff_factor);
expand_cache->falloff_factor_type = falloff_type;
MEM_SAFE_FREE(expand_cache->falloff);
expand_cache->falloff_type = falloff_type;
SculptSession *ss = ob->sculpt;
const bool has_topology_info = BKE_pbvh_type(ss->pbvh) == PBVH_FACES;
switch (falloff_type) {
case SCULPT_EXPAND_FALLOFF_GEODESIC:
expand_cache->falloff_factor = has_topology_info ?
sculpt_expand_geodesic_falloff_create(sd, ob, vertex) :
sculpt_expand_spherical_falloff_create(sd, ob, vertex);
expand_cache->falloff = has_topology_info ?
sculpt_expand_geodesic_falloff_create(sd, ob, vertex) :
sculpt_expand_spherical_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_TOPOLOGY:
expand_cache->falloff_factor = sculpt_expand_topology_falloff_create(sd, ob, vertex);
expand_cache->falloff = sculpt_expand_topology_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS:
expand_cache->falloff_factor = has_topology_info ?
sculpt_expand_diagonals_falloff_create(sd, ob, vertex) :
sculpt_expand_topology_falloff_create(sd, ob, vertex);
expand_cache->falloff = has_topology_info ?
sculpt_expand_diagonals_falloff_create(sd, ob, vertex) :
sculpt_expand_topology_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_NORMALS:
expand_cache->falloff_factor = sculpt_expand_normal_falloff_create(sd, ob, vertex, 300.0f);
expand_cache->falloff = sculpt_expand_normal_falloff_create(sd, ob, vertex, 300.0f);
break;
case SCULPT_EXPAND_FALLOFF_SPHERICAL:
expand_cache->falloff_factor = sculpt_expand_spherical_falloff_create(sd, ob, vertex);
expand_cache->falloff = sculpt_expand_spherical_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY:
expand_cache->falloff_factor = sculpt_expand_boundary_topology_falloff_create(
sd, ob, vertex);
expand_cache->falloff = sculpt_expand_boundary_topology_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET:
sculpt_expand_initialize_from_face_set_boundary(
@ -899,7 +974,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
break;
}
sculpt_expand_update_max_falloff_factor(ss, expand_cache);
sculpt_expand_update_max_falloff_value(ss, expand_cache);
if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
sculpt_expand_mesh_face_falloff_from_vertex_falloff(ss, ob->data, expand_cache);
sculpt_expand_update_max_face_falloff_factor(ss, expand_cache);
@ -912,12 +987,12 @@ static void sculpt_expand_cache_data_free(ExpandCache *expand_cache)
BLI_gset_free(expand_cache->snap_enabled_face_sets, NULL);
}
MEM_SAFE_FREE(expand_cache->nodes);
MEM_SAFE_FREE(expand_cache->falloff_factor);
MEM_SAFE_FREE(expand_cache->face_falloff_factor);
MEM_SAFE_FREE(expand_cache->initial_mask);
MEM_SAFE_FREE(expand_cache->falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
MEM_SAFE_FREE(expand_cache->original_mask);
MEM_SAFE_FREE(expand_cache->origin_face_sets);
MEM_SAFE_FREE(expand_cache->initial_face_sets);
MEM_SAFE_FREE(expand_cache->initial_color);
MEM_SAFE_FREE(expand_cache->original_color);
MEM_SAFE_FREE(expand_cache);
}
@ -952,7 +1027,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
copy_v4_v4(vd.col, expand_cache->initial_color[vd.index]);
copy_v4_v4(vd.col, expand_cache->original_color[vd.index]);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_redraw(node);
@ -970,7 +1045,7 @@ static void sculpt_expand_restore_mask_data(SculptSession *ss, ExpandCache *expa
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE)
{
*vd.mask = expand_cache->initial_mask[vd.index];
*vd.mask = expand_cache->original_mask[vd.index];
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_redraw(node);
@ -1036,14 +1111,14 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
float new_mask;
if (enabled) {
new_mask = sculpt_expand_gradient_falloff_get(ss, expand_cache, vd.index);
new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
}
else {
new_mask = 0.0f;
}
if (expand_cache->preserve) {
new_mask = max_ff(new_mask, expand_cache->initial_mask[vd.index]);
new_mask = max_ff(new_mask, expand_cache->original_mask[vd.index]);
}
if (new_mask == initial_mask) {
@ -1104,7 +1179,7 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
float fade;
if (enabled) {
fade = sculpt_expand_gradient_falloff_get(ss, expand_cache, vd.index);
fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
}
else {
fade = 0.0f;
@ -1117,7 +1192,7 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
float final_fill_color[4];
mul_v4_v4fl(final_fill_color, expand_cache->fill_color, fade);
IMB_blend_color_float(final_color,
expand_cache->initial_color[vd.index],
expand_cache->original_color[vd.index],
final_fill_color,
expand_cache->blend_mode);
@ -1167,9 +1242,9 @@ static void sculpt_expand_initial_state_store(Object *ob, ExpandCache *expand_ca
const int totvert = SCULPT_vertex_count_get(ss);
const int totface = ss->totfaces;
expand_cache->initial_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask");
expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask");
for (int i = 0; i < totvert; i++) {
expand_cache->initial_mask[i] = SCULPT_vertex_mask_get(ss, i);
expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i);
}
expand_cache->initial_face_sets = MEM_malloc_arrayN(totvert, sizeof(int), "initial face set");
@ -1180,9 +1255,9 @@ static void sculpt_expand_initial_state_store(Object *ob, ExpandCache *expand_ca
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) {
expand_cache->initial_color = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors");
expand_cache->original_color = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors");
for (int i = 0; i < totvert; i++) {
copy_v4_v4(expand_cache->initial_color[i], SCULPT_vertex_color_get(ss, i));
copy_v4_v4(expand_cache->original_color[i], SCULPT_vertex_color_get(ss, i));
}
}
}
@ -1202,43 +1277,43 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
ExpandCache *expand_cache = ss->expand_cache;
/* Update the active factor in the cache. */
if (vertex == SCULPT_EXPAND_VERTEX_NONE) {
expand_cache->active_factor = expand_cache->max_falloff_factor;
expand_cache->all_enabled = true;
}
else {
expand_cache->active_factor = expand_cache->falloff_factor[vertex];
expand_cache->all_enabled = false;
}
if (vertex == SCULPT_EXPAND_VERTEX_NONE) {
expand_cache->active_falloff = expand_cache->max_falloff;
expand_cache->all_enabled = true;
}
else {
expand_cache->active_falloff = expand_cache->falloff[vertex];
expand_cache->all_enabled = false;
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
sculpt_expand_face_sets_restore(ss, expand_cache);
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
sculpt_expand_face_sets_restore(ss, expand_cache);
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = expand_cache->nodes,
};
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = expand_cache->nodes,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, expand_cache->totnode);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, expand_cache->totnode);
switch (expand_cache->target) {
case SCULPT_EXPAND_TARGET_MASK:
BLI_task_parallel_range(
0, expand_cache->totnode, &data, sculpt_expand_mask_update_task_cb, &settings);
break;
case SCULPT_EXPAND_TARGET_FACE_SETS:
sculpt_expand_face_sets_update(ss, expand_cache);
break;
case SCULPT_EXPAND_TARGET_COLORS:
BLI_task_parallel_range(
0, expand_cache->totnode, &data, sculpt_expand_colors_update_task_cb, &settings);
break;
}
switch (expand_cache->target) {
case SCULPT_EXPAND_TARGET_MASK:
BLI_task_parallel_range(
0, expand_cache->totnode, &data, sculpt_expand_mask_update_task_cb, &settings);
break;
case SCULPT_EXPAND_TARGET_FACE_SETS:
sculpt_expand_face_sets_update(ss, expand_cache);
break;
case SCULPT_EXPAND_TARGET_COLORS:
BLI_task_parallel_range(
0, expand_cache->totnode, &data, sculpt_expand_colors_update_task_cb, &settings);
break;
}
sculpt_expand_flush_updates(C);
sculpt_expand_flush_updates(C);
}
static int sculpt_expand_target_vertex_update_and_get(bContext *C,
@ -1265,7 +1340,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
expand_cache->invert = false;
BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
const float use_mesh_boundary = expand_cache->falloff_factor_type !=
const float use_mesh_boundary = expand_cache->falloff_type !=
SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY;
BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(
@ -1350,7 +1425,7 @@ static void sculpt_expand_resursion_step_add(Object *ob,
break;
}
sculpt_expand_update_max_falloff_factor(ss, expand_cache);
sculpt_expand_update_max_falloff_value(ss, expand_cache);
if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
sculpt_expand_mesh_face_falloff_from_vertex_falloff(ss, ob->data, expand_cache);
sculpt_expand_update_max_face_falloff_factor(ss, expand_cache);
@ -1428,11 +1503,8 @@ static void sculpt_expand_move_propagation_origin(bContext *C,
add_v2_v2v2(new_mouse, move_disp, expand_cache->original_mouse_move);
sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mouse);
sculpt_expand_falloff_factors_from_vertex_and_symm_create(expand_cache,
sd,
ob,
expand_cache->initial_active_vertex,
expand_cache->falloff_factor_type);
sculpt_expand_falloff_factors_from_vertex_and_symm_create(
expand_cache, sd, ob, expand_cache->initial_active_vertex, expand_cache->falloff_type);
}
static void sculpt_expand_ensure_sculptsession_data(Object *ob)

View File

@ -1154,6 +1154,7 @@ typedef struct SculptGradientContext {
void (*sculpt_gradient_end)(struct bContext *);
} SculptGradientContext;
/* Sculpt Expand. */
typedef enum eSculptExpandFalloffType {
SCULPT_EXPAND_FALLOFF_GEODESIC,
SCULPT_EXPAND_FALLOFF_TOPOLOGY,
@ -1180,16 +1181,21 @@ typedef enum eSculptExpandRecursionType {
#define EXPAND_ACTIVE_COMPOMENT_NONE -1
typedef struct ExpandCache {
/* Target data elements that the expand operation will affect. */
eSculptExpandTargetType target;
eSculptExpandFalloffType falloff_factor_type;
float *falloff_factor;
float max_falloff_factor;
/* Falloff data. */
eSculptExpandFalloffType falloff_type;
float *falloff;
float max_falloff;
float *face_falloff_factor;
float max_face_falloff_factor;
float *face_falloff;
float max_face_falloff;
float active_factor;
float active_falloff;
/* When set to true, expand skips all falloff computations and considers all elements as enabled.
*/
bool all_enabled;
float initial_mouse[2];
@ -1197,17 +1203,23 @@ typedef struct ExpandCache {
int initial_active_face_set;
int next_face_set;
/* Active components checks. */
int active_connected_components[EXPAND_SYMM_AREAS];
/* Snapping. */
GSet *snap_enabled_face_sets;
Brush *brush;
struct Scene *scene;
struct MTex *mtex;
float texture_distortion_strength;
/* Cached PBVH nodes. This allows to skip gathering all nodes from the PBVH each time expand
* needs to update the state of the elements. */
PBVHNode **nodes;
int totnode;
/* Expand state options. */
int loop_count;
bool invert;
bool preserve;
@ -1218,19 +1230,19 @@ typedef struct ExpandCache {
bool reposition_pivot;
bool brush_gradient;
Brush *brush;
float initial_mouse_move[2];
float original_mouse_move[2];
int update_face_set;
/* Color target data type related data. */
float fill_color[4];
short blend_mode;
float *initial_mask;
int *initial_face_sets;
float *original_mask;
int *origin_face_sets;
float (*initial_color)[4];
float (*original_color)[4];
} ExpandCache;
typedef struct FilterCache {