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/shrinkwrap.cc')
-rw-r--r--source/blender/blenkernel/intern/shrinkwrap.cc1591
1 files changed, 1591 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/shrinkwrap.cc b/source/blender/blenkernel/intern/shrinkwrap.cc
new file mode 100644
index 00000000000..65226a5db9d
--- /dev/null
+++ b/source/blender/blenkernel/intern/shrinkwrap.cc
@@ -0,0 +1,1591 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+#include <memory.h>
+
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+#include "BLI_math_solvers.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_lattice.h"
+#include "BKE_lib_id.h"
+#include "BKE_modifier.h"
+#include "BKE_shrinkwrap.h"
+
+#include "BKE_deform.h"
+#include "BKE_editmesh.h"
+#include "BKE_mesh.h" /* for OMP limits. */
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_subsurf.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_strict_flags.h"
+
+/* for timing... */
+#if 0
+# include "PIL_time_utildefines.h"
+#else
+# define TIMEIT_BENCH(expr, id) (expr)
+#endif
+
+/* Util macros */
+#define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n"))
+
+struct ShrinkwrapCalcData {
+ ShrinkwrapModifierData *smd; /* shrinkwrap modifier data */
+
+ Object *ob; /* object we are applying shrinkwrap to */
+
+ MVert *vert; /* Array of verts being projected. */
+ const float (*vert_normals)[3];
+ /* Vertices being shrink-wrapped. */
+ float (*vertexCos)[3];
+ int numVerts;
+
+ const MDeformVert *dvert; /* Pointer to mdeform array */
+ int vgroup; /* Vertex group num */
+ bool invert_vgroup; /* invert vertex group influence */
+
+ Mesh *target; /* mesh we are shrinking to */
+ SpaceTransform local2target; /* transform to move between local and target space */
+ ShrinkwrapTreeData *tree; /* mesh BVH tree data */
+
+ Object *aux_target;
+
+ float keepDist; /* Distance to keep above target surface (units are in local space) */
+};
+
+struct ShrinkwrapCalcCBData {
+ ShrinkwrapCalcData *calc;
+
+ ShrinkwrapTreeData *tree;
+ ShrinkwrapTreeData *aux_tree;
+
+ float *proj_axis;
+ SpaceTransform *local2aux;
+};
+
+bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode)
+{
+ return (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) ||
+ (shrinkType != MOD_SHRINKWRAP_NEAREST_VERTEX &&
+ shrinkMode == MOD_SHRINKWRAP_ABOVE_SURFACE);
+}
+
+bool BKE_shrinkwrap_init_tree(
+ ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals)
+{
+ memset(data, 0, sizeof(*data));
+
+ if (mesh == nullptr) {
+ return false;
+ }
+
+ /* We could create a BVH tree from the edit mesh,
+ * however accessing normals from the face/loop normals gets more involved.
+ * Convert mesh data since this isn't typically used in edit-mode. */
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+
+ if (mesh->totvert <= 0) {
+ return false;
+ }
+
+ data->mesh = mesh;
+ data->polys = BKE_mesh_polys(mesh);
+
+ if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
+ data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2);
+
+ return data->bvh != nullptr;
+ }
+
+ if (mesh->totpoly <= 0) {
+ return false;
+ }
+
+ data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_LOOPTRI, 4);
+
+ if (data->bvh == nullptr) {
+ return false;
+ }
+
+ if (force_normals || BKE_shrinkwrap_needs_normals(shrinkType, shrinkMode)) {
+ data->pnors = BKE_mesh_poly_normals_ensure(mesh);
+ if ((mesh->flag & ME_AUTOSMOOTH) != 0) {
+ data->clnors = static_cast<const float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
+ }
+ }
+
+ if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
+ data->boundary = mesh->runtime->shrinkwrap_data;
+ }
+
+ return true;
+}
+
+void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *data)
+{
+ free_bvhtree_from_mesh(&data->treeData);
+}
+
+void BKE_shrinkwrap_discard_boundary_data(Mesh *mesh)
+{
+ ShrinkwrapBoundaryData *data = mesh->runtime->shrinkwrap_data;
+
+ if (data != nullptr) {
+ MEM_freeN((void *)data->edge_is_boundary);
+ MEM_freeN((void *)data->looptri_has_boundary);
+ MEM_freeN((void *)data->vert_boundary_id);
+ MEM_freeN((void *)data->boundary_verts);
+
+ MEM_freeN(data);
+ }
+
+ mesh->runtime->shrinkwrap_data = nullptr;
+}
+
+/* Accumulate edge for average boundary edge direction. */
+static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata,
+ signed char *status,
+ int index,
+ const float edge_dir[3],
+ signed char side)
+{
+ BLI_assert(index >= 0);
+ float *direction = vdata[index].direction;
+
+ /* Invert the direction vector if either:
+ * - This is the second edge and both edges have the vertex as start or end.
+ * - For third and above, if it points in the wrong direction.
+ */
+ if (status[index] >= 0 ? status[index] == side : dot_v3v3(direction, edge_dir) < 0) {
+ sub_v3_v3(direction, edge_dir);
+ }
+ else {
+ add_v3_v3(direction, edge_dir);
+ }
+
+ status[index] = (status[index] == 0) ? side : -1;
+}
+
+static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh)
+{
+ const MVert *mvert = BKE_mesh_verts(mesh);
+ const MEdge *medge = BKE_mesh_edges(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
+
+ /* Count faces per edge (up to 2). */
+ char *edge_mode = static_cast<char *>(
+ MEM_calloc_arrayN(size_t(mesh->totedge), sizeof(char), __func__));
+
+ for (int i = 0; i < mesh->totloop; i++) {
+ uint eidx = mloop[i].e;
+
+ if (edge_mode[eidx] < 2) {
+ edge_mode[eidx]++;
+ }
+ }
+
+ /* Build the boundary edge bitmask. */
+ BLI_bitmap *edge_is_boundary = BLI_BITMAP_NEW(mesh->totedge,
+ "ShrinkwrapBoundaryData::edge_is_boundary");
+ uint num_boundary_edges = 0;
+
+ for (int i = 0; i < mesh->totedge; i++) {
+ edge_mode[i] = (edge_mode[i] == 1);
+
+ if (edge_mode[i]) {
+ BLI_BITMAP_ENABLE(edge_is_boundary, i);
+ num_boundary_edges++;
+ }
+ }
+
+ /* If no boundary, return nullptr. */
+ if (num_boundary_edges == 0) {
+ MEM_freeN(edge_is_boundary);
+ MEM_freeN(edge_mode);
+ return nullptr;
+ }
+
+ /* Allocate the data object. */
+ ShrinkwrapBoundaryData *data = MEM_cnew<ShrinkwrapBoundaryData>(__func__);
+
+ data->edge_is_boundary = edge_is_boundary;
+
+ /* Build the boundary looptri bitmask. */
+ const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh);
+ int totlooptri = BKE_mesh_runtime_looptri_len(mesh);
+
+ BLI_bitmap *looptri_has_boundary = BLI_BITMAP_NEW(totlooptri,
+ "ShrinkwrapBoundaryData::looptri_is_boundary");
+
+ for (int i = 0; i < totlooptri; i++) {
+ int edges[3];
+ BKE_mesh_looptri_get_real_edges(mesh, &mlooptri[i], edges);
+
+ for (int j = 0; j < 3; j++) {
+ if (edges[j] >= 0 && edge_mode[edges[j]]) {
+ BLI_BITMAP_ENABLE(looptri_has_boundary, i);
+ break;
+ }
+ }
+ }
+
+ data->looptri_has_boundary = looptri_has_boundary;
+
+ /* Find boundary vertices and build a mapping table for compact storage of data. */
+ int *vert_boundary_id = static_cast<int *>(
+ MEM_calloc_arrayN(size_t(mesh->totvert), sizeof(int), __func__));
+
+ for (int i = 0; i < mesh->totedge; i++) {
+ if (edge_mode[i]) {
+ const MEdge *edge = &medge[i];
+
+ vert_boundary_id[edge->v1] = 1;
+ vert_boundary_id[edge->v2] = 1;
+ }
+ }
+
+ uint num_boundary_verts = 0;
+
+ for (int i = 0; i < mesh->totvert; i++) {
+ vert_boundary_id[i] = (vert_boundary_id[i] != 0) ? int(num_boundary_verts++) : -1;
+ }
+
+ data->vert_boundary_id = vert_boundary_id;
+ data->num_boundary_verts = num_boundary_verts;
+
+ /* Compute average directions. */
+ ShrinkwrapBoundaryVertData *boundary_verts = static_cast<ShrinkwrapBoundaryVertData *>(
+ MEM_calloc_arrayN(num_boundary_verts, sizeof(*boundary_verts), __func__));
+
+ signed char *vert_status = static_cast<signed char *>(
+ MEM_calloc_arrayN(num_boundary_verts, sizeof(char), __func__));
+
+ for (int i = 0; i < mesh->totedge; i++) {
+ if (edge_mode[i]) {
+ const MEdge *edge = &medge[i];
+
+ float dir[3];
+ sub_v3_v3v3(dir, mvert[edge->v2].co, mvert[edge->v1].co);
+ normalize_v3(dir);
+
+ merge_vert_dir(boundary_verts, vert_status, vert_boundary_id[edge->v1], dir, 1);
+ merge_vert_dir(boundary_verts, vert_status, vert_boundary_id[edge->v2], dir, 2);
+ }
+ }
+
+ MEM_freeN(vert_status);
+
+ /* Finalize average direction and compute normal. */
+ const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
+ for (int i = 0; i < mesh->totvert; i++) {
+ int bidx = vert_boundary_id[i];
+
+ if (bidx >= 0) {
+ ShrinkwrapBoundaryVertData *vdata = &boundary_verts[bidx];
+ float tmp[3];
+
+ normalize_v3(vdata->direction);
+
+ cross_v3_v3v3(tmp, vert_normals[i], vdata->direction);
+ cross_v3_v3v3(vdata->normal_plane, tmp, vert_normals[i]);
+ normalize_v3(vdata->normal_plane);
+ }
+ }
+
+ data->boundary_verts = boundary_verts;
+
+ MEM_freeN(edge_mode);
+ return data;
+}
+
+void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh)
+{
+ BKE_shrinkwrap_discard_boundary_data(mesh);
+
+ mesh->runtime->shrinkwrap_data = shrinkwrap_build_boundary_data(mesh);
+}
+
+/**
+ * Shrink-wrap to the nearest vertex
+ *
+ * it builds a BVH-tree of vertices we can attach to and then
+ * for each vertex performs a nearest vertex search on the tree.
+ */
+static void shrinkwrap_calc_nearest_vertex_cb_ex(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict tls)
+{
+ ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
+
+ ShrinkwrapCalcData *calc = data->calc;
+ BVHTreeFromMesh *treeData = &data->tree->treeData;
+ BVHTreeNearest *nearest = static_cast<BVHTreeNearest *>(tls->userdata_chunk);
+
+ float *co = calc->vertexCos[i];
+ float tmp_co[3];
+ float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
+
+ if (calc->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight == 0.0f) {
+ return;
+ }
+
+ /* Convert the vertex to tree coordinates */
+ if (calc->vert) {
+ copy_v3_v3(tmp_co, calc->vert[i].co);
+ }
+ else {
+ copy_v3_v3(tmp_co, co);
+ }
+ BLI_space_transform_apply(&calc->local2target, tmp_co);
+
+ /* Use local proximity heuristics (to reduce the nearest search)
+ *
+ * If we already had an hit before.. we assume this vertex is going to have a close hit to that
+ * other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
+ * This will lead in pruning of the search tree. */
+ if (nearest->index != -1) {
+ nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
+ }
+ else {
+ nearest->dist_sq = FLT_MAX;
+ }
+
+ BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData);
+
+ /* Found the nearest vertex */
+ if (nearest->index != -1) {
+ /* Adjusting the vertex weight,
+ * so that after interpolating it keeps a certain distance from the nearest position */
+ if (nearest->dist_sq > FLT_EPSILON) {
+ const float dist = sqrtf(nearest->dist_sq);
+ weight *= (dist - calc->keepDist) / dist;
+ }
+
+ /* Convert the coordinates back to mesh coordinates */
+ copy_v3_v3(tmp_co, nearest->co);
+ BLI_space_transform_invert(&calc->local2target, tmp_co);
+
+ interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
+ }
+}
+
+static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc)
+{
+ BVHTreeNearest nearest = NULL_BVHTreeNearest;
+
+ /* Setup nearest */
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+
+ ShrinkwrapCalcCBData data{};
+ data.calc = calc;
+ data.tree = calc->tree;
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
+ settings.userdata_chunk = &nearest;
+ settings.userdata_chunk_size = sizeof(nearest);
+ BLI_task_parallel_range(
+ 0, calc->numVerts, &data, shrinkwrap_calc_nearest_vertex_cb_ex, &settings);
+}
+
+bool BKE_shrinkwrap_project_normal(char options,
+ const float vert[3],
+ const float dir[3],
+ const float ray_radius,
+ const SpaceTransform *transf,
+ ShrinkwrapTreeData *tree,
+ BVHTreeRayHit *hit)
+{
+ /* don't use this because this dist value could be incompatible
+ * this value used by the callback for comparing previous/new dist values.
+ * also, at the moment there is no need to have a corrected 'dist' value */
+ // #define USE_DIST_CORRECT
+
+ float tmp_co[3], tmp_no[3];
+ const float *co, *no;
+ BVHTreeRayHit hit_tmp;
+
+ /* Copy from hit (we need to convert hit rays from one space coordinates to the other */
+ memcpy(&hit_tmp, hit, sizeof(hit_tmp));
+
+ /* Apply space transform (TODO readjust dist) */
+ if (transf) {
+ copy_v3_v3(tmp_co, vert);
+ BLI_space_transform_apply(transf, tmp_co);
+ co = tmp_co;
+
+ copy_v3_v3(tmp_no, dir);
+ BLI_space_transform_apply_normal(transf, tmp_no);
+ no = tmp_no;
+
+#ifdef USE_DIST_CORRECT
+ hit_tmp.dist *= mat4_to_scale(((SpaceTransform *)transf)->local2target);
+#endif
+ }
+ else {
+ co = vert;
+ no = dir;
+ }
+
+ hit_tmp.index = -1;
+
+ BLI_bvhtree_ray_cast(
+ tree->bvh, co, no, ray_radius, &hit_tmp, tree->treeData.raycast_callback, &tree->treeData);
+
+ if (hit_tmp.index != -1) {
+ /* invert the normal first so face culling works on rotated objects */
+ if (transf) {
+ BLI_space_transform_invert_normal(transf, hit_tmp.no);
+ }
+
+ if (options & MOD_SHRINKWRAP_CULL_TARGET_MASK) {
+ /* Apply back-face. */
+ const float dot = dot_v3v3(dir, hit_tmp.no);
+ if (((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) ||
+ ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f)) {
+ return false; /* Ignore hit */
+ }
+ }
+
+ if (transf) {
+ /* Inverting space transform (TODO: make coherent with the initial dist readjust). */
+ BLI_space_transform_invert(transf, hit_tmp.co);
+#ifdef USE_DIST_CORRECT
+ hit_tmp.dist = len_v3v3(vert, hit_tmp.co);
+#endif
+ }
+
+ BLI_assert(hit_tmp.dist <= hit->dist);
+
+ memcpy(hit, &hit_tmp, sizeof(hit_tmp));
+ return true;
+ }
+ return false;
+}
+
+static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict tls)
+{
+ ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
+
+ ShrinkwrapCalcData *calc = data->calc;
+ ShrinkwrapTreeData *tree = data->tree;
+ ShrinkwrapTreeData *aux_tree = data->aux_tree;
+
+ float *proj_axis = data->proj_axis;
+ SpaceTransform *local2aux = data->local2aux;
+
+ BVHTreeRayHit *hit = static_cast<BVHTreeRayHit *>(tls->userdata_chunk);
+
+ const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit;
+ float *co = calc->vertexCos[i];
+ float tmp_co[3], tmp_no[3];
+ float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
+
+ if (calc->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight == 0.0f) {
+ return;
+ }
+
+ if (calc->vert != nullptr && calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
+ /* calc->vert contains verts from evaluated mesh. */
+ /* These coordinates are deformed by vertexCos only for normal projection
+ * (to get correct normals) for other cases calc->verts contains undeformed coordinates and
+ * vertexCos should be used */
+ copy_v3_v3(tmp_co, calc->vert[i].co);
+ copy_v3_v3(tmp_no, calc->vert_normals[i]);
+ }
+ else {
+ copy_v3_v3(tmp_co, co);
+ copy_v3_v3(tmp_no, proj_axis);
+ }
+
+ hit->index = -1;
+
+ /* TODO: we should use FLT_MAX here, but sweep-sphere code isn't prepared for that. */
+ hit->dist = BVH_RAYCAST_DIST_MAX;
+
+ bool is_aux = false;
+
+ /* Project over positive direction of axis. */
+ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) {
+ if (aux_tree) {
+ if (BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, 0.0, local2aux, aux_tree, hit)) {
+ is_aux = true;
+ }
+ }
+
+ if (BKE_shrinkwrap_project_normal(
+ calc->smd->shrinkOpts, tmp_co, tmp_no, 0.0, &calc->local2target, tree, hit)) {
+ is_aux = false;
+ }
+ }
+
+ /* Project over negative direction of axis */
+ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) {
+ float inv_no[3];
+ negate_v3_v3(inv_no, tmp_no);
+
+ char options = calc->smd->shrinkOpts;
+
+ if ((options & MOD_SHRINKWRAP_INVERT_CULL_TARGET) &&
+ (options & MOD_SHRINKWRAP_CULL_TARGET_MASK)) {
+ options ^= MOD_SHRINKWRAP_CULL_TARGET_MASK;
+ }
+
+ if (aux_tree) {
+ if (BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, 0.0, local2aux, aux_tree, hit)) {
+ is_aux = true;
+ }
+ }
+
+ if (BKE_shrinkwrap_project_normal(
+ options, tmp_co, inv_no, 0.0, &calc->local2target, tree, hit)) {
+ is_aux = false;
+ }
+ }
+
+ /* don't set the initial dist (which is more efficient),
+ * because its calculated in the targets space, we want the dist in our own space */
+ if (proj_limit_squared != 0.0f) {
+ if (hit->index != -1 && len_squared_v3v3(hit->co, co) > proj_limit_squared) {
+ hit->index = -1;
+ }
+ }
+
+ if (hit->index != -1) {
+ if (is_aux) {
+ BKE_shrinkwrap_snap_point_to_surface(aux_tree,
+ local2aux,
+ calc->smd->shrinkMode,
+ hit->index,
+ hit->co,
+ hit->no,
+ calc->keepDist,
+ tmp_co,
+ hit->co);
+ }
+ else {
+ BKE_shrinkwrap_snap_point_to_surface(tree,
+ &calc->local2target,
+ calc->smd->shrinkMode,
+ hit->index,
+ hit->co,
+ hit->no,
+ calc->keepDist,
+ tmp_co,
+ hit->co);
+ }
+
+ interp_v3_v3v3(co, co, hit->co, weight);
+ }
+}
+
+static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
+{
+ /* Options about projection direction */
+ float proj_axis[3] = {0.0f, 0.0f, 0.0f};
+
+ /* Ray-cast and tree stuff. */
+
+ /** \note 'hit.dist' is kept in the targets space, this is only used
+ * for finding the best hit, to get the real dist,
+ * measure the len_v3v3() from the input coord to hit.co */
+ BVHTreeRayHit hit;
+
+ /* auxiliary target */
+ Mesh *auxMesh = nullptr;
+ ShrinkwrapTreeData *aux_tree = nullptr;
+ ShrinkwrapTreeData aux_tree_stack;
+ SpaceTransform local2aux;
+
+ /* If the user doesn't allows to project in any direction of projection axis
+ * then there's nothing todo. */
+ if ((calc->smd->shrinkOpts &
+ (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) {
+ return;
+ }
+
+ /* Prepare data to retrieve the direction in which we should project each vertex */
+ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
+ if (calc->vert == nullptr) {
+ return;
+ }
+ }
+ else {
+ /* The code supports any axis that is a combination of X,Y,Z
+ * although currently UI only allows to set the 3 different axis */
+ if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) {
+ proj_axis[0] = 1.0f;
+ }
+ if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) {
+ proj_axis[1] = 1.0f;
+ }
+ if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) {
+ proj_axis[2] = 1.0f;
+ }
+
+ normalize_v3(proj_axis);
+
+ /* Invalid projection direction */
+ if (len_squared_v3(proj_axis) < FLT_EPSILON) {
+ return;
+ }
+ }
+
+ if (calc->aux_target) {
+ auxMesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(calc->aux_target);
+ if (!auxMesh) {
+ return;
+ }
+ BLI_SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->aux_target);
+ }
+
+ if (BKE_shrinkwrap_init_tree(
+ &aux_tree_stack, auxMesh, calc->smd->shrinkType, calc->smd->shrinkMode, false)) {
+ aux_tree = &aux_tree_stack;
+ }
+
+ /* After successfully build the trees, start projection vertices. */
+ ShrinkwrapCalcCBData data{};
+ data.calc = calc;
+ data.tree = calc->tree;
+ data.aux_tree = aux_tree;
+ data.proj_axis = proj_axis;
+ data.local2aux = &local2aux;
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
+ settings.userdata_chunk = &hit;
+ settings.userdata_chunk_size = sizeof(hit);
+ BLI_task_parallel_range(
+ 0, calc->numVerts, &data, shrinkwrap_calc_normal_projection_cb_ex, &settings);
+
+ /* free data structures */
+ if (aux_tree) {
+ BKE_shrinkwrap_free_tree(aux_tree);
+ }
+}
+
+/*
+ * Shrinkwrap Target Surface Project mode
+ *
+ * It uses Newton's method to find a surface location with its
+ * smooth normal pointing at the original point.
+ *
+ * The equation system on barycentric weights and normal multiplier:
+ *
+ * (w0*V0 + w1*V1 + w2*V2) + l * (w0*N0 + w1*N1 + w2*N2) - CO = 0
+ * w0 + w1 + w2 = 1
+ *
+ * The actual solution vector is [ w0, w1, l ], with w2 eliminated.
+ */
+
+//#define TRACE_TARGET_PROJECT
+
+struct TargetProjectTriData {
+ const float **vtri_co;
+ const float (*vtri_no)[3];
+ const float *point_co;
+
+ float n0_minus_n2[3], n1_minus_n2[3];
+ float c0_minus_c2[3], c1_minus_c2[3];
+
+ /* Current interpolated position and normal. */
+ float co_interp[3], no_interp[3];
+};
+
+/* Computes the deviation of the equation system from goal. */
+static void target_project_tri_deviation(void *userdata, const float x[3], float r_delta[3])
+{
+ TargetProjectTriData *data = static_cast<TargetProjectTriData *>(userdata);
+
+ const float w[3] = {x[0], x[1], 1.0f - x[0] - x[1]};
+ interp_v3_v3v3v3(data->co_interp, data->vtri_co[0], data->vtri_co[1], data->vtri_co[2], w);
+ interp_v3_v3v3v3(data->no_interp, data->vtri_no[0], data->vtri_no[1], data->vtri_no[2], w);
+
+ madd_v3_v3v3fl(r_delta, data->co_interp, data->no_interp, x[2]);
+ sub_v3_v3(r_delta, data->point_co);
+}
+
+/* Computes the Jacobian matrix of the equation system. */
+static void target_project_tri_jacobian(void *userdata, const float x[3], float r_jacobian[3][3])
+{
+ TargetProjectTriData *data = static_cast<TargetProjectTriData *>(userdata);
+
+ madd_v3_v3v3fl(r_jacobian[0], data->c0_minus_c2, data->n0_minus_n2, x[2]);
+ madd_v3_v3v3fl(r_jacobian[1], data->c1_minus_c2, data->n1_minus_n2, x[2]);
+
+ copy_v3_v3(r_jacobian[2], data->vtri_no[2]);
+ madd_v3_v3fl(r_jacobian[2], data->n0_minus_n2, x[0]);
+ madd_v3_v3fl(r_jacobian[2], data->n1_minus_n2, x[1]);
+}
+
+/* Clamp barycentric weights to the triangle. */
+static void target_project_tri_clamp(float x[3])
+{
+ if (x[0] < 0.0f) {
+ x[0] = 0.0f;
+ }
+ if (x[1] < 0.0f) {
+ x[1] = 0.0f;
+ }
+ if (x[0] + x[1] > 1.0f) {
+ x[0] = x[0] / (x[0] + x[1]);
+ x[1] = 1.0f - x[0];
+ }
+}
+
+/* Correct the Newton's method step to keep the coordinates within the triangle. */
+static bool target_project_tri_correct(void * /*userdata*/,
+ const float x[3],
+ float step[3],
+ float x_next[3])
+{
+ /* Insignificant correction threshold */
+ const float epsilon = 1e-5f;
+ /* Dot product threshold for checking if step is 'clearly' pointing outside. */
+ const float dir_epsilon = 0.5f;
+ bool fixed = false, locked = false;
+
+ /* The barycentric coordinate domain is a triangle bounded by
+ * the X and Y axes, plus the x+y=1 diagonal. First, clamp the
+ * movement against the diagonal. Note that step is subtracted. */
+ float sum = x[0] + x[1];
+ float sstep = -(step[0] + step[1]);
+
+ if (sum + sstep > 1.0f) {
+ float ldist = 1.0f - sum;
+
+ /* If already at the boundary, slide along it. */
+ if (ldist < epsilon * float(M_SQRT2)) {
+ float step_len = len_v2(step);
+
+ /* Abort if the solution is clearly outside the domain. */
+ if (step_len > epsilon && sstep > step_len * dir_epsilon * float(M_SQRT2)) {
+ return false;
+ }
+
+ /* Project the new position onto the diagonal. */
+ add_v2_fl(step, (sum + sstep - 1.0f) * 0.5f);
+ fixed = locked = true;
+ }
+ else {
+ /* Scale a significant step down to arrive at the boundary. */
+ mul_v3_fl(step, ldist / sstep);
+ fixed = true;
+ }
+ }
+
+ /* Weight 0 and 1 boundary checks - along axis. */
+ for (int i = 0; i < 2; i++) {
+ if (step[i] > x[i]) {
+ /* If already at the boundary, slide along it. */
+ if (x[i] < epsilon) {
+ float step_len = len_v2(step);
+
+ /* Abort if the solution is clearly outside the domain. */
+ if (step_len > epsilon && (locked || step[i] > step_len * dir_epsilon)) {
+ return false;
+ }
+
+ /* Reset precision errors to stay at the boundary. */
+ step[i] = x[i];
+ fixed = true;
+ }
+ else {
+ /* Scale a significant step down to arrive at the boundary. */
+ mul_v3_fl(step, x[i] / step[i]);
+ fixed = true;
+ }
+ }
+ }
+
+ /* Recompute and clamp the new coordinates after step correction. */
+ if (fixed) {
+ sub_v3_v3v3(x_next, x, step);
+ target_project_tri_clamp(x_next);
+ }
+
+ return true;
+}
+
+static bool target_project_solve_point_tri(const float *vtri_co[3],
+ const float vtri_no[3][3],
+ const float point_co[3],
+ const float hit_co[3],
+ float hit_dist_sq,
+ float r_hit_co[3],
+ float r_hit_no[3])
+{
+ float x[3], tmp[3];
+ float dist = sqrtf(hit_dist_sq);
+ float magnitude_estimate = dist + len_manhattan_v3(vtri_co[0]) + len_manhattan_v3(vtri_co[1]) +
+ len_manhattan_v3(vtri_co[2]);
+ float epsilon = magnitude_estimate * 1.0e-6f;
+
+ /* Initial solution vector: barycentric weights plus distance along normal. */
+ interp_weights_tri_v3(x, UNPACK3(vtri_co), hit_co);
+
+ interp_v3_v3v3v3(r_hit_no, UNPACK3(vtri_no), x);
+ sub_v3_v3v3(tmp, point_co, hit_co);
+
+ x[2] = (dot_v3v3(tmp, r_hit_no) < 0) ? -dist : dist;
+
+ /* Solve the equations iteratively. */
+ TargetProjectTriData tri_data{};
+ tri_data.vtri_co = vtri_co;
+ tri_data.vtri_no = vtri_no;
+ tri_data.point_co = point_co;
+
+ sub_v3_v3v3(tri_data.n0_minus_n2, vtri_no[0], vtri_no[2]);
+ sub_v3_v3v3(tri_data.n1_minus_n2, vtri_no[1], vtri_no[2]);
+ sub_v3_v3v3(tri_data.c0_minus_c2, vtri_co[0], vtri_co[2]);
+ sub_v3_v3v3(tri_data.c1_minus_c2, vtri_co[1], vtri_co[2]);
+
+ target_project_tri_clamp(x);
+
+#ifdef TRACE_TARGET_PROJECT
+ const bool trace = true;
+#else
+ const bool trace = false;
+#endif
+
+ bool ok = BLI_newton3d_solve(target_project_tri_deviation,
+ target_project_tri_jacobian,
+ target_project_tri_correct,
+ &tri_data,
+ epsilon,
+ 20,
+ trace,
+ x,
+ x);
+
+ if (ok) {
+ copy_v3_v3(r_hit_co, tri_data.co_interp);
+ copy_v3_v3(r_hit_no, tri_data.no_interp);
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool update_hit(BVHTreeNearest *nearest,
+ int index,
+ const float co[3],
+ const float hit_co[3],
+ const float hit_no[3])
+{
+ float dist_sq = len_squared_v3v3(hit_co, co);
+
+ if (dist_sq < nearest->dist_sq) {
+#ifdef TRACE_TARGET_PROJECT
+ printf(
+ "#=#=#> %d (%.3f,%.3f,%.3f) %g < %g\n", index, UNPACK3(hit_co), dist_sq, nearest->dist_sq);
+#endif
+ nearest->index = index;
+ nearest->dist_sq = dist_sq;
+ copy_v3_v3(nearest->co, hit_co);
+ normalize_v3_v3(nearest->no, hit_no);
+ return true;
+ }
+
+ return false;
+}
+
+/* Target projection on a non-manifold boundary edge -
+ * treats it like an infinitely thin cylinder. */
+static void target_project_edge(const ShrinkwrapTreeData *tree,
+ int index,
+ const float co[3],
+ BVHTreeNearest *nearest,
+ int eidx)
+{
+ const BVHTreeFromMesh *data = &tree->treeData;
+ const MEdge *edge = &data->edge[eidx];
+ const float *vedge_co[2] = {data->vert[edge->v1].co, data->vert[edge->v2].co};
+
+#ifdef TRACE_TARGET_PROJECT
+ printf("EDGE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f)\n",
+ eidx,
+ UNPACK3(vedge_co[0]),
+ UNPACK3(vedge_co[1]));
+#endif
+
+ /* Retrieve boundary vertex IDs */
+ const int *vert_boundary_id = tree->boundary->vert_boundary_id;
+ int bid1 = vert_boundary_id[edge->v1], bid2 = vert_boundary_id[edge->v2];
+
+ if (bid1 < 0 || bid2 < 0) {
+ return;
+ }
+
+ /* Retrieve boundary vertex normals and align them to direction. */
+ const ShrinkwrapBoundaryVertData *boundary_verts = tree->boundary->boundary_verts;
+ float vedge_dir[2][3], dir[3];
+
+ copy_v3_v3(vedge_dir[0], boundary_verts[bid1].normal_plane);
+ copy_v3_v3(vedge_dir[1], boundary_verts[bid2].normal_plane);
+
+ sub_v3_v3v3(dir, vedge_co[1], vedge_co[0]);
+
+ if (dot_v3v3(boundary_verts[bid1].direction, dir) < 0) {
+ negate_v3(vedge_dir[0]);
+ }
+ if (dot_v3v3(boundary_verts[bid2].direction, dir) < 0) {
+ negate_v3(vedge_dir[1]);
+ }
+
+ /* Solve a quadratic equation: lerp(d0,d1,x) * (co - lerp(v0,v1,x)) = 0 */
+ float d0v0 = dot_v3v3(vedge_dir[0], vedge_co[0]), d0v1 = dot_v3v3(vedge_dir[0], vedge_co[1]);
+ float d1v0 = dot_v3v3(vedge_dir[1], vedge_co[0]), d1v1 = dot_v3v3(vedge_dir[1], vedge_co[1]);
+ float d0co = dot_v3v3(vedge_dir[0], co);
+
+ float a = d0v1 - d0v0 + d1v0 - d1v1;
+ float b = 2 * d0v0 - d0v1 - d0co - d1v0 + dot_v3v3(vedge_dir[1], co);
+ float c = d0co - d0v0;
+ float det = b * b - 4 * a * c;
+
+ if (det >= 0) {
+ const float epsilon = 1e-6f;
+ float sdet = sqrtf(det);
+ float hit_co[3], hit_no[3];
+
+ for (int i = (det > 0 ? 2 : 0); i >= 0; i -= 2) {
+ float x = (-b + (float(i) - 1) * sdet) / (2 * a);
+
+ if (x >= -epsilon && x <= 1.0f + epsilon) {
+ CLAMP(x, 0, 1);
+
+ float vedge_no[2][3];
+ copy_v3_v3(vedge_no[0], data->vert_normals[edge->v1]);
+ copy_v3_v3(vedge_no[1], data->vert_normals[edge->v2]);
+
+ interp_v3_v3v3(hit_co, vedge_co[0], vedge_co[1], x);
+ interp_v3_v3v3(hit_no, vedge_no[0], vedge_no[1], x);
+
+ update_hit(nearest, index, co, hit_co, hit_no);
+ }
+ }
+ }
+}
+
+/* Target normal projection BVH callback - based on mesh_looptri_nearest_point. */
+static void mesh_looptri_target_project(void *userdata,
+ int index,
+ const float co[3],
+ BVHTreeNearest *nearest)
+{
+ const ShrinkwrapTreeData *tree = (ShrinkwrapTreeData *)userdata;
+ const BVHTreeFromMesh *data = &tree->treeData;
+ const MLoopTri *lt = &data->looptri[index];
+ const MLoop *loop[3] = {
+ &data->loop[lt->tri[0]], &data->loop[lt->tri[1]], &data->loop[lt->tri[2]]};
+ const MVert *vtri[3] = {
+ &data->vert[loop[0]->v], &data->vert[loop[1]->v], &data->vert[loop[2]->v]};
+ const float *vtri_co[3] = {vtri[0]->co, vtri[1]->co, vtri[2]->co};
+ float raw_hit_co[3], hit_co[3], hit_no[3], dist_sq, vtri_no[3][3];
+
+ /* First find the closest point and bail out if it's worse than the current solution. */
+ closest_on_tri_to_point_v3(raw_hit_co, co, UNPACK3(vtri_co));
+ dist_sq = len_squared_v3v3(co, raw_hit_co);
+
+#ifdef TRACE_TARGET_PROJECT
+ printf("TRIANGLE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) %g %g\n",
+ index,
+ UNPACK3(vtri_co[0]),
+ UNPACK3(vtri_co[1]),
+ UNPACK3(vtri_co[2]),
+ dist_sq,
+ nearest->dist_sq);
+#endif
+
+ if (dist_sq >= nearest->dist_sq) {
+ return;
+ }
+
+ /* Decode normals */
+ copy_v3_v3(vtri_no[0], tree->treeData.vert_normals[loop[0]->v]);
+ copy_v3_v3(vtri_no[1], tree->treeData.vert_normals[loop[1]->v]);
+ copy_v3_v3(vtri_no[2], tree->treeData.vert_normals[loop[2]->v]);
+
+ /* Solve the equations for the triangle */
+ if (target_project_solve_point_tri(vtri_co, vtri_no, co, raw_hit_co, dist_sq, hit_co, hit_no)) {
+ update_hit(nearest, index, co, hit_co, hit_no);
+ }
+ /* Boundary edges */
+ else if (tree->boundary && BLI_BITMAP_TEST(tree->boundary->looptri_has_boundary, index)) {
+ const BLI_bitmap *is_boundary = tree->boundary->edge_is_boundary;
+ int edges[3];
+
+ BKE_mesh_looptri_get_real_edges(tree->mesh, lt, edges);
+
+ for (int i = 0; i < 3; i++) {
+ if (edges[i] >= 0 && BLI_BITMAP_TEST(is_boundary, edges[i])) {
+ target_project_edge(tree, index, co, nearest, edges[i]);
+ }
+ }
+ }
+}
+
+void BKE_shrinkwrap_find_nearest_surface(ShrinkwrapTreeData *tree,
+ BVHTreeNearest *nearest,
+ float co[3],
+ int type)
+{
+ BVHTreeFromMesh *treeData = &tree->treeData;
+
+ if (type == MOD_SHRINKWRAP_TARGET_PROJECT) {
+#ifdef TRACE_TARGET_PROJECT
+ printf("\n====== TARGET PROJECT START ======\n");
+#endif
+
+ BLI_bvhtree_find_nearest_ex(
+ tree->bvh, co, nearest, mesh_looptri_target_project, tree, BVH_NEAREST_OPTIMAL_ORDER);
+
+#ifdef TRACE_TARGET_PROJECT
+ printf("====== TARGET PROJECT END: %d %g ======\n\n", nearest->index, nearest->dist_sq);
+#endif
+
+ if (nearest->index < 0) {
+ /* fallback to simple nearest */
+ BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
+ }
+ }
+ else {
+ BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
+ }
+}
+
+/**
+ * Shrink-wrap moving vertices to the nearest surface point on the target.
+ *
+ * It builds a #BVHTree from the target mesh and then performs a
+ * NN matches for each vertex
+ */
+static void shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict tls)
+{
+ ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
+
+ ShrinkwrapCalcData *calc = data->calc;
+ BVHTreeNearest *nearest = static_cast<BVHTreeNearest *>(tls->userdata_chunk);
+
+ float *co = calc->vertexCos[i];
+ float tmp_co[3];
+ float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
+
+ if (calc->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight == 0.0f) {
+ return;
+ }
+
+ /* Convert the vertex to tree coordinates */
+ if (calc->vert) {
+ copy_v3_v3(tmp_co, calc->vert[i].co);
+ }
+ else {
+ copy_v3_v3(tmp_co, co);
+ }
+ BLI_space_transform_apply(&calc->local2target, tmp_co);
+
+ /* Use local proximity heuristics (to reduce the nearest search)
+ *
+ * If we already had an hit before.. we assume this vertex is going to have a close hit to that
+ * other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
+ * This will lead in pruning of the search tree. */
+ if (nearest->index != -1) {
+ if (calc->smd->shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
+ /* Heuristic doesn't work because of additional restrictions. */
+ nearest->index = -1;
+ nearest->dist_sq = FLT_MAX;
+ }
+ else {
+ nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
+ }
+ }
+ else {
+ nearest->dist_sq = FLT_MAX;
+ }
+
+ BKE_shrinkwrap_find_nearest_surface(data->tree, nearest, tmp_co, calc->smd->shrinkType);
+
+ /* Found the nearest vertex */
+ if (nearest->index != -1) {
+ BKE_shrinkwrap_snap_point_to_surface(data->tree,
+ nullptr,
+ calc->smd->shrinkMode,
+ nearest->index,
+ nearest->co,
+ nearest->no,
+ calc->keepDist,
+ tmp_co,
+ tmp_co);
+
+ /* Convert the coordinates back to mesh coordinates */
+ BLI_space_transform_invert(&calc->local2target, tmp_co);
+ interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
+ }
+}
+
+void BKE_shrinkwrap_compute_smooth_normal(const ShrinkwrapTreeData *tree,
+ const SpaceTransform *transform,
+ int looptri_idx,
+ const float hit_co[3],
+ const float hit_no[3],
+ float r_no[3])
+{
+ const BVHTreeFromMesh *treeData = &tree->treeData;
+ const MLoopTri *tri = &treeData->looptri[looptri_idx];
+ const float(*vert_normals)[3] = tree->treeData.vert_normals;
+
+ /* Interpolate smooth normals if enabled. */
+ if ((tree->polys[tri->poly].flag & ME_SMOOTH) != 0) {
+ const uint32_t vert_indices[3] = {treeData->loop[tri->tri[0]].v,
+ treeData->loop[tri->tri[1]].v,
+ treeData->loop[tri->tri[2]].v};
+ float w[3], no[3][3], tmp_co[3];
+
+ /* Custom and auto smooth split normals. */
+ if (tree->clnors) {
+ copy_v3_v3(no[0], tree->clnors[tri->tri[0]]);
+ copy_v3_v3(no[1], tree->clnors[tri->tri[1]]);
+ copy_v3_v3(no[2], tree->clnors[tri->tri[2]]);
+ }
+ /* Ordinary vertex normals. */
+ else {
+ copy_v3_v3(no[0], vert_normals[vert_indices[0]]);
+ copy_v3_v3(no[1], vert_normals[vert_indices[1]]);
+ copy_v3_v3(no[2], vert_normals[vert_indices[2]]);
+ }
+
+ /* Barycentric weights from hit point. */
+ copy_v3_v3(tmp_co, hit_co);
+
+ if (transform) {
+ BLI_space_transform_apply(transform, tmp_co);
+ }
+
+ interp_weights_tri_v3(w,
+ treeData->vert[vert_indices[0]].co,
+ treeData->vert[vert_indices[1]].co,
+ treeData->vert[vert_indices[2]].co,
+ tmp_co);
+
+ /* Interpolate using weights. */
+ interp_v3_v3v3v3(r_no, no[0], no[1], no[2], w);
+
+ if (transform) {
+ BLI_space_transform_invert_normal(transform, r_no);
+ }
+ else {
+ normalize_v3(r_no);
+ }
+ }
+ /* Use the polygon normal if flat. */
+ else if (tree->pnors != nullptr) {
+ copy_v3_v3(r_no, tree->pnors[tri->poly]);
+ }
+ /* Finally fallback to the looptri normal. */
+ else {
+ copy_v3_v3(r_no, hit_no);
+ }
+}
+
+/* Helper for MOD_SHRINKWRAP_INSIDE, MOD_SHRINKWRAP_OUTSIDE and MOD_SHRINKWRAP_OUTSIDE_SURFACE. */
+static void shrinkwrap_snap_with_side(float r_point_co[3],
+ const float point_co[3],
+ const float hit_co[3],
+ const float hit_no[3],
+ float goal_dist,
+ float forcesign,
+ bool forcesnap)
+{
+ float delta[3];
+ sub_v3_v3v3(delta, point_co, hit_co);
+
+ float dist = len_v3(delta);
+
+ /* If exactly on the surface, push out along normal */
+ if (dist < FLT_EPSILON) {
+ if (forcesnap || goal_dist > 0) {
+ madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist * forcesign);
+ }
+ else {
+ copy_v3_v3(r_point_co, hit_co);
+ }
+ }
+ /* Move to the correct side if needed */
+ else {
+ float dsign = signf(dot_v3v3(delta, hit_no));
+
+ if (forcesign == 0.0f) {
+ forcesign = dsign;
+ }
+
+ /* If on the wrong side or too close, move to correct */
+ if (forcesnap || dsign * dist * forcesign < goal_dist) {
+ mul_v3_fl(delta, dsign / dist);
+
+ /* At very small distance, blend in the hit normal to stabilize math. */
+ float dist_epsilon = (fabsf(goal_dist) + len_manhattan_v3(hit_co)) * 1e-4f;
+
+ if (dist < dist_epsilon) {
+#ifdef TRACE_TARGET_PROJECT
+ printf("zero_factor %g = %g / %g\n", dist / dist_epsilon, dist, dist_epsilon);
+#endif
+
+ interp_v3_v3v3(delta, hit_no, delta, dist / dist_epsilon);
+ }
+
+ madd_v3_v3v3fl(r_point_co, hit_co, delta, goal_dist * forcesign);
+ }
+ else {
+ copy_v3_v3(r_point_co, point_co);
+ }
+ }
+}
+
+void BKE_shrinkwrap_snap_point_to_surface(const ShrinkwrapTreeData *tree,
+ const SpaceTransform *transform,
+ int mode,
+ int hit_idx,
+ const float hit_co[3],
+ const float hit_no[3],
+ float goal_dist,
+ const float point_co[3],
+ float r_point_co[3])
+{
+ float tmp[3];
+
+ switch (mode) {
+ /* Offsets along the line between point_co and hit_co. */
+ case MOD_SHRINKWRAP_ON_SURFACE:
+ if (goal_dist != 0) {
+ shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, 0, true);
+ }
+ else {
+ copy_v3_v3(r_point_co, hit_co);
+ }
+ break;
+
+ case MOD_SHRINKWRAP_INSIDE:
+ shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, -1, false);
+ break;
+
+ case MOD_SHRINKWRAP_OUTSIDE:
+ shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, false);
+ break;
+
+ case MOD_SHRINKWRAP_OUTSIDE_SURFACE:
+ if (goal_dist != 0) {
+ shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, true);
+ }
+ else {
+ copy_v3_v3(r_point_co, hit_co);
+ }
+ break;
+
+ /* Offsets along the normal */
+ case MOD_SHRINKWRAP_ABOVE_SURFACE:
+ if (goal_dist != 0) {
+ BKE_shrinkwrap_compute_smooth_normal(tree, transform, hit_idx, hit_co, hit_no, tmp);
+ madd_v3_v3v3fl(r_point_co, hit_co, tmp, goal_dist);
+ }
+ else {
+ copy_v3_v3(r_point_co, hit_co);
+ }
+ break;
+
+ default:
+ printf("Unknown Shrinkwrap surface snap mode: %d\n", mode);
+ copy_v3_v3(r_point_co, hit_co);
+ }
+}
+
+static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
+{
+ BVHTreeNearest nearest = NULL_BVHTreeNearest;
+
+ /* Setup nearest */
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+
+ /* Find the nearest vertex */
+ ShrinkwrapCalcCBData data{};
+ data.calc = calc;
+ data.tree = calc->tree;
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
+ settings.userdata_chunk = &nearest;
+ settings.userdata_chunk_size = sizeof(nearest);
+ BLI_task_parallel_range(
+ 0, calc->numVerts, &data, shrinkwrap_calc_nearest_surface_point_cb_ex, &settings);
+}
+
+void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd,
+ const ModifierEvalContext *ctx,
+ Scene *scene,
+ Object *ob,
+ Mesh *mesh,
+ const MDeformVert *dvert,
+ const int defgrp_index,
+ float (*vertexCos)[3],
+ int numVerts)
+{
+
+ DerivedMesh *ss_mesh = nullptr;
+ ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;
+
+ /* remove loop dependencies on derived meshes (TODO should this be done elsewhere?) */
+ if (smd->target == ob) {
+ smd->target = nullptr;
+ }
+ if (smd->auxTarget == ob) {
+ smd->auxTarget = nullptr;
+ }
+
+ /* Configure Shrinkwrap calc data */
+ calc.smd = smd;
+ calc.ob = ob;
+ calc.numVerts = numVerts;
+ calc.vertexCos = vertexCos;
+ calc.dvert = dvert;
+ calc.vgroup = defgrp_index;
+ calc.invert_vgroup = (smd->shrinkOpts & MOD_SHRINKWRAP_INVERT_VGROUP) != 0;
+
+ if (smd->target != nullptr) {
+ Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target);
+ calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
+
+ /* TODO: there might be several "bugs" with non-uniform scales matrices
+ * because it will no longer be nearest surface, not sphere projection
+ * because space has been deformed */
+ BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob, ob_target);
+
+ /* TODO: smd->keepDist is in global units.. must change to local */
+ calc.keepDist = smd->keepDist;
+ }
+ calc.aux_target = DEG_get_evaluated_object(ctx->depsgraph, smd->auxTarget);
+
+ if (mesh != nullptr && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) {
+ /* Setup arrays to get vertices position, normals and deform weights. */
+ calc.vert = BKE_mesh_verts_for_write(mesh);
+ calc.vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
+
+ /* Using vertices positions/normals as if a subsurface was applied */
+ if (smd->subsurfLevels) {
+ SubsurfModifierData ssmd = {{nullptr}};
+ ssmd.subdivType = ME_CC_SUBSURF; /* catmull clark */
+ ssmd.levels = smd->subsurfLevels; /* levels */
+
+ /* TODO: to be moved to Mesh once we are done with changes in subsurf code. */
+ DerivedMesh *dm = CDDM_from_mesh(mesh);
+
+ ss_mesh = subsurf_make_derived_from_derived(
+ dm,
+ &ssmd,
+ scene,
+ nullptr,
+ (ob->mode & OB_MODE_EDIT) ? SUBSURF_IN_EDIT_MODE : SubsurfFlags(0));
+
+ if (ss_mesh) {
+ calc.vert = static_cast<MVert *>(ss_mesh->getVertDataArray(ss_mesh, CD_MVERT));
+ if (calc.vert) {
+ /* TRICKY: this code assumes subsurface will have the transformed original vertices
+ * in their original order at the end of the vert array. */
+ calc.vert = calc.vert + ss_mesh->getNumVerts(ss_mesh) - dm->getNumVerts(dm);
+ }
+ }
+
+ /* Just to make sure we are not leaving any memory behind */
+ BLI_assert(ssmd.emCache == nullptr);
+ BLI_assert(ssmd.mCache == nullptr);
+
+ dm->release(dm);
+ }
+ }
+
+ /* Projecting target defined - lets work! */
+ ShrinkwrapTreeData tree;
+
+ if (BKE_shrinkwrap_init_tree(&tree, calc.target, smd->shrinkType, smd->shrinkMode, false)) {
+ calc.tree = &tree;
+
+ switch (smd->shrinkType) {
+ case MOD_SHRINKWRAP_NEAREST_SURFACE:
+ case MOD_SHRINKWRAP_TARGET_PROJECT:
+ TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), deform_surface);
+ break;
+
+ case MOD_SHRINKWRAP_PROJECT:
+ TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
+ break;
+
+ case MOD_SHRINKWRAP_NEAREST_VERTEX:
+ TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), deform_vertex);
+ break;
+ }
+
+ BKE_shrinkwrap_free_tree(&tree);
+ }
+
+ /* free memory */
+ if (ss_mesh) {
+ ss_mesh->release(ss_mesh);
+ }
+}
+
+void shrinkwrapGpencilModifier_deform(ShrinkwrapGpencilModifierData *mmd,
+ Object *ob,
+ MDeformVert *dvert,
+ const int defgrp_index,
+ float (*vertexCos)[3],
+ int numVerts)
+{
+
+ ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;
+ /* Convert gpencil struct to use the same struct and function used with meshes. */
+ ShrinkwrapModifierData smd;
+ smd.target = mmd->target;
+ smd.auxTarget = mmd->aux_target;
+ smd.keepDist = mmd->keep_dist;
+ smd.shrinkType = mmd->shrink_type;
+ smd.shrinkOpts = mmd->shrink_opts;
+ smd.shrinkMode = mmd->shrink_mode;
+ smd.projLimit = mmd->proj_limit;
+ smd.projAxis = mmd->proj_axis;
+
+ /* Configure Shrinkwrap calc data. */
+ calc.smd = &smd;
+ calc.ob = ob;
+ calc.numVerts = numVerts;
+ calc.vertexCos = vertexCos;
+ calc.dvert = dvert;
+ calc.vgroup = defgrp_index;
+ calc.invert_vgroup = (mmd->flag & GP_SHRINKWRAP_INVERT_VGROUP) != 0;
+
+ BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob, mmd->target);
+ calc.keepDist = mmd->keep_dist;
+ calc.tree = mmd->cache_data;
+
+ switch (mmd->shrink_type) {
+ case MOD_SHRINKWRAP_NEAREST_SURFACE:
+ case MOD_SHRINKWRAP_TARGET_PROJECT:
+ TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), gpdeform_surface);
+ break;
+
+ case MOD_SHRINKWRAP_PROJECT:
+ TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), gpdeform_project);
+ break;
+
+ case MOD_SHRINKWRAP_NEAREST_VERTEX:
+ TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), gpdeform_vertex);
+ break;
+ }
+}
+
+void BKE_shrinkwrap_mesh_nearest_surface_deform(bContext *C, Object *ob_source, Object *ob_target)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Scene *sce = CTX_data_scene(C);
+ ShrinkwrapModifierData ssmd = {{0}};
+ ModifierEvalContext ctx = {depsgraph, ob_source, ModifierApplyFlag(0)};
+ int totvert;
+
+ ssmd.target = ob_target;
+ ssmd.shrinkType = MOD_SHRINKWRAP_NEAREST_SURFACE;
+ ssmd.shrinkMode = MOD_SHRINKWRAP_ON_SURFACE;
+ ssmd.keepDist = 0.0f;
+
+ Mesh *src_me = static_cast<Mesh *>(ob_source->data);
+ float(*vertexCos)[3] = BKE_mesh_vert_coords_alloc(src_me, &totvert);
+
+ shrinkwrapModifier_deform(&ssmd, &ctx, sce, ob_source, src_me, nullptr, -1, vertexCos, totvert);
+
+ BKE_mesh_vert_coords_apply(src_me, vertexCos);
+
+ MEM_freeN(vertexCos);
+}
+
+void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
+{
+ ShrinkwrapModifierData ssmd = {{0}};
+ int totvert;
+
+ ssmd.target = ob_target;
+ ssmd.shrinkType = MOD_SHRINKWRAP_PROJECT;
+ ssmd.shrinkMode = MOD_SHRINKWRAP_ON_SURFACE;
+ ssmd.shrinkOpts = MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR;
+ ssmd.keepDist = 0.0f;
+
+ /* Tolerance value to prevent artifacts on sharp edges of a mesh.
+ * This constant and based on experimenting with different values. */
+ const float projLimitTolerance = 5.0f;
+ ssmd.projLimit = target_me->remesh_voxel_size * projLimitTolerance;
+
+ float(*vertexCos)[3] = BKE_mesh_vert_coords_alloc(src_me, &totvert);
+
+ ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;
+
+ calc.smd = &ssmd;
+ calc.numVerts = src_me->totvert;
+ calc.vertexCos = vertexCos;
+ calc.vert_normals = BKE_mesh_vertex_normals_ensure(src_me);
+ calc.vgroup = -1;
+ calc.target = target_me;
+ calc.keepDist = ssmd.keepDist;
+ calc.vert = BKE_mesh_verts_for_write(src_me);
+ BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob_target, ob_target);
+
+ ShrinkwrapTreeData tree;
+ if (BKE_shrinkwrap_init_tree(&tree, calc.target, ssmd.shrinkType, ssmd.shrinkMode, false)) {
+ calc.tree = &tree;
+ TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
+ BKE_shrinkwrap_free_tree(&tree);
+ }
+
+ BKE_mesh_vert_coords_apply(src_me, vertexCos);
+
+ MEM_freeN(vertexCos);
+}