Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/multires_reshape_smooth.c')
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c1064
1 files changed, 1064 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
new file mode 100644
index 00000000000..0a9a17d7bd8
--- /dev/null
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -0,0 +1,1064 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "multires_reshape.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math_vector.h"
+#include "BLI_task.h"
+
+#include "BKE_multires.h"
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_eval.h"
+#include "BKE_subdiv_foreach.h"
+#include "BKE_subdiv_mesh.h"
+
+#include "opensubdiv_converter_capi.h"
+#include "opensubdiv_evaluator_capi.h"
+#include "opensubdiv_topology_refiner_capi.h"
+
+#include "subdiv_converter.h"
+
+typedef struct SurfacePoint {
+ float P[3];
+ float tangent_matrix[3][3];
+} SurfacePoint;
+
+typedef struct SurfaceGrid {
+ SurfacePoint *points;
+} SurfaceGrid;
+
+typedef struct Vertex {
+ /* All grid coordinates which the vertex corresponding to.
+ * For a vertices which are created from inner points of grids there is always one coordinate. */
+ int num_grid_coords;
+ GridCoord *grid_coords;
+
+ bool is_infinite_sharp;
+} Vertex;
+
+typedef struct Corner {
+ const Vertex *vertex;
+ int grid_index;
+} Corner;
+
+typedef struct Dace {
+ int start_corner_index;
+ int num_corners;
+} Dace;
+
+typedef struct MultiresReshapeSmoothContext {
+ const MultiresReshapeContext *reshape_context;
+
+ // Geometry at a reshape multires level.
+ struct {
+ int num_vertices;
+ Vertex *vertices;
+
+ int num_corners;
+ Corner *corners;
+
+ int num_faces;
+ Dace *faces;
+ } geometry;
+
+ /* Subdivision surface created for geometry at a reshape level. */
+ Subdiv *reshape_subdiv;
+
+ SurfaceGrid *base_surface_grids;
+} MultiresReshapeSmoothContext;
+
+/* ================================================================================================
+ * Masks.
+ */
+
+/* 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)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ if (reshape_context->grid_paint_masks == NULL) {
+ return 0.0f;
+ }
+
+ 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);
+
+ const float x_f = grid_coord->u * grid_size_1;
+ const float y_f = grid_coord->v * grid_size_1;
+
+ 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 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;
+
+ ReshapeConstGridElement element = multires_reshape_orig_grid_element_for_grid_coord(
+ reshape_context, &corner_grid_coord);
+ mask_elements[i] = element.mask;
+ }
+
+ 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), (1.0f - u) * v, u * v};
+
+ return mask_elements[0] * weights[0] + mask_elements[1] * weights[1] +
+ mask_elements[2] * weights[2] + mask_elements[3] * weights[3];
+}
+
+/* ================================================================================================
+ * Surface.
+ */
+
+static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const int num_grids = reshape_context->num_grids;
+ const int grid_size = reshape_context->top.grid_size;
+ const int grid_area = grid_size * grid_size;
+
+ SurfaceGrid *surface_grid = MEM_malloc_arrayN(num_grids, sizeof(SurfaceGrid), "delta grids");
+
+ for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
+ surface_grid[grid_index].points = MEM_calloc_arrayN(
+ sizeof(SurfacePoint), grid_area, "delta grid dispalcement");
+ }
+
+ reshape_smooth_context->base_surface_grids = surface_grid;
+}
+
+static void base_surface_grids_free(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ if (reshape_smooth_context->base_surface_grids == NULL) {
+ return;
+ }
+
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const int num_grids = reshape_context->num_grids;
+ for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
+ MEM_freeN(reshape_smooth_context->base_surface_grids[grid_index].points);
+ }
+ MEM_freeN(reshape_smooth_context->base_surface_grids);
+}
+
+static SurfacePoint *base_surface_grids_read(
+ const MultiresReshapeSmoothContext *reshape_smooth_context, const GridCoord *grid_coord)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const int grid_index = grid_coord->grid_index;
+ const int grid_size = reshape_context->top.grid_size;
+ 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;
+
+ SurfaceGrid *surface_grid = &reshape_smooth_context->base_surface_grids[grid_index];
+ return &surface_grid->points[grid_element_index];
+}
+
+static void base_surface_grids_write(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const GridCoord *grid_coord,
+ float P[3],
+ float tangent_matrix[3][3])
+{
+ SurfacePoint *point = base_surface_grids_read(reshape_smooth_context, grid_coord);
+ copy_v3_v3(point->P, P);
+ copy_m3_m3(point->tangent_matrix, tangent_matrix);
+}
+
+/* ================================================================================================
+ * Evaluation of subdivision surface at a reshape level.
+ */
+
+typedef void (*ForeachTopLevelGridCoordCallback)(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *userdata_v);
+
+typedef struct ForeachTopLevelGridCoordTaskData {
+ const MultiresReshapeSmoothContext *reshape_smooth_context;
+
+ int inner_grid_size;
+ float inner_grid_size_1_inv;
+
+ ForeachTopLevelGridCoordCallback callback;
+ void *callback_userdata_v;
+} ForeachHighLevelCoordTaskData;
+
+/* Find grid index which given face was created for. */
+static int get_face_grid_index(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Dace *face)
+{
+ const Corner *first_corner = &reshape_smooth_context->geometry.corners[face->start_corner_index];
+ const int grid_index = first_corner->grid_index;
+
+#ifndef NDEBUG
+ for (int face_corner = 0; face_corner < face->num_corners; ++face_corner) {
+ const int corner_index = face->start_corner_index + face_corner;
+ const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index];
+ BLI_assert(corner->grid_index == grid_index);
+ }
+#endif
+
+ return grid_index;
+}
+
+static GridCoord *vertex_grid_coord_with_grid_index(const Vertex *vertex, const int grid_index)
+{
+ for (int i = 0; i < vertex->num_grid_coords; ++i) {
+ if (vertex->grid_coords[i].grid_index == grid_index) {
+ return &vertex->grid_coords[i];
+ }
+ }
+ return NULL;
+}
+
+/* Get grid coordinates which correspond to corners of the given face.
+ * All the grid coordinates will be from the same grid index. */
+static void grid_coords_from_face_vertices(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Dace *face,
+ const GridCoord *grid_coords[])
+{
+ BLI_assert(face->num_corners == 4);
+
+ const int grid_index = get_face_grid_index(reshape_smooth_context, face);
+ BLI_assert(grid_index != -1);
+
+ for (int i = 0; i < face->num_corners; ++i) {
+ const int corner_index = face->start_corner_index + i;
+ const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index];
+ grid_coords[i] = vertex_grid_coord_with_grid_index(corner->vertex, grid_index);
+ BLI_assert(grid_coords[i] != NULL);
+ }
+}
+
+static float lerp(float t, float a, float b)
+{
+ return (a + t * (b - a));
+}
+
+static void interpolate_grid_coord(GridCoord *result,
+ const GridCoord *face_grid_coords[4],
+ const float u,
+ const float v)
+{
+ /*
+ * v
+ * ^
+ * | (3) -------- (2)
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * | (0) -------- (1)
+ * *--------------------------> u
+ */
+
+ const float u01 = lerp(u, face_grid_coords[0]->u, face_grid_coords[1]->u);
+ const float u32 = lerp(u, face_grid_coords[3]->u, face_grid_coords[2]->u);
+
+ const float v03 = lerp(v, face_grid_coords[0]->v, face_grid_coords[3]->v);
+ const float v12 = lerp(v, face_grid_coords[1]->v, face_grid_coords[2]->v);
+
+ result->grid_index = face_grid_coords[0]->grid_index;
+ result->u = lerp(v, u01, u32);
+ result->v = lerp(u, v03, v12);
+}
+
+static void foreach_toplevel_grid_coord_task(void *__restrict userdata_v,
+ const int face_index,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ ForeachHighLevelCoordTaskData *data = userdata_v;
+
+ const MultiresReshapeSmoothContext *reshape_smooth_context = data->reshape_smooth_context;
+ const int inner_grid_size = data->inner_grid_size;
+ const float inner_grid_size_1_inv = data->inner_grid_size_1_inv;
+
+ const Dace *face = &reshape_smooth_context->geometry.faces[face_index];
+ const GridCoord *face_grid_coords[4];
+ grid_coords_from_face_vertices(reshape_smooth_context, face, face_grid_coords);
+
+ for (int y = 0; y < inner_grid_size; ++y) {
+ const float ptex_v = (float)y * inner_grid_size_1_inv;
+ for (int x = 0; x < inner_grid_size; ++x) {
+ const float ptex_u = (float)x * inner_grid_size_1_inv;
+
+ PTexCoord ptex_coord;
+ ptex_coord.ptex_face_index = face_index;
+ ptex_coord.u = ptex_u;
+ ptex_coord.v = ptex_v;
+
+ GridCoord grid_coord;
+ interpolate_grid_coord(&grid_coord, face_grid_coords, ptex_u, ptex_v);
+
+ data->callback(reshape_smooth_context, &ptex_coord, &grid_coord, data->callback_userdata_v);
+ }
+ }
+}
+
+static void foreach_toplevel_grid_coord(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ ForeachTopLevelGridCoordCallback callback,
+ void *callback_userdata_v)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
+
+ ForeachHighLevelCoordTaskData data;
+ data.reshape_smooth_context = reshape_smooth_context;
+ data.inner_grid_size = (1 << level_difference) + 1;
+ data.inner_grid_size_1_inv = 1.0f / (float)(data.inner_grid_size - 1);
+ data.callback = callback;
+ data.callback_userdata_v = callback_userdata_v;
+
+ TaskParallelSettings parallel_range_settings;
+ BLI_parallel_range_settings_defaults(&parallel_range_settings);
+ parallel_range_settings.min_iter_per_thread = 1;
+
+ const int num_faces = reshape_smooth_context->geometry.num_faces;
+ BLI_task_parallel_range(
+ 0, num_faces, &data, foreach_toplevel_grid_coord_task, &parallel_range_settings);
+}
+
+/* ================================================================================================
+ * Generation of a topology information for OpenSubdiv converter.
+ *
+ * Calculates vertices, their coordinates in the original grids, and connections of them so then
+ * it's easy to create OpenSubdiv's topology refiner. */
+
+static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
+ const MultiresReshapeContext *reshape_context)
+{
+ reshape_smooth_context->reshape_context = reshape_context;
+
+ reshape_smooth_context->geometry.num_vertices = 0;
+ reshape_smooth_context->geometry.vertices = NULL;
+ reshape_smooth_context->geometry.num_corners = 0;
+ reshape_smooth_context->geometry.corners = NULL;
+
+ reshape_smooth_context->reshape_subdiv = NULL;
+ reshape_smooth_context->base_surface_grids = NULL;
+}
+
+static void context_free_geometry(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ if (reshape_smooth_context->geometry.vertices != NULL) {
+ for (int i = 0; i < reshape_smooth_context->geometry.num_vertices; ++i) {
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.vertices[i].grid_coords);
+ }
+ }
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.vertices);
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.corners);
+ MEM_SAFE_FREE(reshape_smooth_context->geometry.faces);
+}
+
+static void context_free_subdiv(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ if (reshape_smooth_context->reshape_subdiv == NULL) {
+ return;
+ }
+ BKE_subdiv_free(reshape_smooth_context->reshape_subdiv);
+}
+
+static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ context_free_geometry(reshape_smooth_context);
+ context_free_subdiv(reshape_smooth_context);
+ base_surface_grids_free(reshape_smooth_context);
+}
+
+static bool foreach_topology_info(const SubdivForeachContext *foreach_context,
+ const int num_vertices,
+ const int UNUSED(num_edges),
+ const int num_loops,
+ const int num_polygons)
+{
+ MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+
+ /* NOTE: Calloc so the counters are re-set to 0 "for free". */
+ reshape_smooth_context->geometry.num_vertices = num_vertices;
+ reshape_smooth_context->geometry.vertices = MEM_calloc_arrayN(
+ sizeof(Vertex), num_vertices, "smooth vertices");
+
+ reshape_smooth_context->geometry.num_corners = num_loops;
+ reshape_smooth_context->geometry.corners = MEM_malloc_arrayN(
+ sizeof(Corner), num_loops, "smooth corners");
+
+ reshape_smooth_context->geometry.num_faces = num_polygons;
+ reshape_smooth_context->geometry.faces = MEM_malloc_arrayN(
+ sizeof(Dace), num_polygons, "smooth faces");
+
+ return true;
+}
+
+static void foreach_single_vertex(const SubdivForeachContext *foreach_context,
+ const GridCoord *grid_coord,
+ const int subdiv_vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+
+ BLI_assert(subdiv_vertex_index < reshape_smooth_context->geometry.num_vertices);
+
+ Vertex *vertex = &reshape_smooth_context->geometry.vertices[subdiv_vertex_index];
+
+ vertex->grid_coords = MEM_reallocN(vertex->grid_coords,
+ sizeof(Vertex) * (vertex->num_grid_coords + 1));
+ vertex->grid_coords[vertex->num_grid_coords] = *grid_coord;
+ ++vertex->num_grid_coords;
+}
+
+/* TODO(sergey): De-duplicate with similar function in multires_reshape_vertcos.c */
+static void foreach_vertex(const SubdivForeachContext *foreach_context,
+ const PTexCoord *ptex_coord,
+ const int subdiv_vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ const GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord);
+ const int face_index = multires_reshape_grid_to_face_index(reshape_context,
+ grid_coord.grid_index);
+
+ const Mesh *base_mesh = reshape_context->base_mesh;
+ const MPoly *base_poly = &base_mesh->mpoly[face_index];
+ const int num_corners = base_poly->totloop;
+ const int start_grid_index = reshape_context->face_start_grid_index[face_index];
+ const int corner = grid_coord.grid_index - start_grid_index;
+
+ if (grid_coord.u == 0.0f && grid_coord.v == 0.0f) {
+ for (int current_corner = 0; current_corner < num_corners; ++current_corner) {
+ GridCoord corner_grid_coord = grid_coord;
+ corner_grid_coord.grid_index = start_grid_index + current_corner;
+ foreach_single_vertex(foreach_context, &corner_grid_coord, subdiv_vertex_index);
+ }
+ return;
+ }
+
+ foreach_single_vertex(foreach_context, &grid_coord, subdiv_vertex_index);
+
+ if (grid_coord.u == 0.0f) {
+ GridCoord prev_grid_coord;
+ prev_grid_coord.grid_index = start_grid_index + ((corner + num_corners - 1) % num_corners);
+ prev_grid_coord.u = grid_coord.v;
+ prev_grid_coord.v = 0.0f;
+
+ foreach_single_vertex(foreach_context, &prev_grid_coord, subdiv_vertex_index);
+ }
+
+ if (grid_coord.v == 0.0f) {
+ GridCoord next_grid_coord;
+ next_grid_coord.grid_index = start_grid_index + ((corner + 1) % num_corners);
+ next_grid_coord.u = 0.0f;
+ next_grid_coord.v = grid_coord.u;
+
+ foreach_single_vertex(foreach_context, &next_grid_coord, subdiv_vertex_index);
+ }
+}
+
+static void foreach_vertex_inner(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_poly_index),
+ const int UNUSED(coarse_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+static void foreach_vertex_every_corner(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_vertex_index),
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+static void foreach_vertex_every_edge(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls_v),
+ const int ptex_face_index,
+ const float ptex_face_u,
+ const float ptex_face_v,
+ const int UNUSED(coarse_edge_index),
+ const int UNUSED(coarse_face_index),
+ const int UNUSED(coarse_face_corner),
+ const int subdiv_vertex_index)
+{
+ const PTexCoord ptex_coord = {
+ .ptex_face_index = ptex_face_index,
+ .u = ptex_face_u,
+ .v = ptex_face_v,
+ };
+ foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index);
+}
+
+static void foreach_loop(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int UNUSED(ptex_face_index),
+ const float UNUSED(ptex_face_u),
+ const float UNUSED(ptex_face_v),
+ const int UNUSED(coarse_loop_index),
+ const int coarse_poly_index,
+ const int coarse_corner,
+ const int subdiv_loop_index,
+ const int subdiv_vertex_index,
+ const int UNUSED(subdiv_edge_index))
+{
+ MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ BLI_assert(subdiv_loop_index < reshape_smooth_context->geometry.num_corners);
+
+ Corner *corner = &reshape_smooth_context->geometry.corners[subdiv_loop_index];
+ corner->vertex = &reshape_smooth_context->geometry.vertices[subdiv_vertex_index];
+
+ const int first_grid_index = reshape_context->face_start_grid_index[coarse_poly_index];
+ corner->grid_index = first_grid_index + coarse_corner;
+}
+
+static void foreach_poly(const SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int UNUSED(coarse_poly_index),
+ const int subdiv_poly_index,
+ const int start_loop_index,
+ const int num_loops)
+{
+ MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+
+ BLI_assert(subdiv_poly_index < reshape_smooth_context->geometry.num_faces);
+
+ Dace *face = &reshape_smooth_context->geometry.faces[subdiv_poly_index];
+ face->start_corner_index = start_loop_index;
+ face->num_corners = num_loops;
+}
+
+static void foreach_vertex_of_loose_edge(const struct SubdivForeachContext *foreach_context,
+ void *UNUSED(tls),
+ const int UNUSED(coarse_edge_index),
+ const float UNUSED(u),
+ const int vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data;
+ Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index];
+
+ if (vertex->num_grid_coords != 0) {
+ vertex->is_infinite_sharp = true;
+ }
+}
+
+static void geometry_create(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ SubdivForeachContext foreach_context = {
+ .topology_info = foreach_topology_info,
+ .vertex_inner = foreach_vertex_inner,
+ .vertex_every_corner = foreach_vertex_every_corner,
+ .vertex_every_edge = foreach_vertex_every_edge,
+ .loop = foreach_loop,
+ .poly = foreach_poly,
+ .vertex_of_loose_edge = foreach_vertex_of_loose_edge,
+ .user_data = reshape_smooth_context,
+ };
+
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = (1 << reshape_context->reshape.level) + 1;
+ mesh_settings.use_optimal_display = false;
+
+ /* TODO(sergey): Tell the foreach() to ignore loose vertices. */
+ BKE_subdiv_foreach_subdiv_geometry(
+ reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh);
+}
+
+/* ================================================================================================
+ * Generation of OpenSubdiv evaluator for topology created form reshape level.
+ */
+
+static OpenSubdiv_SchemeType get_scheme_type(const OpenSubdiv_Converter *UNUSED(converter))
+{
+ return OSD_SCHEME_CATMARK;
+}
+
+static OpenSubdiv_VtxBoundaryInterpolation get_vtx_boundary_interpolation(
+ const struct OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const SubdivSettings *settings = &reshape_context->subdiv->settings;
+
+ return BKE_subdiv_converter_vtx_boundary_interpolation_from_settings(settings);
+}
+
+static OpenSubdiv_FVarLinearInterpolation get_fvar_linear_interpolation(
+ const OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const SubdivSettings *settings = &reshape_context->subdiv->settings;
+
+ return BKE_subdiv_converter_fvar_linear_from_settings(settings);
+}
+
+static bool specifies_full_topology(const OpenSubdiv_Converter *UNUSED(converter))
+{
+ return false;
+}
+
+static int get_num_faces(const OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+
+ return reshape_smooth_context->geometry.num_faces;
+}
+
+static int get_num_vertices(const OpenSubdiv_Converter *converter)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+
+ return reshape_smooth_context->geometry.num_vertices;
+}
+
+static int get_num_face_vertices(const OpenSubdiv_Converter *converter, int face_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const Dace *face = &reshape_smooth_context->geometry.faces[face_index];
+
+ return face->num_corners;
+}
+
+static void get_face_vertices(const OpenSubdiv_Converter *converter,
+ int face_index,
+ int *face_vertices)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ const Dace *face = &reshape_smooth_context->geometry.faces[face_index];
+
+ for (int i = 0; i < face->num_corners; ++i) {
+ const int corner_index = face->start_corner_index + i;
+ const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index];
+ face_vertices[i] = corner->vertex - reshape_smooth_context->geometry.vertices;
+ }
+}
+
+static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter, int vertex_index)
+{
+ const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data;
+ Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index];
+
+ return vertex->is_infinite_sharp;
+}
+
+static void converter_init(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ OpenSubdiv_Converter *converter)
+{
+ converter->getSchemeType = get_scheme_type;
+ converter->getVtxBoundaryInterpolation = get_vtx_boundary_interpolation;
+ converter->getFVarLinearInterpolation = get_fvar_linear_interpolation;
+ converter->specifiesFullTopology = specifies_full_topology;
+
+ converter->getNumFaces = get_num_faces;
+ converter->getNumEdges = NULL;
+ converter->getNumVertices = get_num_vertices;
+
+ converter->getNumFaceVertices = get_num_face_vertices;
+ converter->getFaceVertices = get_face_vertices;
+ converter->getFaceEdges = NULL;
+
+ converter->getEdgeVertices = NULL;
+ converter->getNumEdgeFaces = NULL;
+ converter->getEdgeFaces = NULL;
+ converter->getEdgeSharpness = NULL;
+
+ converter->getNumVertexEdges = NULL;
+ converter->getVertexEdges = NULL;
+ converter->getNumVertexFaces = NULL;
+ converter->getVertexFaces = NULL;
+ converter->isInfiniteSharpVertex = is_infinite_sharp_vertex;
+ converter->getVertexSharpness = NULL;
+
+ converter->getNumUVLayers = NULL;
+ converter->precalcUVLayer = NULL;
+ converter->finishUVLayer = NULL;
+ converter->getNumUVCoordinates = NULL;
+ converter->getFaceCornerUVIndex = NULL;
+
+ converter->freeUserData = NULL;
+
+ converter->user_data = (void *)reshape_smooth_context;
+}
+
+/* Create subdiv descriptor created for topology at a reshape level, */
+static void reshape_subdiv_create(MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const SubdivSettings *settings = &reshape_context->subdiv->settings;
+
+ OpenSubdiv_Converter converter;
+ converter_init(reshape_smooth_context, &converter);
+
+ Subdiv *reshape_subdiv = BKE_subdiv_new_from_converter(settings, &converter);
+ BKE_subdiv_eval_begin(reshape_subdiv);
+
+ reshape_smooth_context->reshape_subdiv = reshape_subdiv;
+
+ BKE_subdiv_converter_free(&converter);
+}
+
+/* Callback to provide coarse position for subdivision surface topology at a reshape level. */
+typedef void(ReshapeSubdivCoarsePositionCb)(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Vertex *vertex,
+ float r_P[3]);
+
+/* Refine subdivision surface topology at a reshape level for new coarse verticies positions. */
+static void reshape_subdiv_refine(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ ReshapeSubdivCoarsePositionCb coarse_position_cb)
+{
+ Subdiv *reshape_subdiv = reshape_smooth_context->reshape_subdiv;
+
+ /* TODO(sergey): For non-trivial coarse_position_cb we should multi-thread this loop. */
+
+ const int num_vertices = reshape_smooth_context->geometry.num_vertices;
+ for (int i = 0; i < num_vertices; ++i) {
+ const Vertex *vertex = &reshape_smooth_context->geometry.vertices[i];
+ float P[3];
+ coarse_position_cb(reshape_smooth_context, vertex, P);
+ reshape_subdiv->evaluator->setCoarsePositions(reshape_subdiv->evaluator, P, i, 1);
+ }
+ reshape_subdiv->evaluator->refine(reshape_subdiv->evaluator);
+}
+
+BLI_INLINE const GridCoord *reshape_subdiv_refine_vertex_grid_coord(const Vertex *vertex)
+{
+ if (vertex->num_grid_coords == 0) {
+ /* This is a loose vertex, the coordinate is not important. */
+ /* TODO(sergey): Once the subdiv_foreach() supports properly ignoring loose elements this
+ * should become an assert instead. */
+ return NULL;
+ }
+ /* NOTE: All grid coordinates will point to the same object position, so can be simple and use
+ * first grid coordinate. */
+ return &vertex->grid_coords[0];
+}
+
+/* Version of reshape_subdiv_refine() which uses coarse position from original grids. */
+static void reshape_subdiv_refine_orig_P(
+ const MultiresReshapeSmoothContext *reshape_smooth_context, const Vertex *vertex, float r_P[3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const GridCoord *grid_coord = reshape_subdiv_refine_vertex_grid_coord(vertex);
+
+ /* Check whether this is a loose vertex. */
+ if (grid_coord == NULL) {
+ zero_v3(r_P);
+ return;
+ }
+
+ float limit_P[3];
+ float tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, limit_P, tangent_matrix);
+
+ const ReshapeConstGridElement orig_grid_element =
+ multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
+
+ float D[3];
+ mul_v3_m3v3(D, tangent_matrix, orig_grid_element.displacement);
+
+ add_v3_v3v3(r_P, limit_P, D);
+}
+static void reshape_subdiv_refine_orig(const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ reshape_subdiv_refine(reshape_smooth_context, reshape_subdiv_refine_orig_P);
+}
+
+/* Version of reshape_subdiv_refine() which uses coarse position from final grids. */
+static void reshape_subdiv_refine_final_P(
+ const MultiresReshapeSmoothContext *reshape_smooth_context, const Vertex *vertex, float r_P[3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ const GridCoord *grid_coord = reshape_subdiv_refine_vertex_grid_coord(vertex);
+
+ /* Check whether this is a loose vertex. */
+ if (grid_coord == NULL) {
+ zero_v3(r_P);
+ return;
+ }
+
+ const ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(
+ reshape_context, grid_coord);
+
+ /* NOTE: At this point in reshape/propagate pipeline grid displacement is actually storing object
+ * vertices coordinates. */
+ copy_v3_v3(r_P, grid_element.displacement);
+}
+static void reshape_subdiv_refine_final(const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ reshape_subdiv_refine(reshape_smooth_context, reshape_subdiv_refine_final_P);
+}
+
+static void reshape_subdiv_evaluate_limit_at_grid(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ float limit_P[3],
+ float r_tangent_matrix[3][3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ float dPdu[3], dPdv[3];
+ BKE_subdiv_eval_limit_point_and_derivatives(reshape_smooth_context->reshape_subdiv,
+ ptex_coord->ptex_face_index,
+ ptex_coord->u,
+ ptex_coord->v,
+ limit_P,
+ dPdu,
+ dPdv);
+
+ const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index);
+ BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, corner);
+}
+
+/* ================================================================================================
+ * Evaluation of base surface.
+ */
+
+static void evaluate_base_surface_grids_callback(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ float limit_P[3];
+ float tangent_matrix[3][3];
+ reshape_subdiv_evaluate_limit_at_grid(
+ reshape_smooth_context, ptex_coord, grid_coord, limit_P, tangent_matrix);
+
+ base_surface_grids_write(reshape_smooth_context, grid_coord, limit_P, tangent_matrix);
+}
+
+static void evaluate_base_surface_grids(const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ foreach_toplevel_grid_coord(reshape_smooth_context, evaluate_base_surface_grids_callback, NULL);
+}
+
+/* ================================================================================================
+ * Evaluation of new surface.
+ */
+
+/* Evaluate final position of the original (pre-sculpt-edit) point position at a given grid
+ * coordinate. */
+static void evaluate_final_original_point(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const GridCoord *grid_coord,
+ float r_orig_final_P[3])
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ /* Element of an original MDISPS grid) */
+ const ReshapeConstGridElement orig_grid_element =
+ multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord);
+
+ /* Limit surface of the base mesh. */
+ float base_mesh_limit_P[3];
+ float base_mesh_tangent_matrix[3][3];
+ multires_reshape_evaluate_limit_at_grid(
+ reshape_context, grid_coord, base_mesh_limit_P, base_mesh_tangent_matrix);
+
+ /* Convert original displacement from tangent space to object space. */
+ float orig_displacement[3];
+ mul_v3_m3v3(orig_displacement, base_mesh_tangent_matrix, orig_grid_element.displacement);
+
+ /* Final point = limit surface + displacement. */
+ add_v3_v3v3(r_orig_final_P, base_mesh_limit_P, orig_displacement);
+}
+
+static void evaluate_higher_grid_positions_with_details_callback(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+
+ /* Position of the original veretx at top level. */
+ float orig_final_P[3];
+ evaluate_final_original_point(reshape_smooth_context, grid_coord, orig_final_P);
+
+ /* Original surface point on sculpt level (sculpt level before edits in sculpt mode). */
+ const SurfacePoint *orig_sculpt_point = base_surface_grids_read(reshape_smooth_context,
+ grid_coord);
+
+ /* Difference between original top level and original sculpt level in object space. */
+ float original_detail_delta[3];
+ sub_v3_v3v3(original_detail_delta, orig_final_P, orig_sculpt_point->P);
+
+ /* Difference between original top level and original sculpt level in tangent space of original
+ * sculpt level. */
+ float original_detail_delta_tangent[3];
+ float original_sculpt_tangent_matrix_inv[3][3];
+ invert_m3_m3(original_sculpt_tangent_matrix_inv, orig_sculpt_point->tangent_matrix);
+ mul_v3_m3v3(
+ original_detail_delta_tangent, original_sculpt_tangent_matrix_inv, original_detail_delta);
+
+ /* Limit surface of smoothed (subdivided) edited sculpt level. */
+ float smooth_limit_P[3];
+ float smooth_tangent_matrix[3][3];
+ reshape_subdiv_evaluate_limit_at_grid(
+ reshape_smooth_context, ptex_coord, grid_coord, smooth_limit_P, smooth_tangent_matrix);
+
+ /* Add original detail to the smoothed surface. */
+ float smooth_delta[3];
+ mul_v3_m3v3(smooth_delta, smooth_tangent_matrix, original_detail_delta_tangent);
+
+ /* Grid element of the result.
+ *
+ * NOTE: Displacement is storing object space coordinate. */
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ grid_coord);
+
+ add_v3_v3v3(grid_element.displacement, smooth_limit_P, smooth_delta);
+}
+static void evaluate_higher_grid_positions_with_details(
+ const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ foreach_toplevel_grid_coord(
+ reshape_smooth_context, evaluate_higher_grid_positions_with_details_callback, NULL);
+}
+
+static void evaluate_higher_grid_positions_callback(
+ const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const PTexCoord *ptex_coord,
+ const GridCoord *grid_coord,
+ void *UNUSED(userdata_v))
+{
+ const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
+ Subdiv *reshape_subdiv = reshape_smooth_context->reshape_subdiv;
+
+ ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context,
+ 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);
+ }
+}
+
+static void evaluate_higher_grid_positions(
+ const MultiresReshapeSmoothContext *reshape_smooth_context)
+{
+ foreach_toplevel_grid_coord(
+ reshape_smooth_context, evaluate_higher_grid_positions_callback, NULL);
+}
+/* ================================================================================================
+ * Entry point.
+ */
+
+void multires_reshape_smooth_object_grids_with_details(
+ const MultiresReshapeContext *reshape_context)
+{
+ const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
+ if (level_difference == 0) {
+ /* Early output. */
+ return;
+ }
+
+ MultiresReshapeSmoothContext reshape_smooth_context;
+ context_init(&reshape_smooth_context, reshape_context);
+
+ geometry_create(&reshape_smooth_context);
+
+ reshape_subdiv_create(&reshape_smooth_context);
+
+ base_surface_grids_allocate(&reshape_smooth_context);
+ reshape_subdiv_refine_orig(&reshape_smooth_context);
+ evaluate_base_surface_grids(&reshape_smooth_context);
+
+ reshape_subdiv_refine_final(&reshape_smooth_context);
+ evaluate_higher_grid_positions_with_details(&reshape_smooth_context);
+
+ context_free(&reshape_smooth_context);
+}
+
+void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context)
+{
+ const int level_difference = (reshape_context->top.level - reshape_context->reshape.level);
+ if (level_difference == 0) {
+ /* Early output. */
+ return;
+ }
+
+ MultiresReshapeSmoothContext reshape_smooth_context;
+ context_init(&reshape_smooth_context, reshape_context);
+
+ geometry_create(&reshape_smooth_context);
+
+ reshape_subdiv_create(&reshape_smooth_context);
+
+ reshape_subdiv_refine_final(&reshape_smooth_context);
+ evaluate_higher_grid_positions(&reshape_smooth_context);
+
+ context_free(&reshape_smooth_context);
+}