diff options
4 files changed, 246 insertions, 51 deletions
diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index c5184881b7c..e59b19638f8 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -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); diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h index 8eb90809c31..12816a455ee 100644 --- a/source/blender/blenkernel/intern/multires_reshape.h +++ b/source/blender/blenkernel/intern/multires_reshape.h @@ -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); /* -------------------------------------------------------------------- diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index 0fe0ac4b66a..7312ac2bf5e 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -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; - 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); + linear_grids->grids = NULL; + linear_grids->elements_storage = NULL; +} - const float x_f = grid_coord->u * grid_size_1; - const float y_f = grid_coord->v * grid_size_1; +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; - 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); + linear_grids->num_grids = num_grids; + linear_grids->level = level; + linear_grids->grid_size = grid_size; - 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; + 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"); - ReshapeConstGridElement element = multires_reshape_orig_grid_element_for_grid_coord( - reshape_context, &corner_grid_coord); - mask_elements[i] = element.mask; + 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 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 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 int grid_size = linear_grids->grid_size; - return mask_elements[0] * weights[0] + mask_elements[1] * weights[1] + - mask_elements[2] * weights[2] + mask_elements[3] * weights[3]; + 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; + + LinearGrid *grid = &linear_grids->grids[grid_coord->grid_index]; + return &grid->elements[grid_element_index]; +} + +static void linear_grids_free(LinearGrids *linear_grids) +{ + MEM_SAFE_FREE(linear_grids->grids); + MEM_SAFE_FREE(linear_grids->elements_storage); +} + +static void linear_grid_element_init(LinearGridElement *linear_grid_element) +{ + linear_grid_element->mask = 0.0f; +} + +/* 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) @@ -1101,6 +1161,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); diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index fcee98da1c5..e9a779dafeb 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -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); } /** \} */ |