Blender V4.3
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
9#include <cfloat>
10#include <cmath>
11#include <cstdio>
12#include <cstring>
13#include <ctime>
14#include <memory.h>
15
17#include "DNA_mesh_types.h"
18#include "DNA_modifier_types.h"
19#include "DNA_object_types.h"
20
21#include "BLI_math_geom.h"
22#include "BLI_math_matrix.h"
23#include "BLI_math_solvers.h"
24#include "BLI_math_vector.h"
25#include "BLI_task.h"
26#include "BLI_utildefines.h"
27
28#include "BKE_attribute.hh"
30#include "BKE_modifier.hh"
31#include "BKE_shrinkwrap.hh"
32
33#include "BKE_deform.hh"
34#include "BKE_editmesh.hh"
35#include "BKE_mesh.hh" /* for OMP limits. */
36#include "BKE_mesh_wrapper.hh"
37#include "BKE_subsurf.hh"
38
40
41#include "MEM_guardedalloc.h"
42
43#include "BLI_strict_flags.h" /* 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->faces = mesh->faces();
117 data->corner_edges = mesh->corner_edges();
118 data->vert_normals = mesh->vert_normals();
119 const AttributeAccessor attributes = mesh->attributes();
120 data->sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
121
122 if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
123 data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2);
124
125 return data->bvh != nullptr;
126 }
127
128 if (mesh->faces_num <= 0) {
129 return false;
130 }
131
132 data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_CORNER_TRIS, 4);
133
134 if (data->bvh == nullptr) {
135 return false;
136 }
137
138 if (force_normals || BKE_shrinkwrap_needs_normals(shrinkType, shrinkMode)) {
139 data->face_normals = mesh->face_normals();
140 if (mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) {
141 data->corner_normals = mesh->corner_normals();
142 }
143 }
144
145 if (shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
147 }
148
149 return true;
150}
151
153{
154 free_bvhtree_from_mesh(&data->treeData);
155}
156
157namespace blender::bke::shrinkwrap {
158
159/* Accumulate edge for average boundary edge direction. */
161 MutableSpan<int8_t> status,
162 int index,
163 const float edge_dir[3],
164 signed char side)
165{
166 BLI_assert(index >= 0);
167 float *direction = vdata[index].direction;
168
169 /* Invert the direction vector if either:
170 * - This is the second edge and both edges have the vertex as start or end.
171 * - For third and above, if it points in the wrong direction.
172 */
173 if (status[index] >= 0 ? status[index] == side : dot_v3v3(direction, edge_dir) < 0) {
174 sub_v3_v3(direction, edge_dir);
175 }
176 else {
177 add_v3_v3(direction, edge_dir);
178 }
179
180 status[index] = (status[index] == 0) ? side : -1;
181}
182
184{
185 const Span<float3> positions = mesh.vert_positions();
186 const Span<int2> edges = mesh.edges();
187 const Span<int> corner_verts = mesh.corner_verts();
188 const Span<int> corner_edges = mesh.corner_edges();
189
190 /* Count faces per edge (up to 2). */
191 Array<int8_t> edge_mode(edges.size(), 0);
192
193 for (const int edge : corner_edges) {
194 if (edge_mode[edge] < 2) {
195 edge_mode[edge]++;
196 }
197 }
198
199 /* Build the boundary edge bitmask. */
200 BitVector<> edge_is_boundary(mesh.edges_num, false);
201
202 int num_boundary_edges = 0;
203 for (const int64_t i : edges.index_range()) {
204 if (edge_mode[i] == 1) {
205 edge_is_boundary[i].set();
206 num_boundary_edges++;
207 }
208 }
209
210 /* If no boundary, return nullptr. */
211 if (num_boundary_edges == 0) {
212 return {};
213 }
214
215 /* Allocate the data object. */
217
218 /* Build the boundary corner_tris bit-mask. */
219 const Span<int3> corner_tris = mesh.corner_tris();
220
221 BitVector<> tri_has_boundary(corner_tris.size(), false);
222
223 for (const int64_t i : corner_tris.index_range()) {
225 edges, corner_verts, corner_edges, corner_tris[i]);
226
227 for (int j = 0; j < 3; j++) {
228 if (real_edges[j] >= 0 && edge_is_boundary[real_edges[j]]) {
229 tri_has_boundary[i].set();
230 break;
231 }
232 }
233 }
234
235 /* Find boundary vertices and build a mapping table for compact storage of data. */
236 Array<int> vert_boundary_id(mesh.verts_num, 0);
237
238 for (const int64_t i : edges.index_range()) {
239 if (edge_is_boundary[i]) {
240 const int2 &edge = edges[i];
241 vert_boundary_id[edge[0]] = 1;
242 vert_boundary_id[edge[1]] = 1;
243 }
244 }
245
246 int boundary_verts_num = 0;
247 for (const int64_t i : positions.index_range()) {
248 vert_boundary_id[i] = (vert_boundary_id[i] != 0) ? boundary_verts_num++ : -1;
249 }
250
251 /* Compute average directions. */
252 Array<ShrinkwrapBoundaryVertData> boundary_verts(boundary_verts_num);
253
254 Array<int8_t> vert_status(boundary_verts_num);
255 for (const int64_t i : edges.index_range()) {
256 if (edge_is_boundary[i]) {
257 const int2 &edge = edges[i];
258
259 float dir[3];
260 sub_v3_v3v3(dir, positions[edge[1]], positions[edge[0]]);
261 normalize_v3(dir);
262
263 merge_vert_dir(boundary_verts.data(), vert_status, vert_boundary_id[edge[0]], dir, 1);
264 merge_vert_dir(boundary_verts.data(), vert_status, vert_boundary_id[edge[1]], dir, 2);
265 }
266 }
267
268 /* Finalize average direction and compute normal. */
269 const Span<float3> vert_normals = mesh.vert_normals();
270 for (const int64_t i : positions.index_range()) {
271 int bidx = vert_boundary_id[i];
272
273 if (bidx >= 0) {
274 ShrinkwrapBoundaryVertData *vdata = &boundary_verts[bidx];
275 float tmp[3];
276
277 normalize_v3(vdata->direction);
278
279 cross_v3_v3v3(tmp, vert_normals[i], vdata->direction);
280 cross_v3_v3v3(vdata->normal_plane, tmp, vert_normals[i]);
282 }
283 }
284
285 data.edge_is_boundary = std::move(edge_is_boundary);
286 data.tri_has_boundary = std::move(tri_has_boundary);
287 data.vert_boundary_id = std::move(vert_boundary_id);
288 data.boundary_verts = std::move(boundary_verts);
289
290 return data;
291}
292
294{
295 mesh.runtime->shrinkwrap_boundary_cache.ensure(
296 [&](ShrinkwrapBoundaryData &r_data) { r_data = shrinkwrap_build_boundary_data(mesh); });
297 return mesh.runtime->shrinkwrap_boundary_cache.data();
298}
299
300} // namespace blender::bke::shrinkwrap
301
308static void shrinkwrap_calc_nearest_vertex_cb_ex(void *__restrict userdata,
309 const int i,
310 const TaskParallelTLS *__restrict tls)
311{
312 ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
313
314 ShrinkwrapCalcData *calc = data->calc;
315 BVHTreeFromMesh *treeData = &data->tree->treeData;
316 BVHTreeNearest *nearest = static_cast<BVHTreeNearest *>(tls->userdata_chunk);
317
318 float *co = calc->vertexCos[i];
319 float tmp_co[3];
320 float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
321
322 if (calc->invert_vgroup) {
323 weight = 1.0f - weight;
324 }
325
326 if (weight == 0.0f) {
327 return;
328 }
329
330 /* Convert the vertex to tree coordinates */
331 if (calc->vert_positions) {
332 copy_v3_v3(tmp_co, calc->vert_positions[i]);
333 }
334 else {
335 copy_v3_v3(tmp_co, co);
336 }
338
339 /* Use local proximity heuristics (to reduce the nearest search)
340 *
341 * If we already had an hit before.. we assume this vertex is going to have a close hit to that
342 * other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
343 * This will lead in pruning of the search tree. */
344 if (nearest->index != -1) {
345 nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
346 }
347 else {
348 nearest->dist_sq = FLT_MAX;
349 }
350
351 BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData);
352
353 /* Found the nearest vertex */
354 if (nearest->index != -1) {
355 /* Adjusting the vertex weight,
356 * so that after interpolating it keeps a certain distance from the nearest position */
357 if (nearest->dist_sq > FLT_EPSILON) {
358 const float dist = sqrtf(nearest->dist_sq);
359 weight *= (dist - calc->keepDist) / dist;
360 }
361
362 /* Convert the coordinates back to mesh coordinates */
363 copy_v3_v3(tmp_co, nearest->co);
365
366 interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
367 }
368}
369
371{
373
374 /* Setup nearest */
375 nearest.index = -1;
376 nearest.dist_sq = FLT_MAX;
377
379 data.calc = calc;
380 data.tree = calc->tree;
381 TaskParallelSettings settings;
383 settings.use_threading = (calc->numVerts > 10000);
384 settings.userdata_chunk = &nearest;
385 settings.userdata_chunk_size = sizeof(nearest);
387 0, calc->numVerts, &data, shrinkwrap_calc_nearest_vertex_cb_ex, &settings);
388}
389
391 const float vert[3],
392 const float dir[3],
393 const float ray_radius,
394 const SpaceTransform *transf,
396 BVHTreeRayHit *hit)
397{
398 /* don't use this because this dist value could be incompatible
399 * this value used by the callback for comparing previous/new dist values.
400 * also, at the moment there is no need to have a corrected 'dist' value */
401 // #define USE_DIST_CORRECT
402
403 float tmp_co[3], tmp_no[3];
404 const float *co, *no;
405 BVHTreeRayHit hit_tmp;
406
407 /* Copy from hit (we need to convert hit rays from one space coordinates to the other */
408 memcpy(&hit_tmp, hit, sizeof(hit_tmp));
409
410 /* Apply space transform (TODO readjust dist) */
411 if (transf) {
412 copy_v3_v3(tmp_co, vert);
413 BLI_space_transform_apply(transf, tmp_co);
414 co = tmp_co;
415
416 copy_v3_v3(tmp_no, dir);
417 BLI_space_transform_apply_normal(transf, tmp_no);
418 no = tmp_no;
419
420#ifdef USE_DIST_CORRECT
421 hit_tmp.dist *= mat4_to_scale(((SpaceTransform *)transf)->local2target);
422#endif
423 }
424 else {
425 co = vert;
426 no = dir;
427 }
428
429 hit_tmp.index = -1;
430
432 tree->bvh, co, no, ray_radius, &hit_tmp, tree->treeData.raycast_callback, &tree->treeData);
433
434 if (hit_tmp.index != -1) {
435 /* invert the normal first so face culling works on rotated objects */
436 if (transf) {
437 BLI_space_transform_invert_normal(transf, hit_tmp.no);
438 }
439
441 /* Apply back-face. */
442 const float dot = dot_v3v3(dir, hit_tmp.no);
443 if (((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) ||
445 {
446 return false; /* Ignore hit */
447 }
448 }
449
450 if (transf) {
451 /* Inverting space transform (TODO: make coherent with the initial dist readjust). */
452 BLI_space_transform_invert(transf, hit_tmp.co);
453#ifdef USE_DIST_CORRECT
454 hit_tmp.dist = len_v3v3(vert, hit_tmp.co);
455#endif
456 }
457
458 BLI_assert(hit_tmp.dist <= hit->dist);
459
460 memcpy(hit, &hit_tmp, sizeof(hit_tmp));
461 return true;
462 }
463 return false;
464}
465
466static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
467 const int i,
468 const TaskParallelTLS *__restrict tls)
469{
470 ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
471
472 ShrinkwrapCalcData *calc = data->calc;
473 ShrinkwrapTreeData *tree = data->tree;
474 ShrinkwrapTreeData *aux_tree = data->aux_tree;
475
476 float *proj_axis = data->proj_axis;
477 SpaceTransform *local2aux = data->local2aux;
478
479 BVHTreeRayHit *hit = static_cast<BVHTreeRayHit *>(tls->userdata_chunk);
480
481 const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit;
482 float *co = calc->vertexCos[i];
483 const float *tmp_co, *tmp_no;
484 float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
485
486 if (calc->invert_vgroup) {
487 weight = 1.0f - weight;
488 }
489
490 if (weight == 0.0f) {
491 return;
492 }
493
494 if (calc->vert_positions != nullptr && calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL)
495 {
496 /* calc->vert_positions contains verts from evaluated mesh. */
497 /* These coordinates are deformed by vertexCos only for normal projection
498 * (to get correct normals) for other cases calc->verts contains undeformed coordinates and
499 * vertexCos should be used */
500 tmp_co = calc->vert_positions[i];
501 tmp_no = calc->vert_normals[i];
502 }
503 else {
504 tmp_co = co;
505 tmp_no = proj_axis;
506 }
507
508 hit->index = -1;
509
510 /* TODO: we should use FLT_MAX here, but sweep-sphere code isn't prepared for that. */
511 hit->dist = BVH_RAYCAST_DIST_MAX;
512
513 bool is_aux = false;
514
515 /* Project over positive direction of axis. */
517 if (aux_tree) {
518 if (BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, 0.0, local2aux, aux_tree, hit)) {
519 is_aux = true;
520 }
521 }
522
524 calc->smd->shrinkOpts, tmp_co, tmp_no, 0.0, &calc->local2target, tree, hit))
525 {
526 is_aux = false;
527 }
528 }
529
530 /* Project over negative direction of axis */
532 float inv_no[3];
533 negate_v3_v3(inv_no, tmp_no);
534
535 char options = calc->smd->shrinkOpts;
536
539 {
541 }
542
543 if (aux_tree) {
544 if (BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, 0.0, local2aux, aux_tree, hit)) {
545 is_aux = true;
546 }
547 }
548
550 options, tmp_co, inv_no, 0.0, &calc->local2target, tree, hit))
551 {
552 is_aux = false;
553 }
554 }
555
556 /* don't set the initial dist (which is more efficient),
557 * because its calculated in the targets space, we want the dist in our own space */
558 if (proj_limit_squared != 0.0f) {
559 if (hit->index != -1 && len_squared_v3v3(hit->co, co) > proj_limit_squared) {
560 hit->index = -1;
561 }
562 }
563
564 if (hit->index != -1) {
565 if (is_aux) {
567 local2aux,
568 calc->smd->shrinkMode,
569 hit->index,
570 hit->co,
571 hit->no,
572 calc->keepDist,
573 tmp_co,
574 hit->co);
575 }
576 else {
578 &calc->local2target,
579 calc->smd->shrinkMode,
580 hit->index,
581 hit->co,
582 hit->no,
583 calc->keepDist,
584 tmp_co,
585 hit->co);
586 }
587
588 interp_v3_v3v3(co, co, hit->co, weight);
589 }
590}
591
593{
594 /* Options about projection direction */
595 float proj_axis[3] = {0.0f, 0.0f, 0.0f};
596
597 /* Ray-cast and tree stuff. */
598
602 BVHTreeRayHit hit;
603
604 /* auxiliary target */
605 Mesh *auxMesh = nullptr;
606 ShrinkwrapTreeData *aux_tree = nullptr;
607 ShrinkwrapTreeData aux_tree_stack;
608 SpaceTransform local2aux;
609
610 /* If the user doesn't allows to project in any direction of projection axis
611 * then there's nothing todo. */
612 if ((calc->smd->shrinkOpts &
614 {
615 return;
616 }
617
618 /* Prepare data to retrieve the direction in which we should project each vertex */
620 if (calc->vert_positions == nullptr) {
621 return;
622 }
623 }
624 else {
625 /* The code supports any axis that is a combination of X,Y,Z
626 * although currently UI only allows to set the 3 different axis */
628 proj_axis[0] = 1.0f;
629 }
631 proj_axis[1] = 1.0f;
632 }
634 proj_axis[2] = 1.0f;
635 }
636
637 normalize_v3(proj_axis);
638
639 /* Invalid projection direction */
640 if (len_squared_v3(proj_axis) < FLT_EPSILON) {
641 return;
642 }
643 }
644
645 if (calc->aux_target) {
647 if (!auxMesh) {
648 return;
649 }
650 BLI_SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->aux_target);
651 }
652
654 &aux_tree_stack, auxMesh, calc->smd->shrinkType, calc->smd->shrinkMode, false))
655 {
656 aux_tree = &aux_tree_stack;
657 }
658
659 /* After successfully build the trees, start projection vertices. */
661 data.calc = calc;
662 data.tree = calc->tree;
663 data.aux_tree = aux_tree;
664 data.proj_axis = proj_axis;
665 data.local2aux = &local2aux;
666 TaskParallelSettings settings;
668 settings.use_threading = (calc->numVerts > 10000);
669 settings.userdata_chunk = &hit;
670 settings.userdata_chunk_size = sizeof(hit);
672 0, calc->numVerts, &data, shrinkwrap_calc_normal_projection_cb_ex, &settings);
673
674 /* free data structures */
675 if (aux_tree) {
676 BKE_shrinkwrap_free_tree(aux_tree);
677 }
678}
679
680/*
681 * Shrinkwrap Target Surface Project mode
682 *
683 * It uses Newton's method to find a surface location with its
684 * smooth normal pointing at the original point.
685 *
686 * The equation system on barycentric weights and normal multiplier:
687 *
688 * (w0*V0 + w1*V1 + w2*V2) + l * (w0*N0 + w1*N1 + w2*N2) - CO = 0
689 * w0 + w1 + w2 = 1
690 *
691 * The actual solution vector is [ w0, w1, l ], with w2 eliminated.
692 */
693
694// #define TRACE_TARGET_PROJECT
695
697 const float **vtri_co;
698 const float (*vtri_no)[3];
699 const float *point_co;
700
703
704 /* Current interpolated position and normal. */
705 float co_interp[3], no_interp[3];
706};
707
708/* Computes the deviation of the equation system from goal. */
709static void target_project_tri_deviation(void *userdata, const float x[3], float r_delta[3])
710{
711 TargetProjectTriData *data = static_cast<TargetProjectTriData *>(userdata);
712
713 const float w[3] = {x[0], x[1], 1.0f - x[0] - x[1]};
714 interp_v3_v3v3v3(data->co_interp, data->vtri_co[0], data->vtri_co[1], data->vtri_co[2], w);
715 interp_v3_v3v3v3(data->no_interp, data->vtri_no[0], data->vtri_no[1], data->vtri_no[2], w);
716
717 madd_v3_v3v3fl(r_delta, data->co_interp, data->no_interp, x[2]);
718 sub_v3_v3(r_delta, data->point_co);
719}
720
721/* Computes the Jacobian matrix of the equation system. */
722static void target_project_tri_jacobian(void *userdata, const float x[3], float r_jacobian[3][3])
723{
724 TargetProjectTriData *data = static_cast<TargetProjectTriData *>(userdata);
725
726 madd_v3_v3v3fl(r_jacobian[0], data->c0_minus_c2, data->n0_minus_n2, x[2]);
727 madd_v3_v3v3fl(r_jacobian[1], data->c1_minus_c2, data->n1_minus_n2, x[2]);
728
729 copy_v3_v3(r_jacobian[2], data->vtri_no[2]);
730 madd_v3_v3fl(r_jacobian[2], data->n0_minus_n2, x[0]);
731 madd_v3_v3fl(r_jacobian[2], data->n1_minus_n2, x[1]);
732}
733
734/* Clamp barycentric weights to the triangle. */
735static void target_project_tri_clamp(float x[3])
736{
737 if (x[0] < 0.0f) {
738 x[0] = 0.0f;
739 }
740 if (x[1] < 0.0f) {
741 x[1] = 0.0f;
742 }
743 if (x[0] + x[1] > 1.0f) {
744 x[0] = x[0] / (x[0] + x[1]);
745 x[1] = 1.0f - x[0];
746 }
747}
748
749/* Correct the Newton's method step to keep the coordinates within the triangle. */
750static bool target_project_tri_correct(void * /*userdata*/,
751 const float x[3],
752 float step[3],
753 float x_next[3])
754{
755 /* Insignificant correction threshold */
756 const float epsilon = 1e-5f;
757 /* Dot product threshold for checking if step is 'clearly' pointing outside. */
758 const float dir_epsilon = 0.5f;
759 bool fixed = false, locked = false;
760
761 /* The barycentric coordinate domain is a triangle bounded by
762 * the X and Y axes, plus the x+y=1 diagonal. First, clamp the
763 * movement against the diagonal. Note that step is subtracted. */
764 float sum = x[0] + x[1];
765 float sstep = -(step[0] + step[1]);
766
767 if (sum + sstep > 1.0f) {
768 float ldist = 1.0f - sum;
769
770 /* If already at the boundary, slide along it. */
771 if (ldist < epsilon * float(M_SQRT2)) {
772 float step_len = len_v2(step);
773
774 /* Abort if the solution is clearly outside the domain. */
775 if (step_len > epsilon && sstep > step_len * dir_epsilon * float(M_SQRT2)) {
776 return false;
777 }
778
779 /* Project the new position onto the diagonal. */
780 add_v2_fl(step, (sum + sstep - 1.0f) * 0.5f);
781 fixed = locked = true;
782 }
783 else {
784 /* Scale a significant step down to arrive at the boundary. */
785 mul_v3_fl(step, ldist / sstep);
786 fixed = true;
787 }
788 }
789
790 /* Weight 0 and 1 boundary checks - along axis. */
791 for (int i = 0; i < 2; i++) {
792 if (step[i] > x[i]) {
793 /* If already at the boundary, slide along it. */
794 if (x[i] < epsilon) {
795 float step_len = len_v2(step);
796
797 /* Abort if the solution is clearly outside the domain. */
798 if (step_len > epsilon && (locked || step[i] > step_len * dir_epsilon)) {
799 return false;
800 }
801
802 /* Reset precision errors to stay at the boundary. */
803 step[i] = x[i];
804 fixed = true;
805 }
806 else {
807 /* Scale a significant step down to arrive at the boundary. */
808 mul_v3_fl(step, x[i] / step[i]);
809 fixed = true;
810 }
811 }
812 }
813
814 /* Recompute and clamp the new coordinates after step correction. */
815 if (fixed) {
816 sub_v3_v3v3(x_next, x, step);
818 }
819
820 return true;
821}
822
823static bool target_project_solve_point_tri(const float *vtri_co[3],
824 const float vtri_no[3][3],
825 const float point_co[3],
826 const float hit_co[3],
827 float hit_dist_sq,
828 float r_hit_co[3],
829 float r_hit_no[3])
830{
831 float x[3], tmp[3];
832 float dist = sqrtf(hit_dist_sq);
833 float magnitude_estimate = dist + len_manhattan_v3(vtri_co[0]) + len_manhattan_v3(vtri_co[1]) +
834 len_manhattan_v3(vtri_co[2]);
835 float epsilon = magnitude_estimate * 1.0e-6f;
836
837 /* Initial solution vector: barycentric weights plus distance along normal. */
838 interp_weights_tri_v3(x, UNPACK3(vtri_co), hit_co);
839
840 interp_v3_v3v3v3(r_hit_no, UNPACK3(vtri_no), x);
841 sub_v3_v3v3(tmp, point_co, hit_co);
842
843 x[2] = (dot_v3v3(tmp, r_hit_no) < 0) ? -dist : dist;
844
845 /* Solve the equations iteratively. */
846 TargetProjectTriData tri_data{};
847 tri_data.vtri_co = vtri_co;
848 tri_data.vtri_no = vtri_no;
849 tri_data.point_co = point_co;
850
851 sub_v3_v3v3(tri_data.n0_minus_n2, vtri_no[0], vtri_no[2]);
852 sub_v3_v3v3(tri_data.n1_minus_n2, vtri_no[1], vtri_no[2]);
853 sub_v3_v3v3(tri_data.c0_minus_c2, vtri_co[0], vtri_co[2]);
854 sub_v3_v3v3(tri_data.c1_minus_c2, vtri_co[1], vtri_co[2]);
855
857
858#ifdef TRACE_TARGET_PROJECT
859 const bool trace = true;
860#else
861 const bool trace = false;
862#endif
863
867 &tri_data,
868 epsilon,
869 20,
870 trace,
871 x,
872 x);
873
874 if (ok) {
875 copy_v3_v3(r_hit_co, tri_data.co_interp);
876 copy_v3_v3(r_hit_no, tri_data.no_interp);
877
878 return true;
879 }
880
881 return false;
882}
883
884static bool update_hit(BVHTreeNearest *nearest,
885 int index,
886 const float co[3],
887 const float hit_co[3],
888 const float hit_no[3])
889{
890 float dist_sq = len_squared_v3v3(hit_co, co);
891
892 if (dist_sq < nearest->dist_sq) {
893#ifdef TRACE_TARGET_PROJECT
894 printf(
895 "#=#=#> %d (%.3f,%.3f,%.3f) %g < %g\n", index, UNPACK3(hit_co), dist_sq, nearest->dist_sq);
896#endif
897 nearest->index = index;
898 nearest->dist_sq = dist_sq;
899 copy_v3_v3(nearest->co, hit_co);
900 normalize_v3_v3(nearest->no, hit_no);
901 return true;
902 }
903
904 return false;
905}
906
907/* Target projection on a non-manifold boundary edge -
908 * treats it like an infinitely thin cylinder. */
910 int index,
911 const float co[3],
912 BVHTreeNearest *nearest,
913 int eidx)
914{
915 const BVHTreeFromMesh *data = &tree->treeData;
916 const blender::int2 &edge = data->edges[eidx];
917 const float *vedge_co[2] = {data->vert_positions[edge[0]], data->vert_positions[edge[1]]};
918
919#ifdef TRACE_TARGET_PROJECT
920 printf("EDGE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f)\n",
921 eidx,
922 UNPACK3(vedge_co[0]),
923 UNPACK3(vedge_co[1]));
924#endif
925
926 /* Retrieve boundary vertex IDs */
927 const int *vert_boundary_id = tree->boundary->vert_boundary_id.data();
928 int bid1 = vert_boundary_id[edge[0]], bid2 = vert_boundary_id[edge[1]];
929
930 if (bid1 < 0 || bid2 < 0) {
931 return;
932 }
933
934 /* Retrieve boundary vertex normals and align them to direction. */
935 const ShrinkwrapBoundaryVertData *boundary_verts = tree->boundary->boundary_verts.data();
936 float vedge_dir[2][3], dir[3];
937
938 copy_v3_v3(vedge_dir[0], boundary_verts[bid1].normal_plane);
939 copy_v3_v3(vedge_dir[1], boundary_verts[bid2].normal_plane);
940
941 sub_v3_v3v3(dir, vedge_co[1], vedge_co[0]);
942
943 if (dot_v3v3(boundary_verts[bid1].direction, dir) < 0) {
944 negate_v3(vedge_dir[0]);
945 }
946 if (dot_v3v3(boundary_verts[bid2].direction, dir) < 0) {
947 negate_v3(vedge_dir[1]);
948 }
949
950 /* Solve a quadratic equation: lerp(d0,d1,x) * (co - lerp(v0,v1,x)) = 0 */
951 float d0v0 = dot_v3v3(vedge_dir[0], vedge_co[0]), d0v1 = dot_v3v3(vedge_dir[0], vedge_co[1]);
952 float d1v0 = dot_v3v3(vedge_dir[1], vedge_co[0]), d1v1 = dot_v3v3(vedge_dir[1], vedge_co[1]);
953 float d0co = dot_v3v3(vedge_dir[0], co);
954
955 float a = d0v1 - d0v0 + d1v0 - d1v1;
956 float b = 2 * d0v0 - d0v1 - d0co - d1v0 + dot_v3v3(vedge_dir[1], co);
957 float c = d0co - d0v0;
958 float det = b * b - 4 * a * c;
959
960 if (det >= 0) {
961 const float epsilon = 1e-6f;
962 float sdet = sqrtf(det);
963 float hit_co[3], hit_no[3];
964
965 for (int i = (det > 0 ? 2 : 0); i >= 0; i -= 2) {
966 float x = (-b + (float(i) - 1) * sdet) / (2 * a);
967
968 if (x >= -epsilon && x <= 1.0f + epsilon) {
969 CLAMP(x, 0, 1);
970
971 float vedge_no[2][3];
972 copy_v3_v3(vedge_no[0], tree->vert_normals[edge[0]]);
973 copy_v3_v3(vedge_no[1], tree->vert_normals[edge[1]]);
974
975 interp_v3_v3v3(hit_co, vedge_co[0], vedge_co[1], x);
976 interp_v3_v3v3(hit_no, vedge_no[0], vedge_no[1], x);
977
978 update_hit(nearest, index, co, hit_co, hit_no);
979 }
980 }
981 }
982}
983
984/* Target normal projection BVH callback - based on mesh_corner_tri_nearest_point. */
985static void mesh_corner_tris_target_project(void *userdata,
986 int index,
987 const float co[3],
988 BVHTreeNearest *nearest)
989{
990 using namespace blender;
991 const ShrinkwrapTreeData *tree = (ShrinkwrapTreeData *)userdata;
992 const BVHTreeFromMesh *data = &tree->treeData;
993 const int3 &tri = data->corner_tris[index];
994 const int tri_verts[3] = {
995 data->corner_verts[tri[0]],
996 data->corner_verts[tri[1]],
997 data->corner_verts[tri[2]],
998 };
999 const float *vtri_co[3] = {
1000 data->vert_positions[tri_verts[0]],
1001 data->vert_positions[tri_verts[1]],
1002 data->vert_positions[tri_verts[2]],
1003 };
1004 float raw_hit_co[3], hit_co[3], hit_no[3], dist_sq, vtri_no[3][3];
1005
1006 /* First find the closest point and bail out if it's worse than the current solution. */
1007 closest_on_tri_to_point_v3(raw_hit_co, co, UNPACK3(vtri_co));
1008 dist_sq = len_squared_v3v3(co, raw_hit_co);
1009
1010#ifdef TRACE_TARGET_PROJECT
1011 printf("TRIANGLE %d (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) (%.3f,%.3f,%.3f) %g %g\n",
1012 index,
1013 UNPACK3(vtri_co[0]),
1014 UNPACK3(vtri_co[1]),
1015 UNPACK3(vtri_co[2]),
1016 dist_sq,
1017 nearest->dist_sq);
1018#endif
1019
1020 if (dist_sq >= nearest->dist_sq) {
1021 return;
1022 }
1023
1024 /* Decode normals */
1025 copy_v3_v3(vtri_no[0], tree->vert_normals[tri_verts[0]]);
1026 copy_v3_v3(vtri_no[1], tree->vert_normals[tri_verts[1]]);
1027 copy_v3_v3(vtri_no[2], tree->vert_normals[tri_verts[2]]);
1028
1029 /* Solve the equations for the triangle */
1030 if (target_project_solve_point_tri(vtri_co, vtri_no, co, raw_hit_co, dist_sq, hit_co, hit_no)) {
1031 update_hit(nearest, index, co, hit_co, hit_no);
1032 }
1033 /* Boundary edges */
1034 else if (tree->boundary && tree->boundary->has_boundary() &&
1035 tree->boundary->tri_has_boundary[index])
1036 {
1037 const BitSpan is_boundary = tree->boundary->edge_is_boundary;
1038 const int3 edges = bke::mesh::corner_tri_get_real_edges(
1039 data->edges, data->corner_verts, tree->corner_edges, tri);
1040
1041 for (int i = 0; i < 3; i++) {
1042 if (edges[i] >= 0 && is_boundary[edges[i]]) {
1043 target_project_edge(tree, index, co, nearest, edges[i]);
1044 }
1045 }
1046 }
1047}
1048
1050 BVHTreeNearest *nearest,
1051 float co[3],
1052 int type)
1053{
1054 BVHTreeFromMesh *treeData = &tree->treeData;
1055
1056 if (type == MOD_SHRINKWRAP_TARGET_PROJECT) {
1057#ifdef TRACE_TARGET_PROJECT
1058 printf("\n====== TARGET PROJECT START ======\n");
1059#endif
1060
1063
1064#ifdef TRACE_TARGET_PROJECT
1065 printf("====== TARGET PROJECT END: %d %g ======\n\n", nearest->index, nearest->dist_sq);
1066#endif
1067
1068 if (nearest->index < 0) {
1069 /* fallback to simple nearest */
1070 BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
1071 }
1072 }
1073 else {
1074 BLI_bvhtree_find_nearest(tree->bvh, co, nearest, treeData->nearest_callback, treeData);
1075 }
1076}
1077
1084static void shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdata,
1085 const int i,
1086 const TaskParallelTLS *__restrict tls)
1087{
1088 ShrinkwrapCalcCBData *data = static_cast<ShrinkwrapCalcCBData *>(userdata);
1089
1090 ShrinkwrapCalcData *calc = data->calc;
1091 BVHTreeNearest *nearest = static_cast<BVHTreeNearest *>(tls->userdata_chunk);
1092
1093 float *co = calc->vertexCos[i];
1094 float tmp_co[3];
1095 float weight = BKE_defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
1096
1097 if (calc->invert_vgroup) {
1098 weight = 1.0f - weight;
1099 }
1100
1101 if (weight == 0.0f) {
1102 return;
1103 }
1104
1105 /* Convert the vertex to tree coordinates */
1106 if (calc->vert_positions) {
1107 copy_v3_v3(tmp_co, calc->vert_positions[i]);
1108 }
1109 else {
1110 copy_v3_v3(tmp_co, co);
1111 }
1113
1114 /* Use local proximity heuristics (to reduce the nearest search)
1115 *
1116 * If we already had an hit before.. we assume this vertex is going to have a close hit to that
1117 * other vertex so we can initiate the "nearest.dist" with the expected value to that last hit.
1118 * This will lead in pruning of the search tree. */
1119 if (nearest->index != -1) {
1121 /* Heuristic doesn't work because of additional restrictions. */
1122 nearest->index = -1;
1123 nearest->dist_sq = FLT_MAX;
1124 }
1125 else {
1126 nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co);
1127 }
1128 }
1129 else {
1130 nearest->dist_sq = FLT_MAX;
1131 }
1132
1133 BKE_shrinkwrap_find_nearest_surface(data->tree, nearest, tmp_co, calc->smd->shrinkType);
1134
1135 /* Found the nearest vertex */
1136 if (nearest->index != -1) {
1138 nullptr,
1139 calc->smd->shrinkMode,
1140 nearest->index,
1141 nearest->co,
1142 nearest->no,
1143 calc->keepDist,
1144 tmp_co,
1145 tmp_co);
1146
1147 /* Convert the coordinates back to mesh coordinates */
1149 interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
1150 }
1151}
1152
1154 const SpaceTransform *transform,
1155 int corner_tri_idx,
1156 const float hit_co[3],
1157 const float hit_no[3],
1158 float r_no[3])
1159{
1160 using namespace blender;
1161 const BVHTreeFromMesh *treeData = &tree->treeData;
1162 const int3 &tri = treeData->corner_tris[corner_tri_idx];
1163 const int face_i = tree->mesh->corner_tri_faces()[corner_tri_idx];
1164
1165 /* Interpolate smooth normals if enabled. */
1166 if (tree->sharp_faces.is_empty() || tree->sharp_faces[face_i]) {
1167 const int vert_indices[3] = {treeData->corner_verts[tri[0]],
1168 treeData->corner_verts[tri[1]],
1169 treeData->corner_verts[tri[2]]};
1170 float w[3], no[3][3], tmp_co[3];
1171
1172 /* Custom and auto smooth split normals. */
1173 if (!tree->corner_normals.is_empty()) {
1174 copy_v3_v3(no[0], tree->corner_normals[tri[0]]);
1175 copy_v3_v3(no[1], tree->corner_normals[tri[1]]);
1176 copy_v3_v3(no[2], tree->corner_normals[tri[2]]);
1177 }
1178 /* Ordinary vertex normals. */
1179 else {
1180 copy_v3_v3(no[0], tree->vert_normals[vert_indices[0]]);
1181 copy_v3_v3(no[1], tree->vert_normals[vert_indices[1]]);
1182 copy_v3_v3(no[2], tree->vert_normals[vert_indices[2]]);
1183 }
1184
1185 /* Barycentric weights from hit point. */
1186 copy_v3_v3(tmp_co, hit_co);
1187
1188 if (transform) {
1189 BLI_space_transform_apply(transform, tmp_co);
1190 }
1191
1193 treeData->vert_positions[vert_indices[0]],
1194 treeData->vert_positions[vert_indices[1]],
1195 treeData->vert_positions[vert_indices[2]],
1196 tmp_co);
1197
1198 /* Interpolate using weights. */
1199 interp_v3_v3v3v3(r_no, no[0], no[1], no[2], w);
1200
1201 if (transform) {
1202 BLI_space_transform_invert_normal(transform, r_no);
1203 }
1204 else {
1205 normalize_v3(r_no);
1206 }
1207 }
1208 /* Use the face normal if flat. */
1209 else if (!tree->face_normals.is_empty()) {
1210 copy_v3_v3(r_no, tree->face_normals[face_i]);
1211 if (transform) {
1212 BLI_space_transform_invert_normal(transform, r_no);
1213 }
1214 }
1215 /* Finally fallback to the corner_tris normal. */
1216 else {
1217 copy_v3_v3(r_no, hit_no);
1218 }
1219}
1220
1221/* Helper for MOD_SHRINKWRAP_INSIDE, MOD_SHRINKWRAP_OUTSIDE and MOD_SHRINKWRAP_OUTSIDE_SURFACE. */
1222static void shrinkwrap_snap_with_side(float r_point_co[3],
1223 const float point_co[3],
1224 const float hit_co[3],
1225 const float hit_no[3],
1226 float goal_dist,
1227 float forcesign,
1228 bool forcesnap)
1229{
1230 float delta[3];
1231 sub_v3_v3v3(delta, point_co, hit_co);
1232
1233 float dist = len_v3(delta);
1234
1235 /* If exactly on the surface, push out along normal */
1236 if (dist < FLT_EPSILON) {
1237 if (forcesnap || goal_dist > 0) {
1238 madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist * forcesign);
1239 }
1240 else {
1241 copy_v3_v3(r_point_co, hit_co);
1242 }
1243 }
1244 /* Move to the correct side if needed */
1245 else {
1246 float dsign = signf(dot_v3v3(delta, hit_no));
1247
1248 if (forcesign == 0.0f) {
1249 forcesign = dsign;
1250 }
1251
1252 /* If on the wrong side or too close, move to correct */
1253 if (forcesnap || dsign * dist * forcesign < goal_dist) {
1254 mul_v3_fl(delta, dsign / dist);
1255
1256 /* At very small distance, blend in the hit normal to stabilize math. */
1257 float dist_epsilon = (fabsf(goal_dist) + len_manhattan_v3(hit_co)) * 1e-4f;
1258
1259 if (dist < dist_epsilon) {
1260#ifdef TRACE_TARGET_PROJECT
1261 printf("zero_factor %g = %g / %g\n", dist / dist_epsilon, dist, dist_epsilon);
1262#endif
1263
1264 interp_v3_v3v3(delta, hit_no, delta, dist / dist_epsilon);
1265 }
1266
1267 madd_v3_v3v3fl(r_point_co, hit_co, delta, goal_dist * forcesign);
1268 }
1269 else {
1270 copy_v3_v3(r_point_co, point_co);
1271 }
1272 }
1273}
1274
1276 const SpaceTransform *transform,
1277 int mode,
1278 int hit_idx,
1279 const float hit_co[3],
1280 const float hit_no[3],
1281 float goal_dist,
1282 const float point_co[3],
1283 float r_point_co[3])
1284{
1285 float tmp[3];
1286
1287 switch (mode) {
1288 /* Offsets along the line between point_co and hit_co. */
1290 if (goal_dist != 0) {
1291 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, 0, true);
1292 }
1293 else {
1294 copy_v3_v3(r_point_co, hit_co);
1295 }
1296 break;
1297
1299 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, -1, false);
1300 break;
1301
1303 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, false);
1304 break;
1305
1307 if (goal_dist != 0) {
1308 shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, true);
1309 }
1310 else {
1311 copy_v3_v3(r_point_co, hit_co);
1312 }
1313 break;
1314
1315 /* Offsets along the normal */
1317 if (goal_dist != 0) {
1318 BKE_shrinkwrap_compute_smooth_normal(tree, transform, hit_idx, hit_co, hit_no, tmp);
1319 madd_v3_v3v3fl(r_point_co, hit_co, tmp, goal_dist);
1320 }
1321 else {
1322 copy_v3_v3(r_point_co, hit_co);
1323 }
1324 break;
1325
1326 default:
1327 printf("Unknown Shrinkwrap surface snap mode: %d\n", mode);
1328 copy_v3_v3(r_point_co, hit_co);
1329 }
1330}
1331
1333{
1335
1336 /* Setup nearest */
1337 nearest.index = -1;
1338 nearest.dist_sq = FLT_MAX;
1339
1340 /* Find the nearest vertex */
1341 ShrinkwrapCalcCBData data{};
1342 data.calc = calc;
1343 data.tree = calc->tree;
1344 TaskParallelSettings settings;
1346 settings.use_threading = (calc->numVerts > 10000);
1347 settings.userdata_chunk = &nearest;
1348 settings.userdata_chunk_size = sizeof(nearest);
1350 0, calc->numVerts, &data, shrinkwrap_calc_nearest_surface_point_cb_ex, &settings);
1351}
1352
1354 const ModifierEvalContext *ctx,
1355 Scene *scene,
1356 Object *ob,
1357 Mesh *mesh,
1358 const MDeformVert *dvert,
1359 const int defgrp_index,
1360 float (*vertexCos)[3],
1361 int numVerts)
1362{
1363
1364 DerivedMesh *ss_mesh = nullptr;
1366
1367 /* remove loop dependencies on derived meshes (TODO should this be done elsewhere?) */
1368 if (smd->target == ob) {
1369 smd->target = nullptr;
1370 }
1371 if (smd->auxTarget == ob) {
1372 smd->auxTarget = nullptr;
1373 }
1374
1375 /* Configure Shrinkwrap calc data */
1376 calc.smd = smd;
1377 calc.ob = ob;
1378 calc.numVerts = numVerts;
1379 calc.vertexCos = vertexCos;
1380 calc.dvert = dvert;
1381 calc.vgroup = defgrp_index;
1383
1384 if (smd->target != nullptr) {
1385 Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target);
1387
1388 /* TODO: there might be several "bugs" with non-uniform scales matrices
1389 * because it will no longer be nearest surface, not sphere projection
1390 * because space has been deformed */
1391 BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob, ob_target);
1392
1393 /* TODO: smd->keepDist is in global units.. must change to local */
1394 calc.keepDist = smd->keepDist;
1395 }
1397
1398 if (mesh != nullptr && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) {
1399 /* Setup arrays to get vertex positions, normals and deform weights */
1400 calc.vert_positions = reinterpret_cast<float(*)[3]>(mesh->vert_positions_for_write().data());
1401 calc.vert_normals = mesh->vert_normals();
1402
1403 /* Using vertices positions/normals as if a subsurface was applied */
1404 if (smd->subsurfLevels) {
1405 SubsurfModifierData ssmd = {{nullptr}};
1406 ssmd.subdivType = ME_CC_SUBSURF; /* catmull clark */
1407 ssmd.levels = smd->subsurfLevels; /* levels */
1408
1409 /* TODO: to be moved to Mesh once we are done with changes in subsurf code. */
1410 DerivedMesh *dm = CDDM_from_mesh(mesh);
1411
1413 dm,
1414 &ssmd,
1415 scene,
1416 nullptr,
1418
1419 if (ss_mesh) {
1420 calc.vert_positions = reinterpret_cast<float(*)[3]>(ss_mesh->getVertArray(ss_mesh));
1421 if (calc.vert_positions) {
1422 /* TRICKY: this code assumes subsurface will have the transformed original vertices
1423 * in their original order at the end of the vert array. */
1424 calc.vert_positions = calc.vert_positions + ss_mesh->getNumVerts(ss_mesh) -
1425 dm->getNumVerts(dm);
1426 }
1427 }
1428
1429 /* Just to make sure we are not leaving any memory behind */
1430 BLI_assert(ssmd.emCache == nullptr);
1431 BLI_assert(ssmd.mCache == nullptr);
1432
1433 dm->release(dm);
1434 }
1435 }
1436
1437 /* Projecting target defined - lets work! */
1439
1440 if (BKE_shrinkwrap_init_tree(&tree, calc.target, smd->shrinkType, smd->shrinkMode, false)) {
1441 calc.tree = &tree;
1442
1443 switch (smd->shrinkType) {
1447 break;
1448
1450 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
1451 break;
1452
1454 TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), deform_vertex);
1455 break;
1456 }
1457
1459 }
1460
1461 /* free memory */
1462 if (ss_mesh) {
1463 ss_mesh->release(ss_mesh);
1464 }
1465}
1466
1468 Object *ob,
1469 MDeformVert *dvert,
1470 const int defgrp_index,
1471 float (*vertexCos)[3],
1472 int numVerts)
1473{
1474
1476 /* Convert gpencil struct to use the same struct and function used with meshes. */
1478 smd.target = mmd->target;
1479 smd.auxTarget = mmd->aux_target;
1480 smd.keepDist = mmd->keep_dist;
1481 smd.shrinkType = mmd->shrink_type;
1482 smd.shrinkOpts = mmd->shrink_opts;
1483 smd.shrinkMode = mmd->shrink_mode;
1484 smd.projLimit = mmd->proj_limit;
1485 smd.projAxis = mmd->proj_axis;
1486
1487 /* Configure Shrinkwrap calc data. */
1488 calc.smd = &smd;
1489 calc.ob = ob;
1490 calc.numVerts = numVerts;
1491 calc.vertexCos = vertexCos;
1492 calc.dvert = dvert;
1493 calc.vgroup = defgrp_index;
1494 calc.invert_vgroup = (mmd->flag & GP_SHRINKWRAP_INVERT_VGROUP) != 0;
1495
1497 calc.keepDist = mmd->keep_dist;
1498 calc.tree = mmd->cache_data;
1499
1500 switch (mmd->shrink_type) {
1503 TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), gpdeform_surface);
1504 break;
1505
1507 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), gpdeform_project);
1508 break;
1509
1511 TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), gpdeform_vertex);
1512 break;
1513 }
1514}
1515
1517 Object &object,
1519 const blender::Span<MDeformVert> dvert,
1520 const int defgrp_index,
1522{
1523 using namespace blender::bke;
1524
1526 /* Convert params struct to use the same struct and function used with meshes. */
1528 smd.target = params.target;
1529 smd.auxTarget = params.aux_target;
1530 smd.keepDist = params.keep_distance;
1531 smd.shrinkType = params.shrink_type;
1532 smd.shrinkOpts = params.shrink_options;
1533 smd.shrinkMode = params.shrink_mode;
1534 smd.projLimit = params.projection_limit;
1535 smd.projAxis = params.projection_axis;
1536
1537 /* Configure Shrinkwrap calc data. */
1538 calc.smd = &smd;
1539 calc.ob = &object;
1540 calc.numVerts = int(positions.size());
1541 calc.vertexCos = reinterpret_cast<float(*)[3]>(positions.data());
1542 calc.dvert = dvert.is_empty() ? nullptr : dvert.data();
1543 calc.vgroup = defgrp_index;
1544 calc.invert_vgroup = params.invert_vertex_weights;
1545
1546 BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, &object, params.target);
1547 calc.keepDist = params.keep_distance;
1548 calc.tree = &tree;
1549
1550 switch (params.shrink_type) {
1553 TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), gpdeform_surface);
1554 break;
1555
1557 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), gpdeform_project);
1558 break;
1559
1561 TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), gpdeform_vertex);
1562 break;
1563 }
1564}
1565
1567 Scene *scene,
1568 Object *ob_source,
1569 Object *ob_target)
1570{
1571 ShrinkwrapModifierData ssmd = {{nullptr}};
1572 ModifierEvalContext ctx = {depsgraph, ob_source, ModifierApplyFlag(0)};
1573
1574 ssmd.target = ob_target;
1577 ssmd.keepDist = 0.0f;
1578
1579 Mesh *src_me = static_cast<Mesh *>(ob_source->data);
1580
1582 &ssmd,
1583 &ctx,
1584 scene,
1585 ob_source,
1586 src_me,
1587 nullptr,
1588 -1,
1589 reinterpret_cast<float(*)[3]>(src_me->vert_positions_for_write().data()),
1590 src_me->verts_num);
1591 src_me->tag_positions_changed();
1592}
1593
1594void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object *ob_target)
1595{
1596 ShrinkwrapModifierData ssmd = {{nullptr}};
1597
1598 ssmd.target = ob_target;
1602 ssmd.keepDist = 0.0f;
1603
1604 /* Tolerance value to prevent artifacts on sharp edges of a mesh.
1605 * This constant and based on experimenting with different values. */
1606 const float projLimitTolerance = 5.0f;
1607 ssmd.projLimit = target_me->remesh_voxel_size * projLimitTolerance;
1608
1610
1611 calc.smd = &ssmd;
1612 calc.numVerts = src_me->verts_num;
1613 calc.vertexCos = reinterpret_cast<float(*)[3]>(src_me->vert_positions_for_write().data());
1614 calc.vert_normals = src_me->vert_normals();
1615 calc.vgroup = -1;
1616 calc.target = target_me;
1617 calc.keepDist = ssmd.keepDist;
1618 calc.vert_positions = reinterpret_cast<float(*)[3]>(src_me->vert_positions_for_write().data());
1619 BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob_target, ob_target);
1620
1622 if (BKE_shrinkwrap_init_tree(&tree, calc.target, ssmd.shrinkType, ssmd.shrinkMode, false)) {
1623 calc.tree = &tree;
1624 TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), deform_project);
1626 }
1627
1628 src_me->tag_positions_changed();
1629}
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
@ BVHTREE_FROM_CORNER_TRIS
@ BVHTREE_FROM_VERTS
support for deformation groups and hooks.
float BKE_defvert_array_find_weight_safe(const MDeformVert *dvert, int index, int defgroup)
Definition deform.cc:776
DerivedMesh * CDDM_from_mesh(Mesh *mesh)
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
SubsurfFlags
@ SUBSURF_IN_EDIT_MODE
DerivedMesh * subsurf_make_derived_from_derived(DerivedMesh *dm, SubsurfModifierData *smd, const Scene *scene, float(*vertCos)[3], SubsurfFlags flags)
#define BLI_assert(a)
Definition BLI_assert.h:50
@ BVH_NEAREST_OPTIMAL_ORDER
Definition BLI_kdopbvh.h:85
#define BVH_RAYCAST_DIST_MAX
Definition BLI_kdopbvh.h:92
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)
int BLI_bvhtree_find_nearest_ex(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata, int flag)
#define M_SQRT2
MINLINE float signf(float f)
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)
Definition math_vector.c:36
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:230
Utility defines for timing/benchmarks.
#define CLAMP(a, b, c)
#define UNPACK3(a)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ GP_SHRINKWRAP_INVERT_VGROUP
@ ME_CC_SUBSURF
@ MOD_SHRINKWRAP_TARGET_PROJECT
@ MOD_SHRINKWRAP_NEAREST_VERTEX
@ MOD_SHRINKWRAP_PROJECT
@ MOD_SHRINKWRAP_NEAREST_SURFACE
#define MOD_SHRINKWRAP_CULL_TARGET_MASK
@ 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
@ MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS
@ MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS
@ MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS
@ MOD_SHRINKWRAP_PROJECT_OVER_NORMAL
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
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:301
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
GAttributeReader lookup(const StringRef attribute_id) const
local_group_size(16, 16) .push_constant(Type b
#define printf
CCL_NAMESPACE_BEGIN struct Options options
const Depsgraph * depsgraph
#define fabsf(x)
#define sqrtf(x)
KDTree_3d * tree
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
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)
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_free_tree(ShrinkwrapTreeData *data)
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 shrinkwrapGpencilModifier_deform(ShrinkwrapGpencilModifierData *mmd, Object *ob, MDeformVert *dvert, const int defgrp_index, float(*vertexCos)[3], int numVerts)
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
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)
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])
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
__int64 int64_t
Definition stdint.h:89
blender::Span< blender::int3 > corner_tris
blender::Span< blender::float3 > vert_positions
blender::Span< int > corner_verts
BVHTree_NearestPointCallback nearest_callback
float co[3]
Definition BLI_kdopbvh.h:69
float no[3]
Definition BLI_kdopbvh.h:71
int(* getNumVerts)(DerivedMesh *dm)
float *(* getVertArray)(DerivedMesh *dm)
void(* release)(DerivedMesh *dm)
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]