Blender V5.0
shrinkwrap.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cfloat>
11#include <cmath>
12#include <cstdio>
13#include <cstring>
14#include <ctime>
15#include <memory.h>
16
18#include "DNA_mesh_types.h"
19#include "DNA_modifier_types.h"
20#include "DNA_object_types.h"
21
22#include "BLI_math_geom.h"
23#include "BLI_math_matrix.h"
24#include "BLI_math_solvers.h"
25#include "BLI_math_vector.h"
26#include "BLI_task.h"
27#include "BLI_utildefines.h"
28
29#include "BKE_attribute.hh"
30#include "BKE_modifier.hh"
31#include "BKE_shrinkwrap.hh"
32#include "BKE_subdiv.hh"
33
34#include "BKE_deform.hh"
35#include "BKE_editmesh.hh"
36#include "BKE_mesh.hh" /* for OMP limits. */
37#include "BKE_mesh_wrapper.hh"
38#include "BKE_subdiv.hh"
39#include "BKE_subdiv_deform.hh"
40
42
43#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
44
45/* for timing... */
46#if 0
47# include "BLI_time_utildefines.h"
48#else
49# define TIMEIT_BENCH(expr, id) (expr)
50#endif
51
52/* Util macros */
53#define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n"))
54
56 ShrinkwrapModifierData *smd; /* shrinkwrap modifier data */
57
58 Object *ob; /* object we are applying shrinkwrap to */
59
60 float (*vert_positions)[3]; /* Array of verts being projected. */
62 /* Vertices being shrink-wrapped. */
65
66 const MDeformVert *dvert; /* Pointer to mdeform array */
67 int vgroup; /* Vertex group num */
68 bool invert_vgroup; /* invert vertex group influence */
69
70 Mesh *target; /* mesh we are shrinking to */
71 SpaceTransform local2target; /* transform to move between local and target space */
72 ShrinkwrapTreeData *tree; /* mesh BVH tree data */
73
75
76 float keepDist; /* Distance to keep above target surface (units are in local space) */
77};
78
88
89bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode)
90{
91 return (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) ||
92 (shrinkType != MOD_SHRINKWRAP_NEAREST_VERTEX &&
93 shrinkMode == MOD_SHRINKWRAP_ABOVE_SURFACE);
94}
95
97 ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals)
98{
99 using namespace blender::bke;
100 *data = {};
101
102 if (mesh == nullptr) {
103 return false;
104 }
105
106 /* We could create a BVH tree from the edit mesh,
107 * however accessing normals from the face/loop normals gets more involved.
108 * Convert mesh data since this isn't typically used in edit-mode. */
110
111 if (mesh->verts_num <= 0) {
112 return false;
113 }
114
115 data->mesh = mesh;
116 data->edges = mesh->edges();
117 data->faces = mesh->faces();
118 data->corner_edges = mesh->corner_edges();
119 data->vert_normals = mesh->vert_normals();
120 const AttributeAccessor attributes = mesh->attributes();
121 data->sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
122
123 if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
124 data->treeData = mesh->bvh_verts();
125 data->bvh = data->treeData.tree;
126 return data->bvh != nullptr;
127 }
128
129 if (mesh->faces_num <= 0) {
130 return false;
131 }
132
133 data->treeData = mesh->bvh_corner_tris();
134 data->bvh = data->treeData.tree;
135
136 if (data->bvh == nullptr) {
137 return false;
138 }
139
140 if (force_normals || BKE_shrinkwrap_needs_normals(shrinkType, shrinkMode)) {
141 data->face_normals = mesh->face_normals();
142 if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
143 data->corner_normals = mesh->corner_normals();
144 }
145 }
146
147 if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
149 }
150
151 return true;
152}
153
155
156namespace blender::bke::shrinkwrap {
157
158/* Accumulate edge for average boundary edge direction. */
161 int index,
162 const float edge_dir[3],
163 signed char side)
164{
165 BLI_assert(index >= 0);
166 float *direction = vdata[index].direction;
167
168 /* Invert the direction vector if either:
169 * - This is the second edge and both edges have the vertex as start or end.
170 * - For third and above, if it points in the wrong direction.
171 */
172 if (status[index] >= 0 ? status[index] == side : dot_v3v3(direction, edge_dir) < 0) {
173 sub_v3_v3(direction, edge_dir);
174 }
175 else {
176 add_v3_v3(direction, edge_dir);
177 }
178
179 status[index] = (status[index] == 0) ? side : -1;
180}
181
183{
184 const Span<float3> positions = mesh.vert_positions();
185 const Span<int2> edges = mesh.edges();
186 const Span<int> corner_verts = mesh.corner_verts();
187 const Span<int> corner_edges = mesh.corner_edges();
188
189 /* Count faces per edge (up to 2). */
190 Array<int8_t> edge_mode(edges.size(), 0);
191
192 for (const int edge : corner_edges) {
193 if (edge_mode[edge] < 2) {
194 edge_mode[edge]++;
195 }
196 }
197
198 /* Build the boundary edge bitmask. */
199 BitVector<> edge_is_boundary(mesh.edges_num, false);
200
201 int num_boundary_edges = 0;
202 for (const int64_t i : edges.index_range()) {
203 if (edge_mode[i] == 1) {
204 edge_is_boundary[i].set();
205 num_boundary_edges++;
206 }
207 }
208
209 /* If no boundary, return nullptr. */
210 if (num_boundary_edges == 0) {
211 return {};
212 }
213
214 /* Allocate the data object. */
216
217 /* Build the boundary corner_tris bit-mask. */
218 const Span<int3> corner_tris = mesh.corner_tris();
219
220 BitVector<> tri_has_boundary(corner_tris.size(), false);
221
222 for (const int64_t i : corner_tris.index_range()) {
224 edges, corner_verts, corner_edges, corner_tris[i]);
225
226 for (int j = 0; j < 3; j++) {
227 if (real_edges[j] >= 0 && edge_is_boundary[real_edges[j]]) {
228 tri_has_boundary[i].set();
229 break;
230 }
231 }
232 }
233
234 /* Find boundary vertices and build a mapping table for compact storage of data. */
235 Array<int> vert_boundary_id(mesh.verts_num, 0);
236
237 for (const int64_t i : edges.index_range()) {
238 if (edge_is_boundary[i]) {
239 const int2 &edge = edges[i];
240 vert_boundary_id[edge[0]] = 1;
241 vert_boundary_id[edge[1]] = 1;
242 }
243 }
244
245 int boundary_verts_num = 0;
246 for (const int64_t i : positions.index_range()) {
247 vert_boundary_id[i] = (vert_boundary_id[i] != 0) ? boundary_verts_num++ : -1;
248 }
249
250 /* Compute average directions. */
251 Array<ShrinkwrapBoundaryVertData> boundary_verts(boundary_verts_num);
252
253 Array<int8_t> vert_status(boundary_verts_num);
254 for (const int64_t i : edges.index_range()) {
255 if (edge_is_boundary[i]) {
256 const int2 &edge = edges[i];
257
258 float dir[3];
259 sub_v3_v3v3(dir, positions[edge[1]], positions[edge[0]]);
260 normalize_v3(dir);
261
262 merge_vert_dir(boundary_verts.data(), vert_status, vert_boundary_id[edge[0]], dir, 1);
263 merge_vert_dir(boundary_verts.data(), vert_status, vert_boundary_id[edge[1]], dir, 2);
264 }
265 }
266
267 /* Finalize average direction and compute normal. */
268 const Span<float3> vert_normals = mesh.vert_normals();
269 for (const int64_t i : positions.index_range()) {
270 int bidx = vert_boundary_id[i];
271
272 if (bidx >= 0) {
273 ShrinkwrapBoundaryVertData *vdata = &boundary_verts[bidx];
274 float tmp[3];
275
276 normalize_v3(vdata->direction);
277
278 cross_v3_v3v3(tmp, vert_normals[i], vdata->direction);
279 cross_v3_v3v3(vdata->normal_plane, tmp, vert_normals[i]);
281 }
282 }
283
284 data.edge_is_boundary = std::move(edge_is_boundary);
285 data.tri_has_boundary = std::move(tri_has_boundary);
286 data.vert_boundary_id = std::move(vert_boundary_id);
287 data.boundary_verts = std::move(boundary_verts);
288
289 return data;
290}
291
293{
294 mesh.runtime->shrinkwrap_boundary_cache.ensure(
296 return mesh.runtime->shrinkwrap_boundary_cache.data();
297}
298
299} // namespace blender::bke::shrinkwrap
300
307static void shrinkwrap_calc_nearest_vertex_cb_ex(void *__restrict userdata,
308 const int i,
309 const TaskParallelTLS *__restrict tls)
310{
311 ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
312
313 ShrinkwrapCalcData *calc = data->calc;
314 blender::bke::BVHTreeFromMesh *treeData = &data->tree->treeData;
315 BVHTreeNearest *nearest = static_cast<BVHTreeNearest *>(tls->userdata_chunk);
316
317 float *co = calc->vertexCos[i];
318 float tmp_co[3];
320 calc->dvert, i, calc->vgroup, calc->invert_vgroup);
321
322 if (weight == 0.0f) {
323 return;
324 }
325
326 /* Convert the vertex to tree coordinates */
327 if (calc->vert_positions) {
328 copy_v3_v3(tmp_co, calc->vert_positions[i]);
329 }
330 else {
331 copy_v3_v3(tmp_co, co);
332 }
334
335 /* Use local proximity heuristics (to reduce the nearest search)
336 *
337 * If we already had an hit before.. we assume this vertex is going to have a close hit to that
338 * other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
339 * This will lead in pruning of the search tree. */
340 if (nearest->index != -1) {
341 nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
342 }
343 else {
344 nearest->dist_sq = FLT_MAX;
345 }
346
347 BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData);
348
349 /* Found the nearest vertex */
350 if (nearest->index != -1) {
351 /* Adjusting the vertex weight,
352 * so that after interpolating it keeps a certain distance from the nearest position */
353 if (nearest->dist_sq > FLT_EPSILON) {
354 const float dist = sqrtf(nearest->dist_sq);
355 weight *= (dist - calc->keepDist) / dist;
356 }
357
358 /* Convert the coordinates back to mesh coordinates */
359 copy_v3_v3(tmp_co, nearest->co);
361
362 interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
363 }
364}
365
367{
369
370 /* Setup nearest */
371 nearest.index = -1;
372 nearest.dist_sq = FLT_MAX;
373
375 data.calc = calc;
376 data.tree = calc->tree;
377 TaskParallelSettings settings;
379 settings.use_threading = (calc->numVerts > 10000);
380 settings.userdata_chunk = &nearest;
381 settings.userdata_chunk_size = sizeof(nearest);
383 0, calc->numVerts, &data, shrinkwrap_calc_nearest_vertex_cb_ex, &settings);
384}
385
387 const float vert[3],
388 const float dir[3],
389 const float ray_radius,
390 const SpaceTransform *transf,
392 BVHTreeRayHit *hit)
393{
394 /* don't use this because this dist value could be incompatible
395 * this value used by the callback for comparing previous/new dist values.
396 * also, at the moment there is no need to have a corrected 'dist' value */
397 // #define USE_DIST_CORRECT
398
399 float tmp_co[3], tmp_no[3];
400 const float *co, *no;
401 BVHTreeRayHit hit_tmp;
402
403 /* Copy from hit (we need to convert hit rays from one space coordinates to the other */
404 memcpy(&hit_tmp, hit, sizeof(hit_tmp));
405
406 /* Apply space transform (TODO readjust dist) */
407 if (transf) {
408 copy_v3_v3(tmp_co, vert);
409 BLI_space_transform_apply(transf, tmp_co);
410 co = tmp_co;
411
412 copy_v3_v3(tmp_no, dir);
413 BLI_space_transform_apply_normal(transf, tmp_no);
414 no = tmp_no;
415
416#ifdef USE_DIST_CORRECT
417 hit_tmp.dist *= mat4_to_scale(((SpaceTransform *)transf)->local2target);
418#endif
419 }
420 else {
421 co = vert;
422 no = dir;
423 }
424
425 hit_tmp.index = -1;
426
428 tree->bvh, co, no, ray_radius, &hit_tmp, tree->treeData.raycast_callback, &tree->treeData);
429
430 if (hit_tmp.index != -1) {
431 /* invert the normal first so face culling works on rotated objects */
432 if (transf) {
433 BLI_space_transform_invert_normal(transf, hit_tmp.no);
434 }
435
437 /* Apply back-face. */
438 const float dot = dot_v3v3(dir, hit_tmp.no);
439 if (((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) ||
441 {
442 return false; /* Ignore hit */
443 }
444 }
445
446 if (transf) {
447 /* Inverting space transform (TODO: make coherent with the initial dist readjust). */
448 BLI_space_transform_invert(transf, hit_tmp.co);
449#ifdef USE_DIST_CORRECT
450 hit_tmp.dist = len_v3v3(vert, hit_tmp.co);
451#endif
452 }
453
454 BLI_assert(hit_tmp.dist <= hit->dist);
455
456 memcpy(hit, &hit_tmp, sizeof(hit_tmp));
457 return true;
458 }
459 return false;
460}
461
462static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
463 const int i,
464 const TaskParallelTLS *__restrict tls)
465{
466 ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
467
468 ShrinkwrapCalcData *calc = data->calc;
470 ShrinkwrapTreeData *aux_tree = data->aux_tree;
471
472 float *proj_axis = data->proj_axis;
473 SpaceTransform *local2aux = data->local2aux;
474
475 BVHTreeRayHit *hit = static_cast<BVHTreeRayHit *>(tls->userdata_chunk);
476
477 const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit;
478 float *co = calc->vertexCos[i];
479 const float *tmp_co, *tmp_no;
481 calc->dvert, i, calc->vgroup, calc->invert_vgroup);
482
483 if (weight == 0.0f) {
484 return;
485 }
486
487 if (calc->vert_positions != nullptr && calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL)
488 {
489 /* calc->vert_positions contains verts from evaluated mesh. */
490 /* These coordinates are deformed by vertexCos only for normal projection
491 * (to get correct normals) for other cases calc->verts contains undeformed coordinates and
492 * vertexCos should be used */
493 tmp_co = calc->vert_positions[i];
494 tmp_no = calc->vert_normals[i];
495 }
496 else {
497 tmp_co = co;
498 tmp_no = proj_axis;
499 }
500
501 hit->index = -1;
502
503 /* TODO: we should use FLT_MAX here, but sweep-sphere code isn't prepared for that. */
505
506 bool is_aux = false;
507
508 /* Project over positive direction of axis. */
510 if (aux_tree) {
511 if (BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, 0.0, local2aux, aux_tree, hit)) {
512 is_aux = true;
513 }
514 }
515
517 calc->smd->shrinkOpts, tmp_co, tmp_no, 0.0, &calc->local2target, tree, hit))
518 {
519 is_aux = false;
520 }
521 }
522
523 /* Project over negative direction of axis */
525 float inv_no[3];
526 negate_v3_v3(inv_no, tmp_no);
527
528 char options = calc->smd->shrinkOpts;
529
532 {
534 }
535
536 if (aux_tree) {
537 if (BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, 0.0, local2aux, aux_tree, hit)) {
538 is_aux = true;
539 }
540 }
541
543 options, tmp_co, inv_no, 0.0, &calc->local2target, tree, hit))
544 {
545 is_aux = false;
546 }
547 }
548
549 /* don't set the initial dist (which is more efficient),
550 * because its calculated in the targets space, we want the dist in our own space */
551 if (proj_limit_squared != 0.0f) {
552 if (hit->index != -1 && len_squared_v3v3(hit->co, co) > proj_limit_squared) {
553 hit->index = -1;
554 }
555 }
556
557 if (hit->index != -1) {
558 if (is_aux) {
560 local2aux,
561 calc->smd->shrinkMode,
562 hit->index,
563 hit->co,
564 hit->no,
565 calc->keepDist,
566 tmp_co,
567 hit->co);
568 }
569 else {
571 &calc->local2target,
572 calc->smd->shrinkMode,
573 hit->index,
574 hit->co,
575 hit->no,
576 calc->keepDist,
577 tmp_co,
578 hit->co);
579 }
580
581 interp_v3_v3v3(co, co, hit->co, weight);
582 }
583}
584
586{
587 /* Options about projection direction */
588 float proj_axis[3] = {0.0f, 0.0f, 0.0f};
589
590 /* Ray-cast and tree stuff. */
591
595 BVHTreeRayHit hit;
596
597 /* auxiliary target */
598 Mesh *auxMesh = nullptr;
599 ShrinkwrapTreeData *aux_tree = nullptr;
600 ShrinkwrapTreeData aux_tree_stack;
601 SpaceTransform local2aux;
602
603 /* If the user doesn't allows to project in any direction of projection axis
604 * then there's nothing todo. */
605 if ((calc->smd->shrinkOpts &
607 {
608 return;
609 }
610
611 /* Prepare data to retrieve the direction in which we should project each vertex */
613 if (calc->vert_positions == nullptr) {
614 return;
615 }
616 }
617 else {
618 /* The code supports any axis that is a combination of X,Y,Z
619 * although currently UI only allows to set the 3 different axis */
621 proj_axis[0] = 1.0f;
622 }
624 proj_axis[1] = 1.0f;
625 }
627 proj_axis[2] = 1.0f;
628 }
629
630 normalize_v3(proj_axis);
631
632 /* Invalid projection direction */
633 if (len_squared_v3(proj_axis) < FLT_EPSILON) {
634 return;
635 }
636 }
637
638 if (calc->aux_target) {
640 if (!auxMesh) {
641 return;
642 }
643 BLI_SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->aux_target);
644 }
645
647 &aux_tree_stack, auxMesh, calc->smd->shrinkType, calc->smd->shrinkMode, false))
648 {
649 aux_tree = &aux_tree_stack;
650 }
651
652 /* After successfully build the trees, start projection vertices. */
654 data.calc = calc;
655 data.tree = calc->tree;
656 data.aux_tree = aux_tree;
657 data.proj_axis = proj_axis;
658 data.local2aux = &local2aux;
659 TaskParallelSettings settings;
661 settings.use_threading = (calc->numVerts > 10000);
662 settings.userdata_chunk = &hit;
663 settings.userdata_chunk_size = sizeof(hit);
666
667 /* free data structures */
668 if (aux_tree) {
669 BKE_shrinkwrap_free_tree(aux_tree);
670 }
671}
672
673/*
674 * Shrinkwrap Target Surface Project mode
675 *
676 * It uses Newton's method to find a surface location with its
677 * smooth normal pointing at the original point.
678 *
679 * The equation system on barycentric weights and normal multiplier:
680 *
681 * (w0*V0 + w1*V1 + w2*V2) + l * (w0*N0 + w1*N1 + w2*N2) - CO = 0
682 * w0 + w1 + w2 = 1
683 *
684 * The actual solution vector is [ w0, w1, l ], with w2 eliminated.
685 */
686
687// #define TRACE_TARGET_PROJECT
688
690 const float **vtri_co;
691 const float (*vtri_no)[3];
692 const float *point_co;
693
696
697 /* Current interpolated position and normal. */
698 float co_interp[3], no_interp[3];
699};
700
701/* Computes the deviation of the equation system from goal. */
702static void target_project_tri_deviation(void *userdata, const float x[3], float r_delta[3])
703{
704 TargetProjectTriData *data = static_cast<TargetProjectTriData *>(userdata);
705
706 const float w[3] = {x[0], x[1], 1.0f - x[0] - x[1]};
707 interp_v3_v3v3v3(data->co_interp, data->vtri_co[0], data->vtri_co[1], data->vtri_co[2], w);
708 interp_v3_v3v3v3(data->no_interp, data->vtri_no[0], data->vtri_no[1], data->vtri_no[2], w);
709
710 madd_v3_v3v3fl(r_delta, data->co_interp, data->no_interp, x[2]);
711 sub_v3_v3(r_delta, data->point_co);
712}
713
714/* Computes the Jacobian matrix of the equation system. */
715static void target_project_tri_jacobian(void *userdata, const float x[3], float r_jacobian[3][3])
716{
717 TargetProjectTriData *data = static_cast<TargetProjectTriData *>(userdata);
718
719 madd_v3_v3v3fl(r_jacobian[0], data->c0_minus_c2, data->n0_minus_n2, x[2]);
720 madd_v3_v3v3fl(r_jacobian[1], data->c1_minus_c2, data->n1_minus_n2, x[2]);
721
722 copy_v3_v3(r_jacobian[2], data->vtri_no[2]);
723 madd_v3_v3fl(r_jacobian[2], data->n0_minus_n2, x[0]);
724 madd_v3_v3fl(r_jacobian[2], data->n1_minus_n2, x[1]);
725}
726
727/* Clamp barycentric weights to the triangle. */
728static void target_project_tri_clamp(float x[3])
729{
730 x[0] = std::max(x[0], 0.0f);
731 x[1] = std::max(x[1], 0.0f);
732 if (x[0] + x[1] > 1.0f) {
733 x[0] = x[0] / (x[0] + x[1]);
734 x[1] = 1.0f - x[0];
735 }
736}
737
738/* Correct the Newton's method step to keep the coordinates within the triangle. */
739static bool target_project_tri_correct(void * /*userdata*/,
740 const float x[3],
741 float step[3],
742 float x_next[3])
743{
744 /* Insignificant correction threshold */
745 const float epsilon = 1e-5f;
746 /* Dot product threshold for checking if step is 'clearly' pointing outside. */
747 const float dir_epsilon = 0.5f;
748 bool fixed = false, locked = false;
749
750 /* The barycentric coordinate domain is a triangle bounded by
751 * the X and Y axes, plus the x+y=1 diagonal. First, clamp the
752 * movement against the diagonal. Note that step is subtracted. */
753 float sum = x[0] + x[1];
754 float sstep = -(step[0] + step[1]);
755
756 if (sum + sstep > 1.0f) {
757 float ldist = 1.0f - sum;
758
759 /* If already at the boundary, slide along it. */
760 if (ldist < epsilon * float(M_SQRT2)) {
761 float step_len = len_v2(step);
762
763 /* Abort if the solution is clearly outside the domain. */
764 if (step_len > epsilon && sstep > step_len * dir_epsilon * float(M_SQRT2)) {
765 return false;
766 }
767
768 /* Project the new position onto the diagonal. */
769 add_v2_fl(step, (sum + sstep - 1.0f) * 0.5f);
770 fixed = locked = true;
771 }
772 else {
773 /* Scale a significant step down to arrive at the boundary. */
774 mul_v3_fl(step, ldist / sstep);
775 fixed = true;
776 }
777 }
778
779 /* Weight 0 and 1 boundary checks - along axis. */
780 for (int i = 0; i < 2; i++) {
781 if (step[i] > x[i]) {
782 /* If already at the boundary, slide along it. */
783 if (x[i] < epsilon) {
784 float step_len = len_v2(step);
785
786 /* Abort if the solution is clearly outside the domain. */
787 if (step_len > epsilon && (locked || step[i] > step_len * dir_epsilon)) {
788 return false;
789 }
790
791 /* Reset precision errors to stay at the boundary. */
792 step[i] = x[i];
793 fixed = true;
794 }
795 else {
796 /* Scale a significant step down to arrive at the boundary. */
797 mul_v3_fl(step, x[i] / step[i]);
798 fixed = true;
799 }
800 }
801 }
802
803 /* Recompute and clamp the new coordinates after step correction. */
804 if (fixed) {
805 sub_v3_v3v3(x_next, x, step);
807 }
808
809 return true;
810}
811
812static bool target_project_solve_point_tri(const float *vtri_co[3],
813 const float vtri_no[3][3],
814 const float point_co[3],
815 const float hit_co[3],
816 float hit_dist_sq,
817 float r_hit_co[3],
818 float r_hit_no[3])
819{
820 float x[3], tmp[3];
821 float dist = sqrtf(hit_dist_sq);
822 float magnitude_estimate = dist + len_manhattan_v3(vtri_co[0]) + len_manhattan_v3(vtri_co[1]) +
823 len_manhattan_v3(vtri_co[2]);
824 float epsilon = magnitude_estimate * 1.0e-6f;
825
826 /* Initial solution vector: barycentric weights plus distance along normal. */
827 interp_weights_tri_v3(x, UNPACK3(vtri_co), hit_co);
828
829 interp_v3_v3v3v3(r_hit_no, UNPACK3(vtri_no), x);
830 sub_v3_v3v3(tmp, point_co, hit_co);
831
832 x[2] = (dot_v3v3(tmp, r_hit_no) < 0) ? -dist : dist;
833
834 /* Solve the equations iteratively. */
835 TargetProjectTriData tri_data{};
836 tri_data.vtri_co = vtri_co;
837 tri_data.vtri_no = vtri_no;
838 tri_data.point_co = point_co;
839
840 sub_v3_v3v3(tri_data.n0_minus_n2, vtri_no[0], vtri_no[2]);
841 sub_v3_v3v3(tri_data.n1_minus_n2, vtri_no[1], vtri_no[2]);
842 sub_v3_v3v3(tri_data.c0_minus_c2, vtri_co[0], vtri_co[2]);
843 sub_v3_v3v3(tri_data.c1_minus_c2, vtri_co[1], vtri_co[2]);
844
846
847#ifdef TRACE_TARGET_PROJECT
848 const bool trace = true;
849#else
850 const bool trace = false;
851#endif
852
856 &tri_data,
857 epsilon,
858 20,
859 trace,
860 x,
861 x);
862
863 if (ok) {
864 copy_v3_v3(r_hit_co, tri_data.co_interp);
865 copy_v3_v3(r_hit_no, tri_data.no_interp);
866
867 return true;
868 }
869
870 return false;
871}
872
873static bool update_hit(BVHTreeNearest *nearest,
874 int index,
875 const float co[3],
876 const float hit_co[3],
877 const float hit_no[3])
878{
879 float dist_sq = len_squared_v3v3(hit_co, co);
880
881 if (dist_sq < nearest->dist_sq) {
882#ifdef TRACE_TARGET_PROJECT
883 printf(
884 "#=#=#> %d (%.3f,%.3f,%.3f) %g < %g\n", index, UNPACK3(hit_co), dist_sq, nearest->dist_sq);
885#endif
886 nearest->index = index;
887 nearest->dist_sq = dist_sq;
888 copy_v3_v3(nearest->co, hit_co);
889 normalize_v3_v3(nearest->no, hit_no);
890 return true;
891 }
892
893 return false;
894}
895
896/* Target projection on a non-manifold boundary edge -
897 * treats it like an infinitely thin cylinder. */
899 int index,
900 const float co[3],
901 BVHTreeNearest *nearest,
902 int eidx)
903{
904 const blender::bke::BVHTreeFromMesh *data = &tree->treeData;
905 const blender::int2 &edge = tree->edges[eidx];
906 const float *vedge_co[2] = {data->vert_positions[edge[0]], data->vert_positions[edge[1]]};
907
908#ifdef TRACE_TARGET_PROJECT
909 printf("EDGE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f)\n",
910 eidx,
911 UNPACK3(vedge_co[0]),
912 UNPACK3(vedge_co[1]));
913#endif
914
915 /* Retrieve boundary vertex IDs */
916 const int *vert_boundary_id = tree->boundary->vert_boundary_id.data();
917 const int bid1 = vert_boundary_id[edge[0]], bid2 = vert_boundary_id[edge[1]];
918
919 if (bid1 < 0 || bid2 < 0) {
920 return;
921 }
922
923 /* Retrieve boundary vertex normals and align them to direction. */
924 const ShrinkwrapBoundaryVertData *boundary_verts = tree->boundary->boundary_verts.data();
925 float vedge_dir[2][3], dir[3];
926
927 copy_v3_v3(vedge_dir[0], boundary_verts[bid1].normal_plane);
928 copy_v3_v3(vedge_dir[1], boundary_verts[bid2].normal_plane);
929
930 sub_v3_v3v3(dir, vedge_co[1], vedge_co[0]);
931
932 if (dot_v3v3(boundary_verts[bid1].direction, dir) < 0) {
933 negate_v3(vedge_dir[0]);
934 }
935 if (dot_v3v3(boundary_verts[bid2].direction, dir) < 0) {
936 negate_v3(vedge_dir[1]);
937 }
938
939 /* Solve a quadratic equation: `lerp(d0,d1,x) * (co - lerp(v0,v1,x)) = 0`. */
940 const float d0v0 = dot_v3v3(vedge_dir[0], vedge_co[0]),
941 d0v1 = dot_v3v3(vedge_dir[0], vedge_co[1]);
942 const float d1v0 = dot_v3v3(vedge_dir[1], vedge_co[0]),
943 d1v1 = dot_v3v3(vedge_dir[1], vedge_co[1]);
944 const float d0co = dot_v3v3(vedge_dir[0], co);
945
946 /* Checked for non-zero prevent divide by zero when calculating `x`. */
947 const float a = d0v1 - d0v0 + d1v0 - d1v1;
948 if (a != 0.0f) {
949 const float b = 2 * d0v0 - d0v1 - d0co - d1v0 + dot_v3v3(vedge_dir[1], co);
950 const float c = d0co - d0v0;
951 const float det = b * b - 4 * a * c;
952
953 if (det >= 0) {
954 const float epsilon = 1e-6f;
955 const float sdet = sqrtf(det);
956 float hit_co[3], hit_no[3];
957
958 for (int i = (det > 0 ? 2 : 0); i >= 0; i -= 2) {
959 float x = (-b + float(i - 1) * sdet) / (2.0f * a);
960
961 if (x >= -epsilon && x <= 1.0f + epsilon) {
962 CLAMP(x, 0.0f, 1.0f);
963
964 float vedge_no[2][3];
965 copy_v3_v3(vedge_no[0], tree->vert_normals[edge[0]]);
966 copy_v3_v3(vedge_no[1], tree->vert_normals[edge[1]]);
967
968 interp_v3_v3v3(hit_co, vedge_co[0], vedge_co[1], x);
969 interp_v3_v3v3(hit_no, vedge_no[0], vedge_no[1], x);
970
971 update_hit(nearest, index, co, hit_co, hit_no);
972 }
973 }
974 }
975 }
976}
977
978/* Target normal projection BVH callback - based on mesh_corner_tri_nearest_point. */
979static void mesh_corner_tris_target_project(void *userdata,
980 int index,
981 const float co[3],
982 BVHTreeNearest *nearest)
983{
984 using namespace blender;
985 const ShrinkwrapTreeData *tree = (ShrinkwrapTreeData *)userdata;
986 const blender::bke::BVHTreeFromMesh *data = &tree->treeData;
987 const int3 &tri = data->corner_tris[index];
988 const int tri_verts[3] = {
989 data->corner_verts[tri[0]],
990 data->corner_verts[tri[1]],
991 data->corner_verts[tri[2]],
992 };
993 const float *vtri_co[3] = {
994 data->vert_positions[tri_verts[0]],
995 data->vert_positions[tri_verts[1]],
996 data->vert_positions[tri_verts[2]],
997 };
998 float raw_hit_co[3], hit_co[3], hit_no[3], dist_sq, vtri_no[3][3];
999
1000 /* First find the closest point and bail out if it's worse than the current solution. */
1001 closest_on_tri_to_point_v3(raw_hit_co, co, UNPACK3(vtri_co));
1002 dist_sq = len_squared_v3v3(co, raw_hit_co);
1003
1004#ifdef TRACE_TARGET_PROJECT
1005 printf("TRIANGLE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) %g %g\n",
1006 index,
1007 UNPACK3(vtri_co[0]),
1008 UNPACK3(vtri_co[1]),
1009 UNPACK3(vtri_co[2]),
1010 dist_sq,
1011 nearest->dist_sq);
1012#endif
1013
1014 if (dist_sq >= nearest->dist_sq) {
1015 return;
1016 }
1017
1018 /* Decode normals */
1019 copy_v3_v3(vtri_no[0], tree->vert_normals[tri_verts[0]]);
1020 copy_v3_v3(vtri_no[1], tree->vert_normals[tri_verts[1]]);
1021 copy_v3_v3(vtri_no[2], tree->vert_normals[tri_verts[2]]);
1022
1023 /* Solve the equations for the triangle */
1024 if (target_project_solve_point_tri(vtri_co, vtri_no, co, raw_hit_co, dist_sq, hit_co, hit_no)) {
1025 update_hit(nearest, index, co, hit_co, hit_no);
1026 }
1027 /* Boundary edges */
1028 else if (tree->boundary && tree->boundary->has_boundary() &&
1029 tree->boundary->tri_has_boundary[index])
1030 {
1031 const BitSpan is_boundary = tree->boundary->edge_is_boundary;
1033 tree->edges, data->corner_verts, tree->corner_edges, tri);
1034
1035 for (int i = 0; i < 3; i++) {
1036 if (edges[i] >= 0 && is_boundary[edges[i]]) {
1037 target_project_edge(tree, index, co, nearest, edges[i]);
1038 }
1039 }
1040 }
1041}
1042
1044 BVHTreeNearest *nearest,
1045 float co[3],
1046 int type)
1047{
1048 blender::bke::BVHTreeFromMesh *treeData = &tree->treeData;
1049
1050 if (type == MOD_SHRINKWRAP_TARGET_PROJECT) {
1051#ifdef TRACE_TARGET_PROJECT
1052 printf("\n====== TARGET PROJECT START ======\n");
1053#endif
1054
1057
1058#ifdef TRACE_TARGET_PROJECT
1059 printf("====== TARGET PROJECT END: %d %g ======\n\n", nearest->index, nearest->dist_sq);
1060#endif
1061
1062 if (nearest->index < 0) {
1063 /* fall back to simple nearest */
1064 BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
1065 }
1066 }
1067 else {
1068 BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
1069 }
1070}
1071
1078static void shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdata,
1079 const int i,
1080 const TaskParallelTLS *__restrict tls)
1081{
1082 ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
1083
1084 ShrinkwrapCalcData *calc = data->calc;
1085 BVHTreeNearest *nearest = static_cast<BVHTreeNearest *>(tls->userdata_chunk);
1086
1087 float *co = calc->vertexCos[i];
1088 float tmp_co[3];
1090 calc->dvert, i, calc->vgroup, calc->invert_vgroup);
1091
1092 if (weight == 0.0f) {
1093 return;
1094 }
1095
1096 /* Convert the vertex to tree coordinates */
1097 if (calc->vert_positions) {
1098 copy_v3_v3(tmp_co, calc->vert_positions[i]);
1099 }
1100 else {
1101 copy_v3_v3(tmp_co, co);
1102 }
1104
1105 /* Use local proximity heuristics (to reduce the nearest search)
1106 *
1107 * If we already had an hit before.. we assume this vertex is going to have a close hit to that
1108 * other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
1109 * This will lead in pruning of the search tree. */
1110 if (nearest->index != -1) {
1112 /* Heuristic doesn't work because of additional restrictions. */
1113 nearest->index = -1;
1114 nearest->dist_sq = FLT_MAX;
1115 }
1116 else {
1117 nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
1118 }
1119 }
1120 else {
1121 nearest->dist_sq = FLT_MAX;
1122 }
1123
1124 BKE_shrinkwrap_find_nearest_surface(data->tree, nearest, tmp_co, calc->smd->shrinkType);
1125
1126 /* Found the nearest vertex */
1127 if (nearest->index != -1) {
1129 nullptr,
1130 calc->smd->shrinkMode,
1131 nearest->index,
1132 nearest->co,
1133 nearest->no,
1134 calc->keepDist,
1135 tmp_co,
1136 tmp_co);
1137
1138 /* Convert the coordinates back to mesh coordinates */
1140 interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
1141 }
1142}
1143
1146 int corner_tri_idx,
1147 const float hit_co[3],
1148 const float hit_no[3],
1149 float r_no[3])
1150{
1151 using namespace blender;
1152 const blender::bke::BVHTreeFromMesh *treeData = &tree->treeData;
1153 const int3 &tri = treeData->corner_tris[corner_tri_idx];
1154 const int face_i = tree->mesh->corner_tri_faces()[corner_tri_idx];
1155
1156 /* Interpolate smooth normals if enabled. */
1157 if (tree->sharp_faces.is_empty() || tree->sharp_faces[face_i]) {
1158 const int vert_indices[3] = {treeData->corner_verts[tri[0]],
1159 treeData->corner_verts[tri[1]],
1160 treeData->corner_verts[tri[2]]};
1161 float w[3], no[3][3], tmp_co[3];
1162
1163 /* Custom normals. */
1164 if (!tree->corner_normals.is_empty()) {
1165 copy_v3_v3(no[0], tree->corner_normals[tri[0]]);
1166 copy_v3_v3(no[1], tree->corner_normals[tri[1]]);
1167 copy_v3_v3(no[2], tree->corner_normals[tri[2]]);
1168 }
1169 /* Ordinary vertex normals. */
1170 else {
1171 copy_v3_v3(no[0], tree->vert_normals[vert_indices[0]]);
1172 copy_v3_v3(no[1], tree->vert_normals[vert_indices[1]]);
1173 copy_v3_v3(no[2], tree->vert_normals[vert_indices[2]]);
1174 }
1175
1176 /* Barycentric weights from hit point. */
1177 copy_v3_v3(tmp_co, hit_co);
1178
1179 if (transform) {
1181 }
1182
1184 treeData->vert_positions[vert_indices[0]],
1185 treeData->vert_positions[vert_indices[1]],
1186 treeData->vert_positions[vert_indices[2]],
1187 tmp_co);
1188
1189 /* Interpolate using weights. */
1190 interp_v3_v3v3v3(r_no, no[0], no[1], no[2], w);
1191
1192 if (transform) {
1194 }
1195 else {
1196 normalize_v3(r_no);
1197 }
1198 }
1199 /* Use the face normal if flat. */
1200 else if (!tree->face_normals.is_empty()) {
1201 copy_v3_v3(r_no, tree->face_normals[face_i]);
1202 if (transform) {
1204 }
1205 }
1206 /* Finally fall back to the corner_tris normal. */
1207 else {
1208 copy_v3_v3(r_no, hit_no);
1209 }
1210}
1211
1212/* Helper for MOD_SHRINKWRAP_INSIDE, MOD_SHRINKWRAP_OUTSIDE and MOD_SHRINKWRAP_OUTSIDE_SURFACE. */
1213static void shrinkwrap_snap_with_side(float r_point_co[3],
1214 const float point_co[3],
1215 const float hit_co[3],
1216 const float hit_no[3],
1217 float goal_dist,
1218 float forcesign,
1219 bool forcesnap)
1220{
1221 float delta[3];
1222 sub_v3_v3v3(delta, point_co, hit_co);
1223
1224 float dist = len_v3(delta);
1225
1226 /* If exactly on the surface, push out along normal */
1227 if (dist < FLT_EPSILON) {
1228 if (forcesnap || goal_dist > 0) {
1229 madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist * forcesign);
1230 }
1231 else {
1232 copy_v3_v3(r_point_co, hit_co);
1233 }
1234 }
1235 /* Move to the correct side if needed */
1236 else {
1237 float dsign = signf(dot_v3v3(delta, hit_no));
1238
1239 if (forcesign == 0.0f) {
1240 forcesign = dsign;
1241 }
1242
1243 /* If on the wrong side or too close, move to correct */
1244 if (forcesnap || dsign * dist * forcesign < goal_dist) {
1245 mul_v3_fl(delta, dsign / dist);
1246
1247 /* At very small distance, blend in the hit normal to stabilize math. */
1248 float dist_epsilon = (fabsf(goal_dist) + len_manhattan_v3(hit_co)) * 1e-4f;
1249
1250 if (dist < dist_epsilon) {
1251#ifdef TRACE_TARGET_PROJECT
1252 printf("zero_factor %g = %g / %g\n", dist / dist_epsilon, dist, dist_epsilon);
1253#endif
1254
1255 interp_v3_v3v3(delta, hit_no, delta, dist / dist_epsilon);
1256 }
1257
1258 madd_v3_v3v3fl(r_point_co, hit_co, delta, goal_dist * forcesign);
1259 }
1260 else {
1261 copy_v3_v3(r_point_co, point_co);
1262 }
1263 }
1264}
1265
1268 int mode,
1269 int hit_idx,
1270 const float hit_co[3],
1271 const float hit_no[3],
1272 float goal_dist,
1273 const float point_co[3],
1274 float r_point_co[3])
1275{
1276 float tmp[3];
1277
1278 switch (mode) {
1279 /* Offsets along the line between point_co and hit_co. */
1281 if (goal_dist != 0) {
1282 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, 0, true);
1283 }
1284 else {
1285 copy_v3_v3(r_point_co, hit_co);
1286 }
1287 break;
1288
1290 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, -1, false);
1291 break;
1292
1294 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, false);
1295 break;
1296
1298 if (goal_dist != 0) {
1299 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, true);
1300 }
1301 else {
1302 copy_v3_v3(r_point_co, hit_co);
1303 }
1304 break;
1305
1306 /* Offsets along the normal */
1308 if (goal_dist != 0) {
1309 BKE_shrinkwrap_compute_smooth_normal(tree, transform, hit_idx, hit_co, hit_no, tmp);
1310 madd_v3_v3v3fl(r_point_co, hit_co, tmp, goal_dist);
1311 }
1312 else {
1313 copy_v3_v3(r_point_co, hit_co);
1314 }
1315 break;
1316
1317 default:
1318 printf("Unknown Shrinkwrap surface snap mode: %d\n", mode);
1319 copy_v3_v3(r_point_co, hit_co);
1320 }
1321}
1322
1324{
1326
1327 /* Setup nearest */
1328 nearest.index = -1;
1329 nearest.dist_sq = FLT_MAX;
1330
1331 /* Find the nearest vertex */
1333 data.calc = calc;
1334 data.tree = calc->tree;
1335 TaskParallelSettings settings;
1337 settings.use_threading = (calc->numVerts > 10000);
1338 settings.userdata_chunk = &nearest;
1339 settings.userdata_chunk_size = sizeof(nearest);
1342}
1343
1345 Mesh *mesh, const int subdivision_level)
1346{
1347 using namespace blender;
1348 using namespace blender::bke;
1349
1350 Array<float3> positions = mesh->vert_positions();
1351
1352 subdiv::Settings settings{};
1353 settings.is_simple = false;
1354 settings.is_adaptive = false;
1355 settings.level = subdivision_level;
1356 settings.use_creases = true;
1357
1358 /* Default subdivision surface modifier settings:
1359 * - UV Smooth:Keep Corners.
1360 * - BoundarySmooth: All. */
1362 settings.fvar_linear_interpolation =
1364
1365 subdiv::Subdiv *subdiv = subdiv::update_from_mesh(nullptr, &settings, mesh);
1366 if (subdiv) {
1369 }
1370
1371 return positions;
1372}
1373
1375 const ModifierEvalContext *ctx,
1376 Scene * /*scene*/,
1377 Object *ob,
1378 Mesh *mesh,
1379 const MDeformVert *dvert,
1380 const int defgrp_index,
1381 float (*vertexCos)[3],
1382 int numVerts)
1383{
1385 blender::Array<blender::float3> subdivided_positions;
1386
1387 /* remove loop dependencies on derived meshes (TODO should this be done elsewhere?) */
1388 if (smd->target == ob) {
1389 smd->target = nullptr;
1390 }
1391 if (smd->auxTarget == ob) {
1392 smd->auxTarget = nullptr;
1393 }
1394
1395 /* Configure Shrinkwrap calc data */
1396 calc.smd = smd;
1397 calc.ob = ob;
1398 calc.numVerts = numVerts;
1399 calc.vertexCos = vertexCos;
1400 calc.dvert = dvert;
1401 calc.vgroup = defgrp_index;
1403
1404 if (smd->target != nullptr) {
1405 Object *ob_target = DEG_get_evaluated(ctx->depsgraph, smd->target);
1407
1408 /* TODO: there might be several "bugs" with non-uniform scales matrices
1409 * because it will no longer be nearest surface, not sphere projection
1410 * because space has been deformed */
1411 BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob, ob_target);
1412
1413 /* TODO: smd->keepDist is in global units.. must change to local */
1414 calc.keepDist = smd->keepDist;
1415 }
1417
1418 if (mesh != nullptr && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) {
1419 /* Setup arrays to get vertex positions, normals and deform weights */
1420 calc.vert_positions = reinterpret_cast<float (*)[3]>(mesh->vert_positions_for_write().data());
1421 calc.vert_normals = mesh->vert_normals();
1422
1423 /* Using vertices positions/normals as if a subsurface was applied */
1424 if (smd->subsurfLevels) {
1425 subdivided_positions = shrinkwrap_calc_subdivided_positions(mesh, smd->subsurfLevels);
1426 calc.vert_positions = reinterpret_cast<float (*)[3]>(subdivided_positions.data());
1427 }
1428 }
1429
1430 /* Projecting target defined - lets work! */
1432
1433 if (BKE_shrinkwrap_init_tree(&tree, calc.target, smd->shrinkType, smd->shrinkMode, false)) {
1434 calc.tree = &tree;
1435
1436 switch (smd->shrinkType) {
1440 break;
1441
1443 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
1444 break;
1445
1447 TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), deform_vertex);
1448 break;
1449 }
1450
1452 }
1453}
1454
1456 Object &object,
1458 const blender::Span<MDeformVert> dvert,
1459 const int defgrp_index,
1461{
1462 using namespace blender::bke;
1463
1465 /* Convert params struct to use the same struct and function used with meshes. */
1467 smd.target = params.target;
1468 smd.auxTarget = params.aux_target;
1469 smd.keepDist = params.keep_distance;
1470 smd.shrinkType = params.shrink_type;
1471 smd.shrinkOpts = params.shrink_options;
1472 smd.shrinkMode = params.shrink_mode;
1473 smd.projLimit = params.projection_limit;
1474 smd.projAxis = params.projection_axis;
1475
1476 /* Configure Shrinkwrap calc data. */
1477 calc.smd = &smd;
1478 calc.ob = &object;
1479 calc.numVerts = int(positions.size());
1480 calc.vertexCos = reinterpret_cast<float (*)[3]>(positions.data());
1481 calc.dvert = dvert.is_empty() ? nullptr : dvert.data();
1482 calc.vgroup = defgrp_index;
1483 calc.invert_vgroup = params.invert_vertex_weights;
1484
1485 BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, &object, params.target);
1486 calc.keepDist = params.keep_distance;
1487 calc.tree = &tree;
1488
1489 switch (params.shrink_type) {
1490 case MOD_SHRINKWRAP_NEAREST_SURFACE:
1491 case MOD_SHRINKWRAP_TARGET_PROJECT:
1492 TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), gpdeform_surface);
1493 break;
1494
1495 case MOD_SHRINKWRAP_PROJECT:
1496 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), gpdeform_project);
1497 break;
1498
1499 case MOD_SHRINKWRAP_NEAREST_VERTEX:
1500 TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), gpdeform_vertex);
1501 break;
1502 }
1503}
1504
1506 Scene *scene,
1507 Object *ob_source,
1508 Object *ob_target)
1509{
1510 ShrinkwrapModifierData ssmd = {{nullptr}};
1511 ModifierEvalContext ctx = {depsgraph, ob_source, ModifierApplyFlag(0)};
1512
1513 ssmd.target = ob_target;
1516 ssmd.keepDist = 0.0f;
1517
1518 Mesh *src_me = static_cast<Mesh *>(ob_source->data);
1519
1521 &ssmd,
1522 &ctx,
1523 scene,
1524 ob_source,
1525 src_me,
1526 nullptr,
1527 -1,
1528 reinterpret_cast<float (*)[3]>(src_me->vert_positions_for_write().data()),
1529 src_me->verts_num);
1530 src_me->tag_positions_changed();
1531}
1532
1533void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
1534{
1535 ShrinkwrapModifierData ssmd = {{nullptr}};
1536
1537 ssmd.target = ob_target;
1541 ssmd.keepDist = 0.0f;
1542
1543 /* Tolerance value to prevent artifacts on sharp edges of a mesh.
1544 * This constant and based on experimenting with different values. */
1545 const float projLimitTolerance = 5.0f;
1546 ssmd.projLimit = target_me->remesh_voxel_size * projLimitTolerance;
1547
1549
1550 calc.smd = &ssmd;
1551 calc.numVerts = src_me->verts_num;
1552 calc.vertexCos = reinterpret_cast<float (*)[3]>(src_me->vert_positions_for_write().data());
1553 calc.vert_normals = src_me->vert_normals();
1554 calc.vgroup = -1;
1555 calc.target = target_me;
1556 calc.keepDist = ssmd.keepDist;
1557 calc.vert_positions = reinterpret_cast<float (*)[3]>(src_me->vert_positions_for_write().data());
1558 BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob_target, ob_target);
1559
1561 if (BKE_shrinkwrap_init_tree(&tree, calc.target, ssmd.shrinkType, ssmd.shrinkMode, false)) {
1562 calc.tree = &tree;
1563 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
1565 }
1566
1567 src_me->tag_positions_changed();
1568}
support for deformation groups and hooks.
float BKE_defvert_array_find_weight_safe(const MDeformVert *dvert, int index, int defgroup, bool invert)
Definition deform.cc:780
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
Mesh * BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval)
ModifierApplyFlag
#define NULL_BVHTreeNearest
#define NULL_ShrinkwrapCalcData
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BVH_RAYCAST_DIST_MAX
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
@ BVH_NEAREST_OPTIMAL_ORDER
int BLI_bvhtree_find_nearest_ex(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata, int flag)
MINLINE float signf(float f)
#define M_SQRT2
void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3])
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
float mat4_to_scale(const float mat[4][4])
void BLI_space_transform_apply_normal(const struct SpaceTransform *data, float no[3])
void BLI_space_transform_apply(const struct SpaceTransform *data, float co[3])
void BLI_space_transform_invert_normal(const struct SpaceTransform *data, float no[3])
void BLI_space_transform_invert(const struct SpaceTransform *data, float co[3])
#define BLI_SPACE_TRANSFORM_SETUP(data, local, target)
bool BLI_newton3d_solve(Newton3D_DeltaFunc func_delta, Newton3D_JacobianFunc func_jacobian, Newton3D_CorrectionFunc func_correction, void *userdata, float epsilon, int max_iterations, bool trace, const float x_init[3], float result[3])
Solve a generic f(x) = 0 equation using Newton's method.
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void sub_v3_v3(float r[3], const float a[3])
void interp_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float w[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v2_fl(float r[2], float f)
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE float len_manhattan_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
Utility defines for timing/benchmarks.
#define CLAMP(a, b, c)
#define UNPACK3(a)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
#define MOD_SHRINKWRAP_CULL_TARGET_MASK
@ MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS
@ MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS
@ MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS
@ MOD_SHRINKWRAP_PROJECT_OVER_NORMAL
@ MOD_SHRINKWRAP_TARGET_PROJECT
@ MOD_SHRINKWRAP_NEAREST_VERTEX
@ MOD_SHRINKWRAP_PROJECT
@ MOD_SHRINKWRAP_NEAREST_SURFACE
@ MOD_SHRINKWRAP_ON_SURFACE
@ MOD_SHRINKWRAP_OUTSIDE
@ MOD_SHRINKWRAP_INSIDE
@ MOD_SHRINKWRAP_ABOVE_SURFACE
@ MOD_SHRINKWRAP_OUTSIDE_SURFACE
@ MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR
@ MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE
@ MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR
@ MOD_SHRINKWRAP_CULL_TARGET_BACKFACE
@ MOD_SHRINKWRAP_INVERT_VGROUP
@ MOD_SHRINKWRAP_INVERT_CULL_TARGET
Object is a sort of wrapper for general info.
switch((BMIterType) itype)
BMesh const char void * data
BPy_StructRNA * depsgraph
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
long long int int64_t
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static T sum(const btAlignedObjectArray< T > &items)
const T * data() const
Definition BLI_array.hh:312
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr T * data() const
Definition BLI_span.hh:539
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup(const StringRef attribute_id) const
nullptr float
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
CCL_NAMESPACE_BEGIN struct Options options
KDTree_3d * tree
#define printf(...)
#define fixed
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int3 corner_tri_get_real_edges(Span< int2 > edges, Span< int > corner_verts, Span< int > corner_edges, const int3 &corner_tri)
static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata, MutableSpan< int8_t > status, int index, const float edge_dir[3], signed char side)
const ShrinkwrapBoundaryData & boundary_cache_ensure(const Mesh &mesh)
static ShrinkwrapBoundaryData shrinkwrap_build_boundary_data(const Mesh &mesh)
void free(Subdiv *subdiv)
Definition subdiv.cc:190
@ SUBDIV_FVAR_LINEAR_INTERPOLATION_CORNERS_AND_JUNCTIONS
Definition BKE_subdiv.hh:37
void deform_coarse_vertices(Subdiv *subdiv, const Mesh *coarse_mesh, MutableSpan< float3 > vert_positions)
Subdiv * update_from_mesh(Subdiv *subdiv, const Settings *settings, const Mesh *mesh)
Definition subdiv.cc:179
VecBase< int32_t, 2 > int2
VecBase< int32_t, 3 > int3
const int status
#define fabsf
#define sqrtf
static void target_project_edge(const ShrinkwrapTreeData *tree, int index, const float co[3], BVHTreeNearest *nearest, int eidx)
static void target_project_tri_clamp(float x[3])
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])
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])
static void mesh_corner_tris_target_project(void *userdata, int index, const float co[3], BVHTreeNearest *nearest)
static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc)
void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, const ModifierEvalContext *ctx, Scene *, Object *ob, Mesh *mesh, const MDeformVert *dvert, const int defgrp_index, float(*vertexCos)[3], int numVerts)
void BKE_shrinkwrap_free_tree(ShrinkwrapTreeData *)
static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
static bool update_hit(BVHTreeNearest *nearest, int index, const float co[3], const float hit_co[3], const float hit_no[3])
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)
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)
static void target_project_tri_deviation(void *userdata, const float x[3], float r_delta[3])
static void shrinkwrap_calc_nearest_vertex_cb_ex(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
void BKE_shrinkwrap_mesh_nearest_surface_deform(Depsgraph *depsgraph, Scene *scene, Object *ob_source, Object *ob_target)
static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
#define TIMEIT_BENCH(expr, id)
Definition shrinkwrap.cc:49
bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode)
Definition shrinkwrap.cc:89
static void target_project_tri_jacobian(void *userdata, const float x[3], float r_jacobian[3][3])
void BKE_shrinkwrap_compute_smooth_normal(const ShrinkwrapTreeData *tree, const SpaceTransform *transform, int corner_tri_idx, const float hit_co[3], const float hit_no[3], float r_no[3])
static blender::Array< blender::float3 > shrinkwrap_calc_subdivided_positions(Mesh *mesh, const int subdivision_level)
bool BKE_shrinkwrap_init_tree(ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals)
Definition shrinkwrap.cc:96
void shrinkwrapParams_deform(const ShrinkwrapParams &params, Object &object, ShrinkwrapTreeData &tree, const blender::Span< MDeformVert > dvert, const int defgrp_index, const blender::MutableSpan< blender::float3 > positions)
static bool target_project_tri_correct(void *, const float x[3], float step[3], float x_next[3])
void BKE_shrinkwrap_find_nearest_surface(ShrinkwrapTreeData *tree, BVHTreeNearest *nearest, float co[3], int type)
static void shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
#define FLT_MAX
Definition stdcycles.h:14
float remesh_voxel_size
int verts_num
SpaceTransform * local2aux
Definition shrinkwrap.cc:86
ShrinkwrapTreeData * tree
Definition shrinkwrap.cc:82
ShrinkwrapCalcData * calc
Definition shrinkwrap.cc:80
ShrinkwrapTreeData * aux_tree
Definition shrinkwrap.cc:83
float(* vertexCos)[3]
Definition shrinkwrap.cc:63
SpaceTransform local2target
Definition shrinkwrap.cc:71
float(* vert_positions)[3]
Definition shrinkwrap.cc:60
blender::Span< blender::float3 > vert_normals
Definition shrinkwrap.cc:61
const MDeformVert * dvert
Definition shrinkwrap.cc:66
ShrinkwrapTreeData * tree
Definition shrinkwrap.cc:72
ShrinkwrapModifierData * smd
Definition shrinkwrap.cc:56
const float ** vtri_co
const float * point_co
const float(* vtri_no)[3]
size_t userdata_chunk_size
Definition BLI_task.h:164
BVHTree_NearestPointCallback nearest_callback
VtxBoundaryInterpolation vtx_boundary_interpolation
Definition BKE_subdiv.hh:77
FVarLinearInterpolation fvar_linear_interpolation
Definition BKE_subdiv.hh:78
i
Definition text_draw.cc:230