Multires: Fix wrong or missing mask propagation

Propagation when changing sculpt level was missing. In fact, the mask
was simply completely removed when changing sculpt level.

Subdivision worked for simple and linear subdivision, but Catmull-Clark
was giving empty results.

Fixes propagation part of T76386.
This commit is contained in:
Sergey Sharybin 2020-05-13 10:42:13 +02:00
parent 0790d90d02
commit 23fd95458c
4 changed files with 246 additions and 51 deletions

View File

@ -216,19 +216,20 @@ void multiresModifier_subdivide_to_level(struct Object *object,
multires_reshape_store_original_grids(&reshape_context);
multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level);
multires_reshape_assign_final_coords_from_orig_mdisps(&reshape_context);
multires_reshape_assign_final_elements_from_orig_mdisps(&reshape_context);
/* Free original grids which makes it so smoothing with details thinks all the details were
* added against base mesh's limit surface. This is similar behavior to as if we've done all
* displacement in sculpt mode at the old top level and then propagated to the new top level.*/
multires_reshape_free_original_grids(&reshape_context);
if (ELEM(mode, MULTIRES_SUBDIVIDE_LINEAR, MULTIRES_SUBDIVIDE_SIMPLE)) {
multires_reshape_smooth_object_grids(&reshape_context, mode);
}
else {
/* Free original grids which makes it so smoothing with details thinks all the details were
* added against base mesh's limit surface. This is similar behavior to as if we've done all
* displacement in sculpt mode at the old top level and then propagated to the new top level.*/
multires_reshape_free_original_grids(&reshape_context);
multires_reshape_smooth_object_grids_with_details(&reshape_context);
}
multires_reshape_object_grids_to_tangent_displacement(&reshape_context);
multires_reshape_context_free(&reshape_context);

View File

