Blender V4.5
meshlaplacian.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include "MEM_guardedalloc.h"
11
12#include "DNA_mesh_types.h"
13#include "DNA_object_types.h"
14
15#include "BLI_map.hh"
16#include "BLI_math_geom.h"
17#include "BLI_math_matrix.h"
18#include "BLI_math_rotation.h"
19#include "BLI_math_vector.h"
20#include "BLI_memarena.h"
21#include "BLI_ordered_edge.hh"
22#include "BLI_string.h"
23
24#include "BLT_translation.hh"
25
26#include "BKE_attribute.hh"
27#include "BKE_bvhutils.hh"
28#include "BKE_mesh.hh"
29#include "BKE_mesh_wrapper.hh"
30#include "BKE_modifier.hh"
31
32#include "ED_armature.hh"
33#include "ED_mesh.hh"
34#include "ED_object_vgroup.hh"
35
36#include "eigen_capi.h"
37
38#include "meshlaplacian.h"
39
40#include <algorithm>
41
42/* ************* XXX *************** */
43static void waitcursor(int /*val*/) {}
44static void progress_bar(int /*dummy_val*/, const char * /*dummy*/) {}
45static void start_progress_bar() {}
46static void end_progress_bar() {}
47static void error(const char *str)
48{
49 printf("error: %s\n", str);
50}
51/* ************* XXX *************** */
52
53/************************** Laplacian System *****************************/
54
56 LinearSolver *context; /* linear solver */
57
59
60 float **verts; /* vertex coordinates */
61 float *varea; /* vertex weights for laplacian computation */
62 char *vpinned; /* vertex pinning */
63 int (*faces)[3]; /* face vertex indices */
64 float (*fweights)[3]; /* cotangent weights per face */
65
66 int areaweights; /* use area in cotangent weights? */
67 int storeweights; /* store cotangent weights in fweights */
68 bool variablesdone; /* variables set in linear system */
69
70 blender::Map<blender::OrderedEdge, int> edgehash; /* edge hash for construction */
71
74 blender::Span<int> corner_verts; /* needed to find vertices by index */
77 float (*verts)[3]; /* vertex coordinates */
78 float (*vert_normals)[3]; /* vertex normals */
79
80 float (*root)[3]; /* bone root */
81 float (*tip)[3]; /* bone tip */
83
84 float *H; /* diagonal H matrix */
85 float *p; /* values from all p vectors */
86 float *mindist; /* minimum distance to a bone for all vertices */
87
88 BVHTree *bvhtree; /* ray tracing acceleration structure */
89 const blender::int3 **vltree; /* a corner_tri that the vertex belongs to */
91};
92
93/* Laplacian matrix construction */
94
95/* Computation of these weights for the laplacian is based on:
96 * "Discrete Differential-Geometry Operators for Triangulated 2-Manifolds",
97 * Meyer et al, 2002. Section 3.5, formula (8).
98 *
99 * We do it a bit different by going over faces instead of going over each
100 * vertex and adjacent faces, since we don't store this adjacency. Also, the
101 * formulas are tweaked a bit to work for non-manifold meshes. */
102
104 int v1,
105 int v2)
106{
107 edgehash.add_or_modify(
108 {v1, v2}, [](int *value) { *value = 1; }, [](int *value) { (*value)++; });
109}
110
112 int v1,
113 int v2)
114{
115 return edgehash.lookup({v1, v2});
116}
117
118static void laplacian_triangle_area(LaplacianSystem *sys, int i1, int i2, int i3)
119{
120 float t1, t2, t3, len1, len2, len3, area;
121 float *varea = sys->varea, *v1, *v2, *v3;
122 int obtuse = 0;
123
124 v1 = sys->verts[i1];
125 v2 = sys->verts[i2];
126 v3 = sys->verts[i3];
127
128 t1 = cotangent_tri_weight_v3(v1, v2, v3);
129 t2 = cotangent_tri_weight_v3(v2, v3, v1);
130 t3 = cotangent_tri_weight_v3(v3, v1, v2);
131
132 if (angle_v3v3v3(v2, v1, v3) > DEG2RADF(90.0f)) {
133 obtuse = 1;
134 }
135 else if (angle_v3v3v3(v1, v2, v3) > DEG2RADF(90.0f)) {
136 obtuse = 2;
137 }
138 else if (angle_v3v3v3(v1, v3, v2) > DEG2RADF(90.0f)) {
139 obtuse = 3;
140 }
141
142 if (obtuse > 0) {
143 area = area_tri_v3(v1, v2, v3);
144
145 varea[i1] += (obtuse == 1) ? area : area * 0.5f;
146 varea[i2] += (obtuse == 2) ? area : area * 0.5f;
147 varea[i3] += (obtuse == 3) ? area : area * 0.5f;
148 }
149 else {
150 len1 = len_v3v3(v2, v3);
151 len2 = len_v3v3(v1, v3);
152 len3 = len_v3v3(v1, v2);
153
154 t1 *= len1 * len1;
155 t2 *= len2 * len2;
156 t3 *= len3 * len3;
157
158 varea[i1] += (t2 + t3) * 0.25f;
159 varea[i2] += (t1 + t3) * 0.25f;
160 varea[i3] += (t1 + t2) * 0.25f;
161 }
162}
163
164static void laplacian_triangle_weights(LaplacianSystem *sys, int f, int i1, int i2, int i3)
165{
166 float t1, t2, t3;
167 float *varea = sys->varea, *v1, *v2, *v3;
168
169 v1 = sys->verts[i1];
170 v2 = sys->verts[i2];
171 v3 = sys->verts[i3];
172
173 /* instead of *0.5 we divided by the number of faces of the edge, it still
174 * needs to be verified that this is indeed the correct thing to do! */
175 t1 = cotangent_tri_weight_v3(v1, v2, v3) / laplacian_edge_count(sys->edgehash, i2, i3);
176 t2 = cotangent_tri_weight_v3(v2, v3, v1) / laplacian_edge_count(sys->edgehash, i3, i1);
177 t3 = cotangent_tri_weight_v3(v3, v1, v2) / laplacian_edge_count(sys->edgehash, i1, i2);
178
179 EIG_linear_solver_matrix_add(sys->context, i1, i1, (t2 + t3) * varea[i1]);
180 EIG_linear_solver_matrix_add(sys->context, i2, i2, (t1 + t3) * varea[i2]);
181 EIG_linear_solver_matrix_add(sys->context, i3, i3, (t1 + t2) * varea[i3]);
182
183 EIG_linear_solver_matrix_add(sys->context, i1, i2, -t3 * varea[i1]);
184 EIG_linear_solver_matrix_add(sys->context, i2, i1, -t3 * varea[i2]);
185
186 EIG_linear_solver_matrix_add(sys->context, i2, i3, -t1 * varea[i2]);
187 EIG_linear_solver_matrix_add(sys->context, i3, i2, -t1 * varea[i3]);
188
189 EIG_linear_solver_matrix_add(sys->context, i3, i1, -t2 * varea[i3]);
190 EIG_linear_solver_matrix_add(sys->context, i1, i3, -t2 * varea[i1]);
191
192 if (sys->storeweights) {
193 sys->fweights[f][0] = t1 * varea[i1];
194 sys->fweights[f][1] = t2 * varea[i2];
195 sys->fweights[f][2] = t3 * varea[i3];
196 }
197}
198
199static LaplacianSystem *laplacian_system_construct_begin(int verts_num, int faces_num, int lsq)
200{
201 LaplacianSystem *sys;
202
203 sys = MEM_new<LaplacianSystem>(__func__);
204
205 sys->verts = MEM_calloc_arrayN<float *>(verts_num, "LaplacianSystemVerts");
206 sys->vpinned = MEM_calloc_arrayN<char>(verts_num, "LaplacianSystemVpinned");
207 sys->faces = static_cast<int(*)[3]>(
208 MEM_callocN(sizeof(int[3]) * faces_num, "LaplacianSystemFaces"));
209
210 sys->verts_num = 0;
211 sys->faces_num = 0;
212
213 sys->areaweights = 1;
214 sys->storeweights = 0;
215
216 /* create linear solver */
217 if (lsq) {
218 sys->context = EIG_linear_least_squares_solver_new(0, verts_num, 1);
219 }
220 else {
221 sys->context = EIG_linear_solver_new(0, verts_num, 1);
222 }
223
224 return sys;
225}
226
227void laplacian_add_vertex(LaplacianSystem *sys, float *co, int pinned)
228{
229 sys->verts[sys->verts_num] = co;
230 sys->vpinned[sys->verts_num] = pinned;
231 sys->verts_num++;
232}
233
234void laplacian_add_triangle(LaplacianSystem *sys, int v1, int v2, int v3)
235{
236 sys->faces[sys->faces_num][0] = v1;
237 sys->faces[sys->faces_num][1] = v2;
238 sys->faces[sys->faces_num][2] = v3;
239 sys->faces_num++;
240}
241
243{
244 int(*face)[3];
245 int a, verts_num = sys->verts_num, faces_num = sys->faces_num;
246
247 laplacian_begin_solve(sys, 0);
248
249 sys->varea = MEM_calloc_arrayN<float>(verts_num, "LaplacianSystemVarea");
250
251 sys->edgehash.reserve(sys->faces_num);
252 for (a = 0, face = sys->faces; a < sys->faces_num; a++, face++) {
253 laplacian_increase_edge_count(sys->edgehash, (*face)[0], (*face)[1]);
254 laplacian_increase_edge_count(sys->edgehash, (*face)[1], (*face)[2]);
255 laplacian_increase_edge_count(sys->edgehash, (*face)[2], (*face)[0]);
256 }
257
258 if (sys->areaweights) {
259 for (a = 0, face = sys->faces; a < sys->faces_num; a++, face++) {
260 laplacian_triangle_area(sys, (*face)[0], (*face)[1], (*face)[2]);
261 }
262 }
263
264 for (a = 0; a < verts_num; a++) {
265 if (sys->areaweights) {
266 if (sys->varea[a] != 0.0f) {
267 sys->varea[a] = 0.5f / sys->varea[a];
268 }
269 }
270 else {
271 sys->varea[a] = 1.0f;
272 }
273
274 /* for heat weighting */
275 if (sys->heat.H) {
276 EIG_linear_solver_matrix_add(sys->context, a, a, sys->heat.H[a]);
277 }
278 }
279
280 if (sys->storeweights) {
281 sys->fweights = static_cast<float(*)[3]>(
282 MEM_callocN(sizeof(float[3]) * faces_num, "LaplacianFWeight"));
283 }
284
285 for (a = 0, face = sys->faces; a < faces_num; a++, face++) {
286 laplacian_triangle_weights(sys, a, (*face)[0], (*face)[1], (*face)[2]);
287 }
288
289 MEM_freeN(sys->faces);
290 sys->faces = nullptr;
291
292 MEM_SAFE_FREE(sys->varea);
293}
294
296{
297 if (sys->verts) {
298 MEM_freeN(sys->verts);
299 }
300 if (sys->varea) {
301 MEM_freeN(sys->varea);
302 }
303 if (sys->vpinned) {
304 MEM_freeN(sys->vpinned);
305 }
306 if (sys->faces) {
307 MEM_freeN(sys->faces);
308 }
309 if (sys->fweights) {
310 MEM_freeN(sys->fweights);
311 }
312
314 MEM_delete(sys);
315}
316
318{
319 int a;
320
321 if (!sys->variablesdone) {
322 if (index >= 0) {
323 for (a = 0; a < sys->verts_num; a++) {
324 if (sys->vpinned[a]) {
325 EIG_linear_solver_variable_set(sys->context, 0, a, sys->verts[a][index]);
327 }
328 }
329 }
330
331 sys->variablesdone = true;
332 }
333}
334
336{
338}
339
341{
342 sys->variablesdone = false;
343
344 // EIG_linear_solver_print_matrix(sys->context, );
345
346 return EIG_linear_solver_solve(sys->context);
347}
348
353
354/************************* Heat Bone Weighting ******************************/
355/* From "Automatic Rigging and Animation of 3D Characters"
356 * Ilya Baran and Jovan Popovic, SIGGRAPH 2007 */
357
358#define C_WEIGHT 1.0f
359#define WEIGHT_LIMIT_START 0.05f
360#define WEIGHT_LIMIT_END 0.025f
361#define DISTANCE_EPSILON 1e-4f
362
364 float start[3];
365 float vec[3];
367};
368
369static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
370{
371 BVHCallbackUserData *data = static_cast<BVHCallbackUserData *>(userdata);
372 const blender::int3 &tri = data->sys->heat.corner_tris[index];
373 const blender::Span<int> corner_verts = data->sys->heat.corner_verts;
374 float(*verts)[3] = data->sys->heat.verts;
375 const float *vtri_co[3];
376 float dist_test;
377
378 vtri_co[0] = verts[corner_verts[tri[0]]];
379 vtri_co[1] = verts[corner_verts[tri[1]]];
380 vtri_co[2] = verts[corner_verts[tri[2]]];
381
382#ifdef USE_KDOPBVH_WATERTIGHT
384 data->start, ray->isect_precalc, UNPACK3(vtri_co), &dist_test, nullptr))
385#else
386 UNUSED_VARS(ray);
387 if (isect_ray_tri_v3(data->start, data->vec, UNPACK3(vtri_co), &dist_test, nullptr))
388#endif
389 {
390 if (dist_test < hit->dist) {
391 float n[3];
392 normal_tri_v3(n, UNPACK3(vtri_co));
393 if (dot_v3v3(n, data->vec) < -1e-5f) {
394 hit->index = index;
395 hit->dist = dist_test;
396 }
397 }
398 }
399}
400
401/* Ray-tracing for vertex to bone/vertex visibility. */
403{
404 const blender::int3 *corner_tris = sys->heat.corner_tris;
405 const blender::Span<int> corner_verts = sys->heat.corner_verts;
406 float(*verts)[3] = sys->heat.verts;
407 int tris_num = sys->heat.tris_num;
408 int verts_num = sys->heat.verts_num;
409 int a;
410
411 sys->heat.bvhtree = BLI_bvhtree_new(tris_num, 0.0f, 4, 6);
412 sys->heat.vltree = static_cast<const blender::int3 **>(
413 MEM_callocN(sizeof(blender::int3 *) * verts_num, "HeatVFaces"));
414
415 for (a = 0; a < tris_num; a++) {
416 const blender::int3 &tri = corner_tris[a];
417 float bb[6];
418 int vtri[3];
419
420 vtri[0] = corner_verts[tri[0]];
421 vtri[1] = corner_verts[tri[1]];
422 vtri[2] = corner_verts[tri[2]];
423
424 INIT_MINMAX(bb, bb + 3);
425 minmax_v3v3_v3(bb, bb + 3, verts[vtri[0]]);
426 minmax_v3v3_v3(bb, bb + 3, verts[vtri[1]]);
427 minmax_v3v3_v3(bb, bb + 3, verts[vtri[2]]);
428
429 BLI_bvhtree_insert(sys->heat.bvhtree, a, bb, 2);
430
431 /* Setup inverse pointers to use on isect.orig */
432 sys->heat.vltree[vtri[0]] = &tri;
433 sys->heat.vltree[vtri[1]] = &tri;
434 sys->heat.vltree[vtri[2]] = &tri;
435 }
436
438}
439
440static int heat_ray_source_visible(LaplacianSystem *sys, int vertex, int source)
441{
442 BVHTreeRayHit hit;
444 const blender::int3 *lt;
445 float end[3];
446 int visible;
447
448 lt = sys->heat.vltree[vertex];
449 if (lt == nullptr) {
450 return 1;
451 }
452
453 data.sys = sys;
454 copy_v3_v3(data.start, sys->heat.verts[vertex]);
455
456 closest_to_line_segment_v3(end, data.start, sys->heat.root[source], sys->heat.tip[source]);
457
458 sub_v3_v3v3(data.vec, end, data.start);
459 madd_v3_v3v3fl(data.start, data.start, data.vec, 1e-5);
460 mul_v3_fl(data.vec, 1.0f - 2e-5f);
461
462 /* pass normalized vec + distance to bvh */
463 hit.index = -1;
464 hit.dist = normalize_v3(data.vec);
465
466 visible =
468 sys->heat.bvhtree, data.start, data.vec, 0.0f, &hit, bvh_callback, (void *)&data) == -1;
469
470 return visible;
471}
472
473static float heat_source_distance(LaplacianSystem *sys, int vertex, int source)
474{
475 float closest[3], d[3], dist, cosine;
476
477 /* compute Euclidean distance */
479 closest, sys->heat.verts[vertex], sys->heat.root[source], sys->heat.tip[source]);
480
481 sub_v3_v3v3(d, sys->heat.verts[vertex], closest);
482 dist = normalize_v3(d);
483
484 /* if the vertex normal does not point along the bone, increase distance */
485 cosine = dot_v3v3(d, sys->heat.vert_normals[vertex]);
486
487 return dist / (0.5f * (cosine + 1.001f));
488}
489
490static int heat_source_closest(LaplacianSystem *sys, int vertex, int source)
491{
492 float dist;
493
494 dist = heat_source_distance(sys, vertex, source);
495
496 if (dist <= sys->heat.mindist[vertex] * (1.0f + DISTANCE_EPSILON)) {
497 if (heat_ray_source_visible(sys, vertex, source)) {
498 return 1;
499 }
500 }
501
502 return 0;
503}
504
505static void heat_set_H(LaplacianSystem *sys, int vertex)
506{
507 float dist, mindist, h;
508 int j, numclosest = 0;
509
510 mindist = 1e10;
511
512 /* compute minimum distance */
513 for (j = 0; j < sys->heat.numsource; j++) {
514 dist = heat_source_distance(sys, vertex, j);
515
516 mindist = std::min(dist, mindist);
517 }
518
519 sys->heat.mindist[vertex] = mindist;
520
521 /* count number of sources with approximately this minimum distance */
522 for (j = 0; j < sys->heat.numsource; j++) {
523 if (heat_source_closest(sys, vertex, j)) {
524 numclosest++;
525 }
526 }
527
528 sys->heat.p[vertex] = (numclosest > 0) ? 1.0f / numclosest : 0.0f;
529
530 /* compute H entry */
531 if (numclosest > 0) {
532 mindist = max_ff(mindist, 1e-4f);
533 h = numclosest * C_WEIGHT / (mindist * mindist);
534 }
535 else {
536 h = 0.0f;
537 }
538
539 sys->heat.H[vertex] = h;
540}
541
543{
544 float fnor[3];
545 int a, v1, v2, v3, (*face)[3];
546
547 sys->heat.vert_normals = static_cast<float(*)[3]>(
548 MEM_callocN(sizeof(float[3]) * sys->verts_num, "HeatVNors"));
549
550 for (a = 0, face = sys->faces; a < sys->faces_num; a++, face++) {
551 v1 = (*face)[0];
552 v2 = (*face)[1];
553 v3 = (*face)[2];
554
555 normal_tri_v3(fnor, sys->verts[v1], sys->verts[v2], sys->verts[v3]);
556
557 add_v3_v3(sys->heat.vert_normals[v1], fnor);
558 add_v3_v3(sys->heat.vert_normals[v2], fnor);
559 add_v3_v3(sys->heat.vert_normals[v3], fnor);
560 }
561
562 for (a = 0; a < sys->verts_num; a++) {
564 }
565}
566
568{
569 const blender::int3 *corner_tris = sys->heat.corner_tris;
570 const blender::Span<int> corner_verts = sys->heat.corner_verts;
571 int tris_num = sys->heat.tris_num;
572 int verts_num = sys->heat.verts_num;
573 int a;
574
575 /* heat specific definitions */
576 sys->heat.mindist = MEM_calloc_arrayN<float>(verts_num, "HeatMinDist");
577 sys->heat.H = MEM_calloc_arrayN<float>(verts_num, "HeatH");
578 sys->heat.p = MEM_calloc_arrayN<float>(verts_num, "HeatP");
579
580 /* add verts and faces to laplacian */
581 for (a = 0; a < verts_num; a++) {
582 laplacian_add_vertex(sys, sys->heat.verts[a], 0);
583 }
584
585 for (a = 0; a < tris_num; a++) {
586 int vtri[3];
587 vtri[0] = corner_verts[corner_tris[a][0]];
588 vtri[1] = corner_verts[corner_tris[a][1]];
589 vtri[2] = corner_verts[corner_tris[a][2]];
591 }
592
593 /* for distance computation in set_H */
595
596 for (a = 0; a < verts_num; a++) {
597 heat_set_H(sys, a);
598 }
599}
600
602{
604 MEM_freeN(sys->heat.vltree);
606
607 MEM_freeN(sys->heat.mindist);
608 MEM_freeN(sys->heat.H);
609 MEM_freeN(sys->heat.p);
611}
612
613static float heat_limit_weight(float weight)
614{
615 float t;
616
617 if (weight < WEIGHT_LIMIT_END) {
618 return 0.0f;
619 }
620 if (weight < WEIGHT_LIMIT_START) {
622 return t * WEIGHT_LIMIT_START;
623 }
624 return weight;
625}
626
628 Mesh *mesh,
629 float (*verts)[3],
630 int numbones,
631 bDeformGroup **dgrouplist,
632 bDeformGroup **dgroupflip,
633 float (*root)[3],
634 float (*tip)[3],
635 const bool *selected,
636 const char **r_error_str)
637{
638 using namespace blender;
639 LaplacianSystem *sys;
640 blender::int3 *corner_tris;
641 float solution, weight;
642 int *vertsflipped = nullptr, *mask = nullptr;
643 int a, tris_num, j, bbone, firstsegment, lastsegment;
644 bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
645
646 const blender::Span<blender::float3> vert_positions = mesh->vert_positions();
647 const blender::OffsetIndices faces = mesh->faces();
648 const blender::Span<int> corner_verts = mesh->corner_verts();
649 const bke::AttributeAccessor attributes = mesh->attributes();
650 bool use_vert_sel = (mesh->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
651 bool use_face_sel = (mesh->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
652
653 *r_error_str = nullptr;
654
655 /* bone heat needs triangulated faces */
656 tris_num = poly_to_tri_count(mesh->faces_num, mesh->corners_num);
657
658 /* count triangles and create mask */
659 if (ob->mode & OB_MODE_WEIGHT_PAINT && (use_face_sel || use_vert_sel)) {
660 mask = MEM_calloc_arrayN<int>(mesh->verts_num, "heat_bone_weighting mask");
661
662 /* (added selectedVerts content for vertex mask, they used to just equal 1) */
663 if (use_vert_sel) {
664 const VArray select_vert = *attributes.lookup_or_default<bool>(
665 ".select_vert", bke::AttrDomain::Point, false);
666 if (select_vert) {
667 for (const int i : faces.index_range()) {
668 for (const int vert : corner_verts.slice(faces[i])) {
669 mask[vert] = select_vert[vert];
670 }
671 }
672 }
673 }
674 else if (use_face_sel) {
675 const VArray select_poly = *attributes.lookup_or_default<bool>(
676 ".select_poly", bke::AttrDomain::Face, false);
677 if (select_poly) {
678 for (const int i : faces.index_range()) {
679 if (select_poly[i]) {
680 for (const int vert : corner_verts.slice(faces[i])) {
681 mask[vert] = 1;
682 }
683 }
684 }
685 }
686 }
687 }
688
689 /* create laplacian */
690 sys = laplacian_system_construct_begin(mesh->verts_num, tris_num, 1);
691
693 corner_tris = static_cast<blender::int3 *>(
694 MEM_mallocN(sizeof(*sys->heat.corner_tris) * sys->heat.tris_num, __func__));
695
697 vert_positions, faces, corner_verts, {corner_tris, sys->heat.tris_num});
698
699 sys->heat.corner_tris = corner_tris;
700 sys->heat.corner_verts = corner_verts;
701 sys->heat.verts_num = mesh->verts_num;
702 sys->heat.verts = verts;
703 sys->heat.root = root;
704 sys->heat.tip = tip;
705 sys->heat.numsource = numbones;
706
709
711
712 if (dgroupflip) {
713 vertsflipped = MEM_calloc_arrayN<int>(mesh->verts_num, "vertsflipped");
714 for (a = 0; a < mesh->verts_num; a++) {
715 vertsflipped[a] = mesh_get_x_mirror_vert(ob, nullptr, a, use_topology);
716 }
717 }
718
719 /* compute weights per bone */
720 for (j = 0; j < numbones; j++) {
721 if (selected[j] == false) {
722 continue;
723 }
724
725 firstsegment = (j == 0 || dgrouplist[j - 1] != dgrouplist[j]);
726 lastsegment = (j == numbones - 1 || dgrouplist[j] != dgrouplist[j + 1]);
727 bbone = !(firstsegment && lastsegment);
728
729 /* clear weights */
730 if (bbone && firstsegment) {
731 for (a = 0; a < mesh->verts_num; a++) {
732 if (mask && !mask[a]) {
733 continue;
734 }
735
736 blender::ed::object::vgroup_vert_remove(ob, dgrouplist[j], a);
737 if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
738 blender::ed::object::vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
739 }
740 }
741 }
742
743 /* fill right hand side */
744 laplacian_begin_solve(sys, -1);
745
746 for (a = 0; a < mesh->verts_num; a++) {
747 if (heat_source_closest(sys, a, j)) {
748 laplacian_add_right_hand_side(sys, a, sys->heat.H[a] * sys->heat.p[a]);
749 }
750 }
751
752 /* solve */
753 if (laplacian_system_solve(sys)) {
754 /* load solution into vertex groups */
755 for (a = 0; a < mesh->verts_num; a++) {
756 if (mask && !mask[a]) {
757 continue;
758 }
759
760 solution = laplacian_system_get_solution(sys, a);
761
762 if (bbone) {
763 if (solution > 0.0f) {
764 blender::ed::object::vgroup_vert_add(ob, dgrouplist[j], a, solution, WEIGHT_ADD);
765 }
766 }
767 else {
768 weight = heat_limit_weight(solution);
769 if (weight > 0.0f) {
770 blender::ed::object::vgroup_vert_add(ob, dgrouplist[j], a, weight, WEIGHT_REPLACE);
771 }
772 else {
773 blender::ed::object::vgroup_vert_remove(ob, dgrouplist[j], a);
774 }
775 }
776
777 /* do same for mirror */
778 if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
779 if (bbone) {
780 if (solution > 0.0f) {
782 ob, dgroupflip[j], vertsflipped[a], solution, WEIGHT_ADD);
783 }
784 }
785 else {
786 weight = heat_limit_weight(solution);
787 if (weight > 0.0f) {
789 ob, dgroupflip[j], vertsflipped[a], weight, WEIGHT_REPLACE);
790 }
791 else {
792 blender::ed::object::vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
793 }
794 }
795 }
796 }
797 }
798 else if (*r_error_str == nullptr) {
799 *r_error_str = N_("Bone Heat Weighting: failed to find solution for one or more bones");
800 break;
801 }
802
803 /* remove too small vertex weights */
804 if (bbone && lastsegment) {
805 for (a = 0; a < mesh->verts_num; a++) {
806 if (mask && !mask[a]) {
807 continue;
808 }
809
810 weight = blender::ed::object::vgroup_vert_weight(ob, dgrouplist[j], a);
811 weight = heat_limit_weight(weight);
812 if (weight <= 0.0f) {
813 blender::ed::object::vgroup_vert_remove(ob, dgrouplist[j], a);
814 }
815
816 if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
817 weight = blender::ed::object::vgroup_vert_weight(ob, dgroupflip[j], vertsflipped[a]);
818 weight = heat_limit_weight(weight);
819 if (weight <= 0.0f) {
820 blender::ed::object::vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
821 }
822 }
823 }
824 }
825 }
826
827 /* free */
828 if (vertsflipped) {
829 MEM_freeN(vertsflipped);
830 }
831 if (mask) {
833 }
834
835 heat_system_free(sys);
836
838}
839
840/************************** Harmonic Coordinates ****************************/
841/* From "Harmonic Coordinates for Character Articulation",
842 * Pushkar Joshi, Mark Meyer, Tony DeRose, Brian Green and Tom Sanocki,
843 * SIGGRAPH 2007. */
844
845#define EPSILON 0.0001f
846
847#define MESHDEFORM_TAG_UNTYPED 0
848#define MESHDEFORM_TAG_BOUNDARY 1
849#define MESHDEFORM_TAG_INTERIOR 2
850#define MESHDEFORM_TAG_EXTERIOR 3
851
853#define MESHDEFORM_LEN_THRESHOLD 1e-6f
854
855#define MESHDEFORM_MIN_INFLUENCE 0.0005f
856
857static const int MESHDEFORM_OFFSET[7][3] = {
858 {0, 0, 0},
859 {1, 0, 0},
860 {-1, 0, 0},
861 {0, 1, 0},
862 {0, -1, 0},
863 {0, 0, 1},
864 {0, 0, -1},
865};
866
868 /* intersection on the cage 'cagecos' */
869 float co[3];
870 /* non-facing intersections are considered interior */
871 bool facing;
872 /* ray-cast index aligned with polygons (ray-hit-triangle isn't needed) */
874 /* distance from 'co' to the ray-cast start (clamped to avoid zero division) */
875 float len;
876 /* weights aligned with the polygons's loop indices */
877 float poly_weights[0];
878};
879
885
887 /* grid dimensions */
888 float min[3], max[3];
889 float width[3], halfwidth[3];
891
892 /* meshes */
894 float (*cagecos)[3];
895 float (*vertexcos)[3];
897
898 /* grids */
900 MDefBoundIsect *(*boundisect)[6];
902 int *tag;
903 float *phi, *totalphi;
904
905 /* mesh stuff */
906 int *inside;
907 float *weights;
909 float cagemat[4][4];
910
911 /* direct solver */
912 int *varidx;
913
916
917 /* avoid DM function calls during intersections */
918 struct {
925};
926
928 float start[3];
929 float vec[3];
931 float lambda;
932
933 bool isect;
934 float u, v;
935};
936
937/* ray intersection */
938
943
944static void harmonic_ray_callback(void *userdata,
945 int index,
946 const BVHTreeRay *ray,
947 BVHTreeRayHit *hit)
948{
949 MeshRayCallbackData *data = static_cast<MeshRayCallbackData *>(userdata);
950 MeshDeformBind *mdb = data->mdb;
951 const blender::Span<int> corner_verts = mdb->cagemesh_cache.corner_verts;
952 const blender::Span<int> tri_faces = mdb->cagemesh_cache.tri_faces;
954 MeshDeformIsect *isec = data->isec;
955 float no[3], co[3], dist;
956 float *face[3];
957
958 const blender::int3 &tri = mdb->cagemesh_cache.corner_tris[index];
959
960 face[0] = mdb->cagecos[corner_verts[tri[0]]];
961 face[1] = mdb->cagecos[corner_verts[tri[1]]];
962 face[2] = mdb->cagecos[corner_verts[tri[2]]];
963
964 bool isect_ray_tri = isect_ray_tri_watertight_v3(
965 ray->origin, ray->isect_precalc, UNPACK3(face), &dist, nullptr);
966
967 if (!isect_ray_tri || dist > isec->vec_length) {
968 return;
969 }
970
971 if (!face_normals.is_empty()) {
972 copy_v3_v3(no, face_normals[tri_faces[index]]);
973 }
974 else {
975 normal_tri_v3(no, UNPACK3(face));
976 }
977
978 madd_v3_v3v3fl(co, ray->origin, ray->direction, dist);
979 dist /= isec->vec_length;
980 if (dist < hit->dist) {
981 hit->index = index;
982 hit->dist = dist;
983 copy_v3_v3(hit->co, co);
984
985 isec->isect = (dot_v3v3(no, ray->direction) <= 0.0f);
986 isec->lambda = dist;
987 }
988}
989
991 const float co1[3],
992 const float co2[3])
993{
994 BVHTreeRayHit hit;
995 MeshDeformIsect isect_mdef;
997 mdb,
998 &isect_mdef,
999 };
1000 float end[3], vec_normal[3];
1001
1002 /* happens binding when a cage has no faces */
1003 if (UNLIKELY(mdb->bvhtree == nullptr)) {
1004 return nullptr;
1005 }
1006
1007 /* setup isec */
1008 memset(&isect_mdef, 0, sizeof(isect_mdef));
1009 isect_mdef.lambda = 1e10f;
1010
1011 copy_v3_v3(isect_mdef.start, co1);
1012 copy_v3_v3(end, co2);
1013 sub_v3_v3v3(isect_mdef.vec, end, isect_mdef.start);
1014 isect_mdef.vec_length = normalize_v3_v3(vec_normal, isect_mdef.vec);
1015
1016 hit.index = -1;
1019 isect_mdef.start,
1020 vec_normal,
1021 0.0,
1022 &hit,
1024 &data,
1026 {
1027 const blender::Span<int> corner_verts = mdb->cagemesh_cache.corner_verts;
1028 const int face_i = mdb->cagemesh_cache.tri_faces[hit.index];
1029 const blender::IndexRange face = mdb->cagemesh_cache.faces[face_i];
1030 const float(*cagecos)[3] = mdb->cagecos;
1031 const float len = isect_mdef.lambda;
1032 MDefBoundIsect *isect;
1033
1034 blender::Array<blender::float3, 64> mp_cagecos(face.size());
1035
1036 /* create MDefBoundIsect, and extra for 'poly_weights[]' */
1037 isect = static_cast<MDefBoundIsect *>(
1038 BLI_memarena_alloc(mdb->memarena, sizeof(*isect) + (sizeof(float) * face.size())));
1039
1040 /* compute intersection coordinate */
1041 madd_v3_v3v3fl(isect->co, co1, isect_mdef.vec, len);
1042
1043 isect->facing = isect_mdef.isect;
1044
1045 isect->face_index = face_i;
1046
1047 isect->len = max_ff(len_v3v3(co1, isect->co), MESHDEFORM_LEN_THRESHOLD);
1048
1049 /* compute mean value coordinates for interpolation */
1050 for (int i = 0; i < face.size(); i++) {
1051 copy_v3_v3(mp_cagecos[i], cagecos[corner_verts[face[i]]]);
1052 }
1053
1055 reinterpret_cast<float(*)[3]>(mp_cagecos.data()),
1056 face.size(),
1057 isect->co);
1058
1059 return isect;
1060 }
1061
1062 return nullptr;
1063}
1064
1065static int meshdeform_inside_cage(MeshDeformBind *mdb, float *co)
1066{
1067 MDefBoundIsect *isect;
1068 float outside[3], start[3], dir[3];
1069 int i;
1070
1071 for (i = 1; i <= 6; i++) {
1072 outside[0] = co[0] + (mdb->max[0] - mdb->min[0] + 1.0f) * MESHDEFORM_OFFSET[i][0];
1073 outside[1] = co[1] + (mdb->max[1] - mdb->min[1] + 1.0f) * MESHDEFORM_OFFSET[i][1];
1074 outside[2] = co[2] + (mdb->max[2] - mdb->min[2] + 1.0f) * MESHDEFORM_OFFSET[i][2];
1075
1076 copy_v3_v3(start, co);
1077 sub_v3_v3v3(dir, outside, start);
1078 normalize_v3(dir);
1079
1080 isect = meshdeform_ray_tree_intersect(mdb, start, outside);
1081 if (isect && !isect->facing) {
1082 return 1;
1083 }
1084 }
1085
1086 return 0;
1087}
1088
1089/* solving */
1090
1091BLI_INLINE int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n)
1092{
1093 int size = mdb->size;
1094
1095 x += MESHDEFORM_OFFSET[n][0];
1096 y += MESHDEFORM_OFFSET[n][1];
1097 z += MESHDEFORM_OFFSET[n][2];
1098
1099 if (x < 0 || x >= mdb->size) {
1100 return -1;
1101 }
1102 if (y < 0 || y >= mdb->size) {
1103 return -1;
1104 }
1105 if (z < 0 || z >= mdb->size) {
1106 return -1;
1107 }
1108
1109 return x + y * size + z * size * size;
1110}
1111
1113 MeshDeformBind *mdb, int x, int y, int z, int n, float *center)
1114{
1115 x += MESHDEFORM_OFFSET[n][0];
1116 y += MESHDEFORM_OFFSET[n][1];
1117 z += MESHDEFORM_OFFSET[n][2];
1118
1119 center[0] = mdb->min[0] + x * mdb->width[0] + mdb->halfwidth[0];
1120 center[1] = mdb->min[1] + y * mdb->width[1] + mdb->halfwidth[1];
1121 center[2] = mdb->min[2] + z * mdb->width[2] + mdb->halfwidth[2];
1122}
1123
1124static void meshdeform_add_intersections(MeshDeformBind *mdb, int x, int y, int z)
1125{
1126 MDefBoundIsect *isect;
1127 float center[3], ncenter[3];
1128 int i, a;
1129
1130 a = meshdeform_index(mdb, x, y, z, 0);
1131 meshdeform_cell_center(mdb, x, y, z, 0, center);
1132
1133 /* check each outgoing edge for intersection */
1134 for (i = 1; i <= 6; i++) {
1135 if (meshdeform_index(mdb, x, y, z, i) == -1) {
1136 continue;
1137 }
1138
1139 meshdeform_cell_center(mdb, x, y, z, i, ncenter);
1140
1141 isect = meshdeform_ray_tree_intersect(mdb, center, ncenter);
1142 if (isect) {
1143 mdb->boundisect[a][i - 1] = isect;
1144 mdb->tag[a] = MESHDEFORM_TAG_BOUNDARY;
1145 }
1146 }
1147}
1148
1150{
1151 int *stack, *tag = mdb->tag;
1152 int a, b, i, xyz[3], stacksize, size = mdb->size;
1153
1154 stack = MEM_calloc_arrayN<int>(mdb->size3, __func__);
1155
1156 /* we know lower left corner is EXTERIOR because of padding */
1157 tag[0] = MESHDEFORM_TAG_EXTERIOR;
1158 stack[0] = 0;
1159 stacksize = 1;
1160
1161 /* floodfill exterior tag */
1162 while (stacksize > 0) {
1163 a = stack[--stacksize];
1164
1165 xyz[2] = a / (size * size);
1166 xyz[1] = (a - xyz[2] * size * size) / size;
1167 xyz[0] = a - xyz[1] * size - xyz[2] * size * size;
1168
1169 for (i = 1; i <= 6; i++) {
1170 b = meshdeform_index(mdb, xyz[0], xyz[1], xyz[2], i);
1171
1172 if (b != -1) {
1173 if (tag[b] == MESHDEFORM_TAG_UNTYPED ||
1174 (tag[b] == MESHDEFORM_TAG_BOUNDARY && !mdb->boundisect[a][i - 1]))
1175 {
1177 stack[stacksize++] = b;
1178 }
1179 }
1180 }
1181 }
1182
1183 /* other cells are interior */
1184 for (a = 0; a < size * size * size; a++) {
1185 if (tag[a] == MESHDEFORM_TAG_UNTYPED) {
1186 tag[a] = MESHDEFORM_TAG_INTERIOR;
1187 }
1188 }
1189
1190#if 0
1191 {
1192 int tb, ti, te, ts;
1193 tb = ti = te = ts = 0;
1194 for (a = 0; a < size * size * size; a++) {
1195 if (tag[a] == MESHDEFORM_TAG_BOUNDARY) {
1196 tb++;
1197 }
1198 else if (tag[a] == MESHDEFORM_TAG_INTERIOR) {
1199 ti++;
1200 }
1201 else if (tag[a] == MESHDEFORM_TAG_EXTERIOR) {
1202 te++;
1203
1204 if (mdb->semibound[a]) {
1205 ts++;
1206 }
1207 }
1208 }
1209
1210 printf("interior %d exterior %d boundary %d semi-boundary %d\n", ti, te, tb, ts);
1211 }
1212#endif
1213
1214 MEM_freeN(stack);
1215}
1216
1218 const MDefBoundIsect *isect,
1219 int cagevert)
1220{
1221 const blender::IndexRange face = mdb->cagemesh_cache.faces[isect->face_index];
1222 const blender::Span<int> corner_verts = mdb->cagemesh_cache.corner_verts;
1223
1224 for (int i = 0; i < face.size(); i++) {
1225 if (corner_verts[face[i]] == cagevert) {
1226 return isect->poly_weights[i];
1227 }
1228 }
1229
1230 return 0.0f;
1231}
1232
1234 const float *gridvec,
1235 float * /*vec*/,
1236 int /*cagevert*/)
1237{
1238 float dvec[3], ivec[3], result = 0.0f;
1239 float totweight = 0.0f;
1240
1241 for (int i = 0; i < 3; i++) {
1242 ivec[i] = int(gridvec[i]);
1243 dvec[i] = gridvec[i] - ivec[i];
1244 }
1245
1246 for (int i = 0; i < 8; i++) {
1247 int x, y, z;
1248 float wx, wy, wz;
1249
1250 if (i & 1) {
1251 x = ivec[0] + 1;
1252 wx = dvec[0];
1253 }
1254 else {
1255 x = ivec[0];
1256 wx = 1.0f - dvec[0];
1257 }
1258
1259 if (i & 2) {
1260 y = ivec[1] + 1;
1261 wy = dvec[1];
1262 }
1263 else {
1264 y = ivec[1];
1265 wy = 1.0f - dvec[1];
1266 }
1267
1268 if (i & 4) {
1269 z = ivec[2] + 1;
1270 wz = dvec[2];
1271 }
1272 else {
1273 z = ivec[2];
1274 wz = 1.0f - dvec[2];
1275 }
1276
1277 CLAMP(x, 0, mdb->size - 1);
1278 CLAMP(y, 0, mdb->size - 1);
1279 CLAMP(z, 0, mdb->size - 1);
1280
1281 int a = meshdeform_index(mdb, x, y, z, 0);
1282 float weight = wx * wy * wz;
1283 result += weight * mdb->phi[a];
1284 totweight += weight;
1285 }
1286
1287 if (totweight > 0.0f) {
1288 result /= totweight;
1289 }
1290
1291 return result;
1292}
1293
1294static void meshdeform_check_semibound(MeshDeformBind *mdb, int x, int y, int z)
1295{
1296 int i, a;
1297
1298 a = meshdeform_index(mdb, x, y, z, 0);
1299 if (mdb->tag[a] != MESHDEFORM_TAG_EXTERIOR) {
1300 return;
1301 }
1302
1303 for (i = 1; i <= 6; i++) {
1304 if (mdb->boundisect[a][i - 1]) {
1305 mdb->semibound[a] = 1;
1306 }
1307 }
1308}
1309
1310static float meshdeform_boundary_total_weight(MeshDeformBind *mdb, int x, int y, int z)
1311{
1312 float weight, totweight = 0.0f;
1313 int i, a;
1314
1315 a = meshdeform_index(mdb, x, y, z, 0);
1316
1317 /* count weight for neighbor cells */
1318 for (i = 1; i <= 6; i++) {
1319 if (meshdeform_index(mdb, x, y, z, i) == -1) {
1320 continue;
1321 }
1322
1323 if (mdb->boundisect[a][i - 1]) {
1324 weight = 1.0f / mdb->boundisect[a][i - 1]->len;
1325 }
1326 else if (!mdb->semibound[a]) {
1327 weight = 1.0f / mdb->width[0];
1328 }
1329 else {
1330 weight = 0.0f;
1331 }
1332
1333 totweight += weight;
1334 }
1335
1336 return totweight;
1337}
1338
1340 MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z)
1341{
1342 MDefBoundIsect *isect;
1343 float weight, totweight;
1344 int i, a, acenter;
1345
1346 acenter = meshdeform_index(mdb, x, y, z, 0);
1347 if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) {
1348 return;
1349 }
1350
1351 EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[acenter], 1.0f);
1352
1353 totweight = meshdeform_boundary_total_weight(mdb, x, y, z);
1354 for (i = 1; i <= 6; i++) {
1355 a = meshdeform_index(mdb, x, y, z, i);
1356 if (a == -1 || mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) {
1357 continue;
1358 }
1359
1360 isect = mdb->boundisect[acenter][i - 1];
1361 if (!isect) {
1362 weight = (1.0f / mdb->width[0]) / totweight;
1363 EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[a], -weight);
1364 }
1365 }
1366}
1367
1369 MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z, int cagevert)
1370{
1371 MDefBoundIsect *isect;
1372 float rhs, weight, totweight;
1373 int i, a, acenter;
1374
1375 acenter = meshdeform_index(mdb, x, y, z, 0);
1376 if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) {
1377 return;
1378 }
1379
1380 totweight = meshdeform_boundary_total_weight(mdb, x, y, z);
1381 for (i = 1; i <= 6; i++) {
1382 a = meshdeform_index(mdb, x, y, z, i);
1383 if (a == -1) {
1384 continue;
1385 }
1386
1387 isect = mdb->boundisect[acenter][i - 1];
1388
1389 if (isect) {
1390 weight = (1.0f / isect->len) / totweight;
1391 rhs = weight * meshdeform_boundary_phi(mdb, isect, cagevert);
1392 EIG_linear_solver_right_hand_side_add(context, 0, mdb->varidx[acenter], rhs);
1393 }
1394 }
1395}
1396
1398 MeshDeformBind *mdb, int x, int y, int z, int cagevert)
1399{
1400 MDefBoundIsect *isect;
1401 float rhs, weight, totweight;
1402 int i, a;
1403
1404 a = meshdeform_index(mdb, x, y, z, 0);
1405 if (!mdb->semibound[a]) {
1406 return;
1407 }
1408
1409 mdb->phi[a] = 0.0f;
1410
1411 totweight = meshdeform_boundary_total_weight(mdb, x, y, z);
1412 for (i = 1; i <= 6; i++) {
1413 isect = mdb->boundisect[a][i - 1];
1414
1415 if (isect) {
1416 weight = (1.0f / isect->len) / totweight;
1417 rhs = weight * meshdeform_boundary_phi(mdb, isect, cagevert);
1418 mdb->phi[a] += rhs;
1419 }
1420 }
1421}
1422
1424 MeshDeformBind *mdb, int x, int y, int z, int /*cagevert*/)
1425{
1426 float phi, totweight;
1427 int i, a, acenter;
1428
1429 acenter = meshdeform_index(mdb, x, y, z, 0);
1430 if (mdb->tag[acenter] != MESHDEFORM_TAG_EXTERIOR || mdb->semibound[acenter]) {
1431 return;
1432 }
1433
1434 phi = 0.0f;
1435 totweight = 0.0f;
1436 for (i = 1; i <= 6; i++) {
1437 a = meshdeform_index(mdb, x, y, z, i);
1438
1439 if (a != -1 && mdb->semibound[a]) {
1440 phi += mdb->phi[a];
1441 totweight += 1.0f;
1442 }
1443 }
1444
1445 if (totweight != 0.0f) {
1446 mdb->phi[acenter] = phi / totweight;
1447 }
1448}
1449
1451{
1452 LinearSolver *context;
1453 float vec[3], gridvec[3];
1454 int a, b, x, y, z, totvar;
1455 char message[256];
1456
1457 /* setup variable indices */
1458 mdb->varidx = MEM_calloc_arrayN<int>(mdb->size3, "MeshDeformDSvaridx");
1459 for (a = 0, totvar = 0; a < mdb->size3; a++) {
1460 mdb->varidx[a] = (mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) ? -1 : totvar++;
1461 }
1462
1463 if (totvar == 0) {
1464 MEM_freeN(mdb->varidx);
1465 return;
1466 }
1467
1468 progress_bar(0, "Starting mesh deform solve");
1469
1470 /* setup linear solver */
1471 context = EIG_linear_solver_new(totvar, totvar, 1);
1472
1473 /* build matrix */
1474 for (z = 0; z < mdb->size; z++) {
1475 for (y = 0; y < mdb->size; y++) {
1476 for (x = 0; x < mdb->size; x++) {
1477 meshdeform_matrix_add_cell(mdb, context, x, y, z);
1478 }
1479 }
1480 }
1481
1482 /* solve for each cage vert */
1483 for (a = 0; a < mdb->cage_verts_num; a++) {
1484 /* fill in right hand side and solve */
1485 for (z = 0; z < mdb->size; z++) {
1486 for (y = 0; y < mdb->size; y++) {
1487 for (x = 0; x < mdb->size; x++) {
1488 meshdeform_matrix_add_rhs(mdb, context, x, y, z, a);
1489 }
1490 }
1491 }
1492
1493 if (EIG_linear_solver_solve(context)) {
1494 for (z = 0; z < mdb->size; z++) {
1495 for (y = 0; y < mdb->size; y++) {
1496 for (x = 0; x < mdb->size; x++) {
1498 }
1499 }
1500 }
1501
1502 for (z = 0; z < mdb->size; z++) {
1503 for (y = 0; y < mdb->size; y++) {
1504 for (x = 0; x < mdb->size; x++) {
1506 }
1507 }
1508 }
1509
1510 for (b = 0; b < mdb->size3; b++) {
1511 if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) {
1512 mdb->phi[b] = EIG_linear_solver_variable_get(context, 0, mdb->varidx[b]);
1513 }
1514 mdb->totalphi[b] += mdb->phi[b];
1515 }
1516
1517 if (mdb->weights) {
1518 /* static bind : compute weights for each vertex */
1519 for (b = 0; b < mdb->verts_num; b++) {
1520 if (mdb->inside[b]) {
1521 copy_v3_v3(vec, mdb->vertexcos[b]);
1522 gridvec[0] = (vec[0] - mdb->min[0] - mdb->halfwidth[0]) / mdb->width[0];
1523 gridvec[1] = (vec[1] - mdb->min[1] - mdb->halfwidth[1]) / mdb->width[1];
1524 gridvec[2] = (vec[2] - mdb->min[2] - mdb->halfwidth[2]) / mdb->width[2];
1525
1526 mdb->weights[b * mdb->cage_verts_num + a] = meshdeform_interp_w(mdb, gridvec, vec, a);
1527 }
1528 }
1529 }
1530 else {
1531 MDefBindInfluence *inf;
1532
1533 /* dynamic bind */
1534 for (b = 0; b < mdb->size3; b++) {
1535 if (mdb->phi[b] >= MESHDEFORM_MIN_INFLUENCE) {
1536 inf = static_cast<MDefBindInfluence *>(
1537 BLI_memarena_alloc(mdb->memarena, sizeof(*inf)));
1538 inf->vertex = a;
1539 inf->weight = mdb->phi[b];
1540 inf->next = mdb->dyngrid[b];
1541 mdb->dyngrid[b] = inf;
1542 }
1543 }
1544 }
1545 }
1546 else {
1548 mmd->object, &mmd->modifier, "Failed to find bind solution (increase precision?)");
1549 error("Mesh Deform: failed to find bind solution.");
1550 break;
1551 }
1552
1553 SNPRINTF(message, "Mesh deform solve %d / %d |||", a + 1, mdb->cage_verts_num);
1554 progress_bar(float(a + 1) / float(mdb->cage_verts_num), message);
1555 }
1556
1557#if 0
1558 /* sanity check */
1559 for (b = 0; b < mdb->size3; b++) {
1560 if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) {
1561 if (fabsf(mdb->totalphi[b] - 1.0f) > 1e-4f) {
1562 printf("totalphi deficiency [%s|%d] %d: %.10f\n",
1563 (mdb->tag[b] == MESHDEFORM_TAG_INTERIOR) ? "interior" : "boundary",
1564 mdb->semibound[b],
1565 mdb->varidx[b],
1566 mdb->totalphi[b]);
1567 }
1568 }
1569 }
1570#endif
1571
1572 /* free */
1573 MEM_freeN(mdb->varidx);
1574
1575 EIG_linear_solver_delete(context);
1576}
1577
1579{
1580 MDefBindInfluence *inf;
1581 MDefInfluence *mdinf;
1582 MDefCell *cell;
1583 float center[3], vec[3], maxwidth, totweight;
1584 int a, b, x, y, z, totinside, offset;
1585
1586 /* compute bounding box of the cage mesh */
1587 INIT_MINMAX(mdb->min, mdb->max);
1588
1589 for (a = 0; a < mdb->cage_verts_num; a++) {
1590 minmax_v3v3_v3(mdb->min, mdb->max, mdb->cagecos[a]);
1591 }
1592
1593 /* allocate memory */
1594 mdb->size = (2 << (mmd->gridsize - 1)) + 2;
1595 mdb->size3 = mdb->size * mdb->size * mdb->size;
1596 mdb->tag = MEM_calloc_arrayN<int>(mdb->size3, "MeshDeformBindTag");
1597 mdb->phi = MEM_calloc_arrayN<float>(mdb->size3, "MeshDeformBindPhi");
1598 mdb->totalphi = MEM_calloc_arrayN<float>(mdb->size3, "MeshDeformBindTotalPhi");
1599 mdb->boundisect = static_cast<MDefBoundIsect *(*)[6]>(
1600 MEM_callocN(sizeof(*mdb->boundisect) * mdb->size3, "MDefBoundIsect"));
1601 mdb->semibound = MEM_calloc_arrayN<int>(mdb->size3, "MDefSemiBound");
1602 mdb->bvhdata = mdb->cagemesh->bvh_corner_tris();
1603 mdb->bvhtree = mdb->bvhdata.tree;
1604 mdb->inside = MEM_calloc_arrayN<int>(mdb->verts_num, "MDefInside");
1605
1606 if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) {
1607 mdb->dyngrid = MEM_calloc_arrayN<MDefBindInfluence *>(mdb->size3, "MDefDynGrid");
1608 }
1609 else {
1610 mdb->weights = MEM_calloc_arrayN<float>(mdb->verts_num * mdb->cage_verts_num, "MDefWeights");
1611 }
1612
1613 mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena");
1615
1616 /* Initialize data from `cagedm` for reuse. */
1617 {
1618 Mesh *mesh = mdb->cagemesh;
1619 mdb->cagemesh_cache.faces = mesh->faces();
1620 mdb->cagemesh_cache.corner_verts = mesh->corner_verts();
1621 mdb->cagemesh_cache.corner_tris = mesh->corner_tris();
1622 mdb->cagemesh_cache.tri_faces = mesh->corner_tri_faces();
1623 mdb->cagemesh_cache.face_normals = mesh->face_normals();
1624 }
1625
1626 /* make bounding box equal size in all directions, add padding, and compute
1627 * width of the cells */
1628 maxwidth = -1.0f;
1629 for (a = 0; a < 3; a++) {
1630 maxwidth = std::max(mdb->max[a] - mdb->min[a], maxwidth);
1631 }
1632
1633 for (a = 0; a < 3; a++) {
1634 center[a] = (mdb->min[a] + mdb->max[a]) * 0.5f;
1635 mdb->min[a] = center[a] - maxwidth * 0.5f;
1636 mdb->max[a] = center[a] + maxwidth * 0.5f;
1637
1638 mdb->width[a] = (mdb->max[a] - mdb->min[a]) / (mdb->size - 4);
1639 mdb->min[a] -= 2.1f * mdb->width[a];
1640 mdb->max[a] += 2.1f * mdb->width[a];
1641
1642 mdb->width[a] = (mdb->max[a] - mdb->min[a]) / mdb->size;
1643 mdb->halfwidth[a] = mdb->width[a] * 0.5f;
1644 }
1645
1646 progress_bar(0, "Setting up mesh deform system");
1647
1648 totinside = 0;
1649 for (a = 0; a < mdb->verts_num; a++) {
1650 copy_v3_v3(vec, mdb->vertexcos[a]);
1651 mdb->inside[a] = meshdeform_inside_cage(mdb, vec);
1652 if (mdb->inside[a]) {
1653 totinside++;
1654 }
1655 }
1656 (void)totinside; /* Quiet set-but-unused warning (may be removed). */
1657
1658 /* free temporary MDefBoundIsects */
1660 mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena");
1661
1662 /* start with all cells untyped */
1663 for (a = 0; a < mdb->size3; a++) {
1664 mdb->tag[a] = MESHDEFORM_TAG_UNTYPED;
1665 }
1666
1667 /* detect intersections and tag boundary cells */
1668 for (z = 0; z < mdb->size; z++) {
1669 for (y = 0; y < mdb->size; y++) {
1670 for (x = 0; x < mdb->size; x++) {
1672 }
1673 }
1674 }
1675
1676 /* compute exterior and interior tags */
1678
1679 for (z = 0; z < mdb->size; z++) {
1680 for (y = 0; y < mdb->size; y++) {
1681 for (x = 0; x < mdb->size; x++) {
1683 }
1684 }
1685 }
1686
1687 /* solve */
1688 meshdeform_matrix_solve(mmd, mdb);
1689
1690 /* assign results */
1691 if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) {
1692 mmd->influences_num = 0;
1693 for (a = 0; a < mdb->size3; a++) {
1694 for (inf = mdb->dyngrid[a]; inf; inf = inf->next) {
1695 mmd->influences_num++;
1696 }
1697 }
1698
1699 /* convert MDefBindInfluences to smaller MDefInfluences */
1700 mmd->dyngrid = MEM_calloc_arrayN<MDefCell>(mdb->size3, "MDefDynGrid");
1702 offset = 0;
1703 for (a = 0; a < mdb->size3; a++) {
1704 cell = &mmd->dyngrid[a];
1705 cell->offset = offset;
1706
1707 totweight = 0.0f;
1708 mdinf = mmd->dyninfluences + cell->offset;
1709 for (inf = mdb->dyngrid[a]; inf; inf = inf->next, mdinf++) {
1710 mdinf->weight = inf->weight;
1711 mdinf->vertex = inf->vertex;
1712 totweight += mdinf->weight;
1713 cell->influences_num++;
1714 }
1715
1716 if (totweight > 0.0f) {
1717 mdinf = mmd->dyninfluences + cell->offset;
1718 for (b = 0; b < cell->influences_num; b++, mdinf++) {
1719 mdinf->weight /= totweight;
1720 }
1721 }
1722
1723 offset += cell->influences_num;
1724 }
1725
1726 mmd->dynverts = mdb->inside;
1727 mmd->dyngridsize = mdb->size;
1728 copy_v3_v3(mmd->dyncellmin, mdb->min);
1729 mmd->dyncellwidth = mdb->width[0];
1730 MEM_freeN(mdb->dyngrid);
1731 }
1732 else {
1733 mmd->bindweights = mdb->weights;
1734 MEM_freeN(mdb->inside);
1735 }
1736
1737 MEM_freeN(mdb->tag);
1738 MEM_freeN(mdb->phi);
1739 MEM_freeN(mdb->totalphi);
1740 MEM_freeN(mdb->boundisect);
1741 MEM_freeN(mdb->semibound);
1743}
1744
1747 Mesh *cagemesh,
1748 float *vertexcos,
1749 int verts_num,
1750 float cagemat[4][4])
1751{
1752 MeshDeformModifierData *mmd_orig = reinterpret_cast<MeshDeformModifierData *>(
1753 BKE_modifier_get_original(object, &mmd->modifier));
1754 MeshDeformBind mdb{};
1755 int a;
1756
1757 waitcursor(1);
1759
1760 /* No need to support other kinds of mesh data as binding is a one-off action. */
1762
1763 /* get mesh and cage mesh */
1764 mdb.vertexcos = static_cast<float(*)[3]>(
1765 MEM_callocN(sizeof(float[3]) * verts_num, "MeshDeformCos"));
1766 mdb.verts_num = verts_num;
1767
1768 mdb.cagemesh = cagemesh;
1770 mdb.cagecos = static_cast<float(*)[3]>(
1771 MEM_callocN(sizeof(*mdb.cagecos) * mdb.cage_verts_num, "MeshDeformBindCos"));
1772 copy_m4_m4(mdb.cagemat, cagemat);
1773
1774 const blender::Span<blender::float3> positions = mdb.cagemesh->vert_positions();
1775 for (a = 0; a < mdb.cage_verts_num; a++) {
1776 copy_v3_v3(mdb.cagecos[a], positions[a]);
1777 }
1778 for (a = 0; a < mdb.verts_num; a++) {
1779 mul_v3_m4v3(mdb.vertexcos[a], mdb.cagemat, vertexcos + a * 3);
1780 }
1781
1782 /* solve */
1783 harmonic_coordinates_bind(mmd_orig, &mdb);
1784
1785 /* assign bind variables */
1786 mmd_orig->bindcagecos = (float *)mdb.cagecos;
1787 mmd_orig->verts_num = mdb.verts_num;
1788 mmd_orig->cage_verts_num = mdb.cage_verts_num;
1789 copy_m4_m4(mmd_orig->bindmat, mmd_orig->object->object_to_world().ptr());
1790
1791 /* transform bindcagecos to world space */
1792 for (a = 0; a < mdb.cage_verts_num; a++) {
1793 mul_m4_v3(mmd_orig->object->object_to_world().ptr(), mmd_orig->bindcagecos + a * 3);
1794 }
1795
1796 /* free */
1797 MEM_freeN(mdb.vertexcos);
1798
1799 /* compact weights */
1800 BKE_modifier_mdef_compact_influences(reinterpret_cast<ModifierData *>(mmd_orig));
1801
1803 waitcursor(0);
1804}
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
ModifierData * BKE_modifier_get_original(const Object *object, ModifierData *md)
void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_modifier_mdef_compact_influences(ModifierData *md)
#define BLI_INLINE
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
#define BVH_RAYCAST_DIST_MAX
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
int BLI_bvhtree_ray_cast_ex(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata, int flag)
@ BVH_RAYCAST_WATERTIGHT
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)
MINLINE float max_ff(float a, float b)
#define DEG2RADF(_deg)
bool isect_ray_tri_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2])
float closest_to_line_segment_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3])
Definition math_geom.cc:387
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:100
bool isect_ray_tri_watertight_v3(const float ray_origin[3], const struct IsectRayPrecalc *isect_precalc, const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2])
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
float cotangent_tri_weight_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:198
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
void interp_weights_poly_v3(float w[], float v[][3], int n, const float co[3])
void mul_m4_v3(const float M[4][4], float r[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
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 float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float n[3])
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_use_calloc(MemArena *ma) ATTR_NONNULL(1)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
#define CLAMP(a, b, c)
#define INIT_MINMAX(min, max)
#define UNUSED_VARS(...)
#define UNPACK3(a)
#define UNLIKELY(x)
@ ME_EDIT_PAINT_VERT_SEL
@ ME_EDIT_PAINT_FACE_SEL
@ ME_EDIT_MIRROR_TOPO
@ MOD_MDEF_DYNAMIC_BIND
@ OB_MODE_WEIGHT_PAINT
Object is a sort of wrapper for general info.
int mesh_get_x_mirror_vert(Object *ob, Mesh *mesh_eval, int index, bool use_topology)
Definition meshtools.cc:911
#define WEIGHT_REPLACE
#define WEIGHT_ADD
Read Guarded memory(de)allocation.
for(;discarded_id_iter !=nullptr;discarded_id_iter=static_cast< ID * >(discarded_id_iter->next))
Definition blendfile.cc:634
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
bool closest(btVector3 &v)
AttributeSet attributes
void reserve(int64_t n)
Definition BLI_map.hh:1028
const T * data() const
Definition BLI_array.hh:301
constexpr int64_t size() const
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:481
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
#define fabsf(x)
#define str(s)
static float verts[][3]
#define printf(...)
#define MEM_SAFE_FREE(v)
LinearSolver * EIG_linear_solver_new(int num_rows, int num_columns, int num_right_hand_sides)
void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_right_hand_sides)
void EIG_linear_solver_delete(LinearSolver *solver)
double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
bool EIG_linear_solver_solve(LinearSolver *solver)
void EIG_linear_solver_variable_lock(LinearSolver *solver, int index)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static char faces[256]
static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
static void meshdeform_bind_floodfill(MeshDeformBind *mdb)
void heat_bone_weighting(Object *ob, Mesh *mesh, float(*verts)[3], int numbones, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float(*root)[3], float(*tip)[3], const bool *selected, const char **r_error_str)
static float meshdeform_boundary_phi(const MeshDeformBind *mdb, const MDefBoundIsect *isect, int cagevert)
static void laplacian_system_delete(LaplacianSystem *sys)
BLI_INLINE void meshdeform_cell_center(MeshDeformBind *mdb, int x, int y, int z, int n, float *center)
static void laplacian_triangle_area(LaplacianSystem *sys, int i1, int i2, int i3)
static void start_progress_bar()
float laplacian_system_get_solution(LaplacianSystem *sys, int v)
static void laplacian_system_construct_end(LaplacianSystem *sys)
static int meshdeform_inside_cage(MeshDeformBind *mdb, float *co)
static void heat_set_H(LaplacianSystem *sys, int vertex)
void laplacian_add_right_hand_side(LaplacianSystem *sys, int v, float value)
static float heat_limit_weight(float weight)
int laplacian_system_solve(LaplacianSystem *sys)
static void heat_ray_tree_create(LaplacianSystem *sys)
static void meshdeform_matrix_add_semibound_phi(MeshDeformBind *mdb, int x, int y, int z, int cagevert)
#define WEIGHT_LIMIT_END
#define WEIGHT_LIMIT_START
static float heat_source_distance(LaplacianSystem *sys, int vertex, int source)
static void meshdeform_matrix_add_cell(MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z)
void ED_mesh_deform_bind_callback(Object *object, MeshDeformModifierData *mmd, Mesh *cagemesh, float *vertexcos, int verts_num, float cagemat[4][4])
#define MESHDEFORM_MIN_INFLUENCE
static void meshdeform_matrix_solve(MeshDeformModifierData *mmd, MeshDeformBind *mdb)
static int laplacian_edge_count(const blender::Map< blender::OrderedEdge, int > &edgehash, int v1, int v2)
static void end_progress_bar()
static void progress_bar(int, const char *)
static void meshdeform_add_intersections(MeshDeformBind *mdb, int x, int y, int z)
static void harmonic_coordinates_bind(MeshDeformModifierData *mmd, MeshDeformBind *mdb)
void laplacian_add_triangle(LaplacianSystem *sys, int v1, int v2, int v3)
static void waitcursor(int)
static void error(const char *str)
static LaplacianSystem * laplacian_system_construct_begin(int verts_num, int faces_num, int lsq)
static MDefBoundIsect * meshdeform_ray_tree_intersect(MeshDeformBind *mdb, const float co1[3], const float co2[3])
static int heat_source_closest(LaplacianSystem *sys, int vertex, int source)
BLI_INLINE int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n)
static void meshdeform_matrix_add_exterior_phi(MeshDeformBind *mdb, int x, int y, int z, int)
static float meshdeform_boundary_total_weight(MeshDeformBind *mdb, int x, int y, int z)
static void heat_calc_vnormals(LaplacianSystem *sys)
static void harmonic_ray_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
static void heat_laplacian_create(LaplacianSystem *sys)
static const int MESHDEFORM_OFFSET[7][3]
#define MESHDEFORM_TAG_UNTYPED
#define MESHDEFORM_TAG_BOUNDARY
static void meshdeform_check_semibound(MeshDeformBind *mdb, int x, int y, int z)
#define MESHDEFORM_TAG_INTERIOR
static void laplacian_triangle_weights(LaplacianSystem *sys, int f, int i1, int i2, int i3)
#define MESHDEFORM_LEN_THRESHOLD
static void heat_system_free(LaplacianSystem *sys)
#define C_WEIGHT
static int heat_ray_source_visible(LaplacianSystem *sys, int vertex, int source)
static void meshdeform_matrix_add_rhs(MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z, int cagevert)
void laplacian_add_vertex(LaplacianSystem *sys, float *co, int pinned)
#define DISTANCE_EPSILON
static void laplacian_increase_edge_count(blender::Map< blender::OrderedEdge, int > &edgehash, int v1, int v2)
#define MESHDEFORM_TAG_EXTERIOR
static float meshdeform_interp_w(MeshDeformBind *mdb, const float *gridvec, float *, int)
void laplacian_begin_solve(LaplacianSystem *sys, int index)
void corner_tris_calc(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, MutableSpan< int3 > corner_tris)
float vgroup_vert_weight(Object *ob, bDeformGroup *dg, int vertnum)
void vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight, int assignmode)
void vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum)
VecBase< int32_t, 3 > int3
LaplacianSystem * sys
float origin[3]
struct IsectRayPrecalc * isect_precalc
float direction[3]
const blender::int3 * corner_tris
blender::Span< int > corner_verts
const blender::int3 ** vltree
float(* fweights)[3]
blender::Map< blender::OrderedEdge, int > edgehash
struct LaplacianSystem::HeatWeighting heat
LinearSolver * context
MDefBindInfluence * next
float poly_weights[0]
float cagemat[4][4]
struct MeshDeformBind::@372076370303355101026327206103335165277374006253 cagemesh_cache
blender::Span< blender::int3 > corner_tris
blender::Span< int > corner_verts
const BVHTree * bvhtree
blender::Span< int > tri_faces
float(* vertexcos)[3]
blender::bke::BVHTreeFromMesh bvhdata
MemArena * memarena
blender::OffsetIndices< int > faces
MDefBoundIsect *(* boundisect)[6]
float(* cagecos)[3]
blender::Span< blender::float3 > face_normals
MDefBindInfluence ** dyngrid
MeshDeformBind * mdb
MeshDeformIsect * isec
int corners_num
char editflag
int faces_num
int verts_num
i
Definition text_draw.cc:230
uint len
#define N_(msgid)