@ -275,7 +275,7 @@ void multires_reshape_assign_final_coords_from_mdisps(
const MultiresReshapeContext *reshape_context);
/* Reads from original CD_MIDTSPS, writes to the current mesh CD_MDISPS. */
void multires_reshape_assign_final_coords_from_orig_mdisps(
void multires_reshape_assign_final_elements_from_orig_mdisps(
const MultiresReshapeContext *reshape_context);
/* --------------------------------------------------------------------

View File

@ -95,6 +95,30 @@ typedef struct Edge {
float sharpness;
} Edge;
/* Storage of data which is linearly interpolated from the reshape level to the top level. */
typedef struct LinearGridElement {
float mask;
} LinearGridElement;
typedef struct LinearGrid {
LinearGridElement *elements;
} LinearGrid;
typedef struct LinearGrids {
int num_grids;
int level;
/* Cached size for the grid, for faster lookup. */
int grid_size;
/* Indexed by grid index. */
LinearGrid *grids;
/* Elements for all grids are allocated in a single array, for the allocation performance. */
LinearGridElement *elements_storage;
} LinearGrids;
/* Context which holds all information eeded during propagation and smoothing. */
typedef struct MultiresReshapeSmoothContext {
@ -122,6 +146,10 @@ typedef struct MultiresReshapeSmoothContext {
Face *faces;
} geometry;
/* Grids of data which is linearly interpolated between grid elements at the reshape level.
* The data is actually stored as a delta, which is then to be added to the higher levels. */
LinearGrids linear_delta_grids;
/* Index i of this map indicates that base edge i is adjacent to at least one face. */
BLI_bitmap *non_loose_base_edge_map;
@ -150,51 +178,79 @@ typedef struct MultiresReshapeSmoothContext {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Masks
/** \name Linear grids manipulation
* \{ */
/* Interpolate mask grid at a reshape level.
* Will return 0 if there is no masks custom data layer. */
static float interpolate_masks_grid(const MultiresReshapeSmoothContext *reshape_smooth_context,
const GridCoord *grid_coord)
static void linear_grids_init(LinearGrids *linear_grids)
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
if (reshape_context->orig.grid_paint_masks == NULL) {
return 0.0f;
linear_grids->num_grids = 0;
linear_grids->level = 0;
linear_grids->grids = NULL;
linear_grids->elements_storage = NULL;
}
static void linear_grids_allocate(LinearGrids *linear_grids, int num_grids, int level)
{
const size_t grid_size = BKE_subdiv_grid_size_from_level(level);
const size_t grid_area = grid_size * grid_size;
const size_t num_grid_elements = num_grids * grid_area;
linear_grids->num_grids = num_grids;
linear_grids->level = level;
linear_grids->grid_size = grid_size;
linear_grids->grids = MEM_malloc_arrayN(num_grids, sizeof(LinearGrid), "linear grids");
linear_grids->elements_storage = MEM_calloc_arrayN(
num_grid_elements, sizeof(LinearGridElement), "linear elements storage");
for (int i = 0; i < num_grids; ++i) {
const size_t element_offset = grid_area * i;
linear_grids->grids[i].elements = &linear_grids->elements_storage[element_offset];
}
}
const GridPaintMask *grid = &reshape_context->orig.grid_paint_masks[grid_coord->grid_index];
const int grid_size = BKE_subdiv_grid_size_from_level(grid->level);
const int grid_size_1 = grid_size - 1;
const float grid_size_1_inv = 1.0f / (float)(grid_size_1);
static LinearGridElement *linear_grid_element_get(const LinearGrids *linear_grids,
const GridCoord *grid_coord)
{
BLI_assert(grid_coord->grid_index >= 0);
BLI_assert(grid_coord->grid_index < linear_grids->num_grids);
const float x_f = grid_coord->u * grid_size_1;
const float y_f = grid_coord->v * grid_size_1;
const int grid_size = linear_grids->grid_size;
const int x_i = x_f;
const int y_i = y_f;
const int x_n_i = (x_i == grid_size - 1) ? (x_i) : (x_i + 1);
const int y_n_i = (y_i == grid_size - 1) ? (y_i) : (y_i + 1);
const int grid_x = lround(grid_coord->u * (grid_size - 1));
const int grid_y = lround(grid_coord->v * (grid_size - 1));
const int grid_element_index = grid_y * grid_size + grid_x;
const int corners[4][2] = {{x_i, y_i}, {x_n_i, y_i}, {x_n_i, y_n_i}, {x_i, y_n_i}};
float mask_elements[4];
for (int i = 0; i < 4; ++i) {
GridCoord corner_grid_coord;
corner_grid_coord.grid_index = grid_coord->grid_index;
corner_grid_coord.u = corners[i][0] * grid_size_1_inv;
corner_grid_coord.v = corners[i][1] * grid_size_1_inv;
LinearGrid *grid = &linear_grids->grids[grid_coord->grid_index];
return &grid->elements[grid_element_index];
}
ReshapeConstGridElement element = multires_reshape_orig_grid_element_for_grid_coord(
reshape_context, &corner_grid_coord);
mask_elements[i] = element.mask;
}
static void linear_grids_free(LinearGrids *linear_grids)
{
MEM_SAFE_FREE(linear_grids->grids);
MEM_SAFE_FREE(linear_grids->elements_storage);
}
const float u = x_f - x_i;
const float v = y_f - y_i;
const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), u * v, (1.0f - u) * v};
static void linear_grid_element_init(LinearGridElement *linear_grid_element)
{
linear_grid_element->mask = 0.0f;
}
return mask_elements[0] * weights[0] + mask_elements[1] * weights[1] +
mask_elements[2] * weights[2] + mask_elements[3] * weights[3];
/* result = a - b. */
static void linear_grid_element_sub(LinearGridElement *result,
const LinearGridElement *a,
const LinearGridElement *b)
{
result->mask = a->mask - b->mask;
}
static void linear_grid_element_interpolate(LinearGridElement *result,
const LinearGridElement elements[4],
const float weights[4])
{
result->mask = elements[0].mask * weights[0] + elements[1].mask * weights[1] +
elements[2].mask * weights[2] + elements[3].mask * weights[3];
}
/** \} */
@ -465,6 +521,8 @@ static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
reshape_smooth_context->geometry.num_faces = 0;
reshape_smooth_context->geometry.faces = NULL;
linear_grids_init(&reshape_smooth_context->linear_delta_grids);
reshape_smooth_context->non_loose_base_edge_map = NULL;
reshape_smooth_context->reshape_subdiv = NULL;
reshape_smooth_context->base_surface_grids = NULL;
@ -483,6 +541,8 @@ static void context_free_geometry(MultiresReshapeSmoothContext *reshape_smooth_c
MEM_SAFE_FREE(reshape_smooth_context->geometry.corners);
MEM_SAFE_FREE(reshape_smooth_context->geometry.faces);
MEM_SAFE_FREE(reshape_smooth_context->geometry.edges);
linear_grids_free(&reshape_smooth_context->linear_delta_grids);
}
static void context_free_subdiv(MultiresReshapeSmoothContext *reshape_smooth_context)
@ -1100,6 +1160,133 @@ static void reshape_subdiv_evaluate_limit_at_grid(
/** \} */
/* -------------------------------------------------------------------- */
/** \name Linearly interpolated data
* \{ */
static LinearGridElement linear_grid_element_orig_get(
const MultiresReshapeSmoothContext *reshape_smooth_context, const GridCoord *grid_coord)
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const ReshapeConstGridElement orig_grid_element =
multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
LinearGridElement linear_grid_element;
linear_grid_element_init(&linear_grid_element);
linear_grid_element.mask = orig_grid_element.mask;
return linear_grid_element;
}
static LinearGridElement linear_grid_element_final_get(
const MultiresReshapeSmoothContext *reshape_smooth_context, const GridCoord *grid_coord)
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const ReshapeGridElement final_grid_element = multires_reshape_grid_element_for_grid_coord(
reshape_context, grid_coord);
LinearGridElement linear_grid_element;
linear_grid_element_init(&linear_grid_element);
if (final_grid_element.mask != NULL) {
linear_grid_element.mask = *final_grid_element.mask;
}
return linear_grid_element;
}
/* Interpolate difference of the linear data.
*
* Will access final data and original data at the grid elements at the reshape level,
* calculate difference between final and original, and linearly interpolate to get value at the
* top level. */
static void linear_grid_element_delta_interpolate(
const MultiresReshapeSmoothContext *reshape_smooth_context,
const GridCoord *grid_coord,
LinearGridElement *result)
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const int reshape_level = reshape_context->reshape.level;
const int reshape_level_grid_size = BKE_subdiv_grid_size_from_level(reshape_level);
const int reshape_level_grid_size_1 = reshape_level_grid_size - 1;
const float reshape_level_grid_size_1_inv = 1.0f / (float)(reshape_level_grid_size_1);
const float x_f = grid_coord->u * reshape_level_grid_size_1;
const float y_f = grid_coord->v * reshape_level_grid_size_1;
const int x_i = x_f;
const int y_i = y_f;
const int x_n_i = (x_i == reshape_level_grid_size - 1) ? (x_i) : (x_i + 1);
const int y_n_i = (y_i == reshape_level_grid_size - 1) ? (y_i) : (y_i + 1);
const int corners_int_coords[4][2] = {{x_i, y_i}, {x_n_i, y_i}, {x_n_i, y_n_i}, {x_i, y_n_i}};
LinearGridElement corner_elements[4];
for (int i = 0; i < 4; ++i) {
GridCoord corner_grid_coord;
corner_grid_coord.grid_index = grid_coord->grid_index;
corner_grid_coord.u = corners_int_coords[i][0] * reshape_level_grid_size_1_inv;
corner_grid_coord.v = corners_int_coords[i][1] * reshape_level_grid_size_1_inv;
const LinearGridElement orig_element = linear_grid_element_orig_get(reshape_smooth_context,
&corner_grid_coord);
const LinearGridElement final_element = linear_grid_element_final_get(reshape_smooth_context,
&corner_grid_coord);
linear_grid_element_sub(&corner_elements[i], &final_element, &orig_element);
}
const float u = x_f - x_i;
const float v = y_f - y_i;
const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), u * v, (1.0f - u) * v};
linear_grid_element_interpolate(result, corner_elements, weights);
}
static void evaluate_linear_delta_grids_callback(
const MultiresReshapeSmoothContext *reshape_smooth_context,
const PTexCoord *UNUSED(ptex_coord),
const GridCoord *grid_coord,
void *UNUSED(userdata_v))
{
LinearGridElement *linear_delta_element = linear_grid_element_get(
&reshape_smooth_context->linear_delta_grids, grid_coord);
linear_grid_element_delta_interpolate(reshape_smooth_context, grid_coord, linear_delta_element);
}
static void evaluate_linear_delta_grids(MultiresReshapeSmoothContext *reshape_smooth_context)
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const int num_grids = reshape_context->num_grids;
const int top_level = reshape_context->top.level;
linear_grids_allocate(&reshape_smooth_context->linear_delta_grids, num_grids, top_level);
foreach_toplevel_grid_coord(reshape_smooth_context, evaluate_linear_delta_grids_callback, NULL);
}
static void propagate_linear_data_delta(const MultiresReshapeSmoothContext *reshape_smooth_context,
ReshapeGridElement *final_grid_element,
const GridCoord *grid_coord)
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
LinearGridElement *linear_delta_element = linear_grid_element_get(
&reshape_smooth_context->linear_delta_grids, grid_coord);
const ReshapeConstGridElement orig_grid_element =
multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
if (final_grid_element->mask != NULL) {
*final_grid_element->mask = clamp_f(
orig_grid_element.mask + linear_delta_element->mask, 0.0f, 1.0f);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Evaluation of base surface
* \{ */
@ -1201,7 +1388,11 @@ static void evaluate_higher_grid_positions_with_details_callback(
grid_coord);
add_v3_v3v3(grid_element.displacement, smooth_limit_P, smooth_delta);
/* Propagate non-coordinate data. */
propagate_linear_data_delta(reshape_smooth_context, &grid_element, grid_coord);
}
static void evaluate_higher_grid_positions_with_details(
const MultiresReshapeSmoothContext *reshape_smooth_context)
{
@ -1222,17 +1413,14 @@ static void evaluate_higher_grid_positions_callback(
grid_coord);
/* Surface. */
float P[3];
BKE_subdiv_eval_limit_point(
reshape_subdiv, ptex_coord->ptex_face_index, ptex_coord->u, ptex_coord->v, P);
copy_v3_v3(grid_element.displacement, P);
/* Masks. */
if (grid_element.mask != NULL) {
*grid_element.mask = interpolate_masks_grid(reshape_smooth_context, grid_coord);
}
/* Propagate non-coordinate data. */
propagate_linear_data_delta(reshape_smooth_context, &grid_element, grid_coord);
}
static void evaluate_higher_grid_positions(
@ -1266,6 +1454,7 @@ void multires_reshape_smooth_object_grids_with_details(
}
geometry_create(&reshape_smooth_context);
evaluate_linear_delta_grids(&reshape_smooth_context);
reshape_subdiv_create(&reshape_smooth_context);
@ -1292,6 +1481,7 @@ void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_
context_init(&reshape_smooth_context, reshape_context, mode);
geometry_create(&reshape_smooth_context);
evaluate_linear_delta_grids(&reshape_smooth_context);
reshape_subdiv_create(&reshape_smooth_context);

View File

@ -751,9 +751,9 @@ void multires_reshape_assign_final_coords_from_mdisps(
reshape_context, reshape_context->top.level, assign_final_coords_from_mdisps, NULL);
}
static void assign_final_coords_from_orig_mdisps(const MultiresReshapeContext *reshape_context,
const GridCoord *grid_coord,
void *UNUSED(userdata_v))
static void assign_final_elements_from_orig_mdisps(const MultiresReshapeContext *reshape_context,
const GridCoord *grid_coord,
void *UNUSED(userdata_v))
{
float P[3];
float tangent_matrix[3][3];
@ -768,13 +768,17 @@ static void assign_final_coords_from_orig_mdisps(const MultiresReshapeContext *r
ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
grid_coord);
add_v3_v3v3(grid_element.displacement, P, D);
if (grid_element.mask != NULL) {
*grid_element.mask = orig_grid_element.mask;
}
}
void multires_reshape_assign_final_coords_from_orig_mdisps(
void multires_reshape_assign_final_elements_from_orig_mdisps(
const MultiresReshapeContext *reshape_context)
{
foreach_grid_coordinate(
reshape_context, reshape_context->top.level, assign_final_coords_from_orig_mdisps, NULL);
reshape_context, reshape_context->top.level, assign_final_elements_from_orig_mdisps, NULL);
}
/** \} */