This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/modifiers/intern/MOD_surfacedeform.c
Sergey Sharybin 59f6371a85 Fix T63252: Bind in Mesh Deform Modifier fails
A regression since 64c8d72ef1.

The solution is to force modifier evaluation for an evaluated
object, and let it to copy binding data back to original when
is being evaluated for binding.

Reviewers: brecht

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D4642
2019-04-04 15:49:30 +02:00

1265 lines
38 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2017, Blender Foundation.
*/
/** \file
* \ingroup modifiers
*/
#include "BLI_math.h"
#include "BLI_math_geom.h"
#include "BLI_task.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_bvhutils.h"
#include "BKE_mesh_runtime.h"
#include "BKE_editmesh.h"
#include "BKE_library.h"
#include "BKE_library_query.h"
#include "BKE_modifier.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "MEM_guardedalloc.h"
#include "MOD_util.h"
typedef struct SDefAdjacency {
struct SDefAdjacency *next;
unsigned int index;
} SDefAdjacency;
typedef struct SDefAdjacencyArray {
SDefAdjacency *first;
unsigned int num; /* Careful, this is twice the number of polygons (avoids an extra loop) */
} SDefAdjacencyArray;
typedef struct SDefEdgePolys {
unsigned int polys[2], num;
} SDefEdgePolys;
typedef struct SDefBindCalcData {
BVHTreeFromMesh * const treeData;
const SDefAdjacencyArray * const vert_edges;
const SDefEdgePolys * const edge_polys;
SDefVert * const bind_verts;
const MLoopTri * const looptri;
const MPoly * const mpoly;
const MEdge * const medge;
const MLoop * const mloop;
float (* const targetCos)[3];
float (* const vertexCos)[3];
float imat[4][4];
const float falloff;
int success;
} SDefBindCalcData;
typedef struct SDefBindPoly {
float (*coords)[3];
float (*coords_v2)[2];
float point_v2[2];
float weight_angular;
float weight_dist_proj;
float weight_dist;
float weight;
float scales[2];
float centroid[3];
float centroid_v2[2];
float normal[3];
float cent_edgemid_vecs_v2[2][2];
float edgemid_angle;
float point_edgemid_angles[2];
float corner_edgemid_angles[2];
float dominant_angle_weight;
unsigned int index;
unsigned int numverts;
unsigned int loopstart;
unsigned int edge_inds[2];
unsigned int edge_vert_inds[2];
unsigned int corner_ind;
unsigned int dominant_edge;
bool inside;
} SDefBindPoly;
typedef struct SDefBindWeightData {
SDefBindPoly *bind_polys;
unsigned int numpoly;
unsigned int numbinds;
} SDefBindWeightData;
typedef struct SDefDeformData {
const SDefVert * const bind_verts;
float (* const targetCos)[3];
float (* const vertexCos)[3];
} SDefDeformData;
/* Bind result values */
enum {
MOD_SDEF_BIND_RESULT_SUCCESS = 1,
MOD_SDEF_BIND_RESULT_GENERIC_ERR = 0,
MOD_SDEF_BIND_RESULT_MEM_ERR = -1,
MOD_SDEF_BIND_RESULT_NONMANY_ERR = -2,
MOD_SDEF_BIND_RESULT_CONCAVE_ERR = -3,
MOD_SDEF_BIND_RESULT_OVERLAP_ERR = -4,
};
/* Infinite weight flags */
enum {
MOD_SDEF_INFINITE_WEIGHT_ANGULAR = (1 << 0),
MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ = (1 << 1),
MOD_SDEF_INFINITE_WEIGHT_DIST = (1 << 2),
};
static void initData(ModifierData *md)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
smd->target = NULL;
smd->verts = NULL;
smd->flags = 0;
smd->falloff = 4.0f;
}
static void freeData(ModifierData *md)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
if (smd->verts) {
for (int i = 0; i < smd->numverts; i++) {
if (smd->verts[i].binds) {
for (int j = 0; j < smd->verts[i].numbinds; j++) {
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds);
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_weights);
}
MEM_SAFE_FREE(smd->verts[i].binds);
}
}
MEM_SAFE_FREE(smd->verts);
}
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
const SurfaceDeformModifierData *smd = (const SurfaceDeformModifierData *)md;
SurfaceDeformModifierData *tsmd = (SurfaceDeformModifierData *)target;
modifier_copyData_generic(md, target, flag);
if (smd->verts) {
tsmd->verts = MEM_dupallocN(smd->verts);
for (int i = 0; i < smd->numverts; i++) {
if (smd->verts[i].binds) {
tsmd->verts[i].binds = MEM_dupallocN(smd->verts[i].binds);
for (int j = 0; j < smd->verts[i].numbinds; j++) {
if (smd->verts[i].binds[j].vert_inds) {
tsmd->verts[i].binds[j].vert_inds = MEM_dupallocN(smd->verts[i].binds[j].vert_inds);
}
if (smd->verts[i].binds[j].vert_weights) {
tsmd->verts[i].binds[j].vert_weights = MEM_dupallocN(smd->verts[i].binds[j].vert_weights);
}
}
}
}
}
}
static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
walk(userData, ob, &smd->target, IDWALK_NOP);
}
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
if (smd->target != NULL) {
DEG_add_object_relation(ctx->node, smd->target, DEG_OB_COMP_GEOMETRY, "Surface Deform Modifier");
}
}
static void freeAdjacencyMap(SDefAdjacencyArray * const vert_edges, SDefAdjacency * const adj_ref, SDefEdgePolys * const edge_polys)
{
MEM_freeN(edge_polys);
MEM_freeN(adj_ref);
MEM_freeN(vert_edges);
}
static int buildAdjacencyMap(
const MPoly *poly, const MEdge *edge, const MLoop * const mloop, const unsigned int numpoly, const unsigned int numedges,
SDefAdjacencyArray * const vert_edges, SDefAdjacency *adj, SDefEdgePolys * const edge_polys)
{
const MLoop *loop;
/* Fing polygons adjacent to edges */
for (int i = 0; i < numpoly; i++, poly++) {
loop = &mloop[poly->loopstart];
for (int j = 0; j < poly->totloop; j++, loop++) {
if (edge_polys[loop->e].num == 0) {
edge_polys[loop->e].polys[0] = i;
edge_polys[loop->e].polys[1] = -1;
edge_polys[loop->e].num++;
}
else if (edge_polys[loop->e].num == 1) {
edge_polys[loop->e].polys[1] = i;
edge_polys[loop->e].num++;
}
else {
return MOD_SDEF_BIND_RESULT_NONMANY_ERR;
}
}
}
/* Find edges adjacent to vertices */
for (int i = 0; i < numedges; i++, edge++) {
adj->next = vert_edges[edge->v1].first;
adj->index = i;
vert_edges[edge->v1].first = adj;
vert_edges[edge->v1].num += edge_polys[i].num;
adj++;
adj->next = vert_edges[edge->v2].first;
adj->index = i;
vert_edges[edge->v2].first = adj;
vert_edges[edge->v2].num += edge_polys[i].num;
adj++;
}
return MOD_SDEF_BIND_RESULT_SUCCESS;
}
BLI_INLINE void sortPolyVertsEdge(unsigned int *indices, const MLoop * const mloop, const unsigned int edge, const unsigned int num)
{
bool found = false;
for (int i = 0; i < num; i++) {
if (mloop[i].e == edge) {
found = true;
}
if (found) {
*indices = mloop[i].v;
indices++;
}
}
/* Fill in remaining vertex indices that occur before the edge */
for (int i = 0; mloop[i].e != edge; i++) {
*indices = mloop[i].v;
indices++;
}
}
BLI_INLINE void sortPolyVertsTri(unsigned int *indices, const MLoop * const mloop, const unsigned int loopstart, const unsigned int num)
{
for (int i = loopstart; i < num; i++) {
*indices = mloop[i].v;
indices++;
}
for (int i = 0; i < loopstart; i++) {
*indices = mloop[i].v;
indices++;
}
}
BLI_INLINE unsigned int nearestVert(SDefBindCalcData * const data, const float point_co[3])
{
BVHTreeNearest nearest = { .dist_sq = FLT_MAX, .index = -1, };
const MPoly *poly;
const MEdge *edge;
const MLoop *loop;
float t_point[3];
float max_dist = FLT_MAX;
float dist;
unsigned int index = 0;
mul_v3_m4v3(t_point, data->imat, point_co);
BLI_bvhtree_find_nearest(data->treeData->tree, t_point, &nearest, data->treeData->nearest_callback, data->treeData);
poly = &data->mpoly[data->looptri[nearest.index].poly];
loop = &data->mloop[poly->loopstart];
for (int i = 0; i < poly->totloop; i++, loop++) {
edge = &data->medge[loop->e];
dist = dist_squared_to_line_segment_v3(point_co, data->targetCos[edge->v1], data->targetCos[edge->v2]);
if (dist < max_dist) {
max_dist = dist;
index = loop->e;
}
}
edge = &data->medge[index];
if (len_squared_v3v3(point_co, data->targetCos[edge->v1]) < len_squared_v3v3(point_co, data->targetCos[edge->v2])) {
return edge->v1;
}
else {
return edge->v2;
}
}
BLI_INLINE int isPolyValid(const float coords[][2], const unsigned int nr)
{
float prev_co[2];
float curr_vec[2], prev_vec[2];
if (!is_poly_convex_v2(coords, nr)) {
return MOD_SDEF_BIND_RESULT_CONCAVE_ERR;
}
copy_v2_v2(prev_co, coords[nr - 1]);
sub_v2_v2v2(prev_vec, prev_co, coords[nr - 2]);
normalize_v2(prev_vec);
for (int i = 0; i < nr; i++) {
sub_v2_v2v2(curr_vec, coords[i], prev_co);
const float curr_len = normalize_v2(curr_vec);
if (curr_len < FLT_EPSILON) {
return MOD_SDEF_BIND_RESULT_OVERLAP_ERR;
}
if (1.0f - dot_v2v2(prev_vec, curr_vec) < FLT_EPSILON) {
return MOD_SDEF_BIND_RESULT_CONCAVE_ERR;
}
copy_v2_v2(prev_co, coords[i]);
copy_v2_v2(prev_vec, curr_vec);
}
return MOD_SDEF_BIND_RESULT_SUCCESS;
}
static void freeBindData(SDefBindWeightData * const bwdata)
{
SDefBindPoly *bpoly = bwdata->bind_polys;
if (bwdata->bind_polys) {
for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
MEM_SAFE_FREE(bpoly->coords);
MEM_SAFE_FREE(bpoly->coords_v2);
}
MEM_freeN(bwdata->bind_polys);
}
MEM_freeN(bwdata);
}
BLI_INLINE float computeAngularWeight(const float point_angle, const float edgemid_angle)
{
float weight;
weight = point_angle;
weight /= edgemid_angle;
weight *= M_PI_2;
return sinf(weight);
}
BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData * const data, const float point_co[3])
{
const unsigned int nearest = nearestVert(data, point_co);
const SDefAdjacency * const vert_edges = data->vert_edges[nearest].first;
const SDefEdgePolys * const edge_polys = data->edge_polys;
const SDefAdjacency *vedge;
const MPoly *poly;
const MLoop *loop;
SDefBindWeightData *bwdata;
SDefBindPoly *bpoly;
float world[3] = {0.0f, 0.0f, 1.0f};
float avg_point_dist = 0.0f;
float tot_weight = 0.0f;
int inf_weight_flags = 0;
bwdata = MEM_callocN(sizeof(*bwdata), "SDefBindWeightData");
if (bwdata == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return NULL;
}
bwdata->numpoly = data->vert_edges[nearest].num / 2;
bpoly = MEM_calloc_arrayN(bwdata->numpoly, sizeof(*bpoly), "SDefBindPoly");
if (bpoly == NULL) {
freeBindData(bwdata);
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return NULL;
}
bwdata->bind_polys = bpoly;
/* Loop over all adjacent edges, and build the SDefBindPoly data for each poly adjacent to those */
for (vedge = vert_edges; vedge; vedge = vedge->next) {
unsigned int edge_ind = vedge->index;
for (int i = 0; i < edge_polys[edge_ind].num; i++) {
{
bpoly = bwdata->bind_polys;
for (int j = 0; j < bwdata->numpoly; bpoly++, j++) {
/* If coords isn't allocated, we have reached the first uninitialized bpoly */
if ((bpoly->index == edge_polys[edge_ind].polys[i]) || (!bpoly->coords)) {
break;
}
}
}
/* Check if poly was already created by another edge or still has to be initialized */
if (!bpoly->coords) {
float angle;
float axis[3];
float tmp_vec_v2[2];
int is_poly_valid;
bpoly->index = edge_polys[edge_ind].polys[i];
bpoly->coords = NULL;
bpoly->coords_v2 = NULL;
/* Copy poly data */
poly = &data->mpoly[bpoly->index];
loop = &data->mloop[poly->loopstart];
bpoly->numverts = poly->totloop;
bpoly->loopstart = poly->loopstart;
bpoly->coords = MEM_malloc_arrayN(poly->totloop, sizeof(*bpoly->coords), "SDefBindPolyCoords");
if (bpoly->coords == NULL) {
freeBindData(bwdata);
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return NULL;
}
bpoly->coords_v2 = MEM_malloc_arrayN(poly->totloop, sizeof(*bpoly->coords_v2), "SDefBindPolyCoords_v2");
if (bpoly->coords_v2 == NULL) {
freeBindData(bwdata);
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return NULL;
}
for (int j = 0; j < poly->totloop; j++, loop++) {
copy_v3_v3(bpoly->coords[j], data->targetCos[loop->v]);
/* Find corner and edge indices within poly loop array */
if (loop->v == nearest) {
bpoly->corner_ind = j;
bpoly->edge_vert_inds[0] = (j == 0) ? (poly->totloop - 1) : (j - 1);
bpoly->edge_vert_inds[1] = (j == poly->totloop - 1) ? (0) : (j + 1);
bpoly->edge_inds[0] = data->mloop[poly->loopstart + bpoly->edge_vert_inds[0]].e;
bpoly->edge_inds[1] = loop->e;
}
}
/* Compute poly's parametric data */
mid_v3_v3_array(bpoly->centroid, bpoly->coords, poly->totloop);
normal_poly_v3(bpoly->normal, bpoly->coords, poly->totloop);
/* Compute poly skew angle and axis */
angle = angle_normalized_v3v3(bpoly->normal, world);
cross_v3_v3v3(axis, bpoly->normal, world);
normalize_v3(axis);
/* Map coords onto 2d normal plane */
map_to_plane_axis_angle_v2_v3v3fl(bpoly->point_v2, point_co, axis, angle);
zero_v2(bpoly->centroid_v2);
for (int j = 0; j < poly->totloop; j++) {
map_to_plane_axis_angle_v2_v3v3fl(bpoly->coords_v2[j], bpoly->coords[j], axis, angle);
madd_v2_v2fl(bpoly->centroid_v2, bpoly->coords_v2[j], 1.0f / poly->totloop);
}
is_poly_valid = isPolyValid(bpoly->coords_v2, poly->totloop);
if (is_poly_valid != MOD_SDEF_BIND_RESULT_SUCCESS) {
freeBindData(bwdata);
data->success = is_poly_valid;
return NULL;
}
bpoly->inside = isect_point_poly_v2(bpoly->point_v2, bpoly->coords_v2, poly->totloop, false);
/* Initialize weight components */
bpoly->weight_angular = 1.0f;
bpoly->weight_dist_proj = len_v2v2(bpoly->centroid_v2, bpoly->point_v2);
bpoly->weight_dist = len_v3v3(bpoly->centroid, point_co);
avg_point_dist += bpoly->weight_dist;
/* Compute centroid to mid-edge vectors */
mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[0],
bpoly->coords_v2[bpoly->edge_vert_inds[0]],
bpoly->coords_v2[bpoly->corner_ind]);
mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[1],
bpoly->coords_v2[bpoly->edge_vert_inds[1]],
bpoly->coords_v2[bpoly->corner_ind]);
sub_v2_v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->centroid_v2);
sub_v2_v2(bpoly->cent_edgemid_vecs_v2[1], bpoly->centroid_v2);
/* Compute poly scales with respect to mid-edges, and normalize the vectors */
bpoly->scales[0] = normalize_v2(bpoly->cent_edgemid_vecs_v2[0]);
bpoly->scales[1] = normalize_v2(bpoly->cent_edgemid_vecs_v2[1]);
/* Compute the required polygon angles */
bpoly->edgemid_angle = angle_normalized_v2v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->cent_edgemid_vecs_v2[1]);
sub_v2_v2v2(tmp_vec_v2, bpoly->coords_v2[bpoly->corner_ind], bpoly->centroid_v2);
normalize_v2(tmp_vec_v2);
bpoly->corner_edgemid_angles[0] = angle_normalized_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[0]);
bpoly->corner_edgemid_angles[1] = angle_normalized_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[1]);
/* Check for inifnite weights, and compute angular data otherwise */
if (bpoly->weight_dist < FLT_EPSILON) {
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST;
}
else if (bpoly->weight_dist_proj < FLT_EPSILON) {
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
}
else {
float cent_point_vec[2];
sub_v2_v2v2(cent_point_vec, bpoly->point_v2, bpoly->centroid_v2);
normalize_v2(cent_point_vec);
bpoly->point_edgemid_angles[0] = angle_normalized_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[0]);
bpoly->point_edgemid_angles[1] = angle_normalized_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[1]);
}
}
}
}
avg_point_dist /= bwdata->numpoly;
/* If weights 1 and 2 are not infinite, loop over all adjacent edges again,
* and build adjacency dependent angle data (depends on all polygons having been computed) */
if (!inf_weight_flags) {
for (vedge = vert_edges; vedge; vedge = vedge->next) {
SDefBindPoly *bpolys[2];
const SDefEdgePolys *epolys;
float ang_weights[2];
unsigned int edge_ind = vedge->index;
unsigned int edge_on_poly[2];
epolys = &edge_polys[edge_ind];
/* Find bind polys corresponding to the edge's adjacent polys */
bpoly = bwdata->bind_polys;
for (int i = 0, j = 0; (i < bwdata->numpoly) && (j < epolys->num); bpoly++, i++) {
if (ELEM(bpoly->index, epolys->polys[0], epolys->polys[1])) {
bpolys[j] = bpoly;
if (bpoly->edge_inds[0] == edge_ind) {
edge_on_poly[j] = 0;
}
else {
edge_on_poly[j] = 1;
}
j++;
}
}
/* Compute angular weight component */
if (epolys->num == 1) {
ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]], bpolys[0]->edgemid_angle);
bpolys[0]->weight_angular *= ang_weights[0] * ang_weights[0];
}
else if (epolys->num == 2) {
ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]], bpolys[0]->edgemid_angle);
ang_weights[1] = computeAngularWeight(bpolys[1]->point_edgemid_angles[edge_on_poly[1]], bpolys[1]->edgemid_angle);
bpolys[0]->weight_angular *= ang_weights[0] * ang_weights[1];
bpolys[1]->weight_angular *= ang_weights[0] * ang_weights[1];
}
}
}
/* Compute scalings and falloff.
* Scale all weights if no infinite weight is found,
* scale only unprojected weight if projected weight is infinite,
* scale none if both are infinite. */
if (!inf_weight_flags) {
bpoly = bwdata->bind_polys;
for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
float corner_angle_weights[2];
float scale_weight, sqr, inv_sqr;
corner_angle_weights[0] = bpoly->point_edgemid_angles[0] / bpoly->corner_edgemid_angles[0];
corner_angle_weights[1] = bpoly->point_edgemid_angles[1] / bpoly->corner_edgemid_angles[1];
if (isnan(corner_angle_weights[0]) || isnan(corner_angle_weights[1])) {
freeBindData(bwdata);
data->success = MOD_SDEF_BIND_RESULT_GENERIC_ERR;
return NULL;
}
/* Find which edge the point is closer to */
if (corner_angle_weights[0] < corner_angle_weights[1]) {
bpoly->dominant_edge = 0;
bpoly->dominant_angle_weight = corner_angle_weights[0];
}
else {
bpoly->dominant_edge = 1;
bpoly->dominant_angle_weight = corner_angle_weights[1];
}
bpoly->dominant_angle_weight = sinf(bpoly->dominant_angle_weight * M_PI_2);
/* Compute quadratic angular scale interpolation weight */
scale_weight = bpoly->point_edgemid_angles[bpoly->dominant_edge] / bpoly->edgemid_angle;
scale_weight /= scale_weight + (bpoly->point_edgemid_angles[!bpoly->dominant_edge] / bpoly->edgemid_angle);
sqr = scale_weight * scale_weight;
inv_sqr = 1.0f - scale_weight;
inv_sqr *= inv_sqr;
scale_weight = sqr / (sqr + inv_sqr);
/* Compute interpolated scale (no longer need the individual scales,
* so simply storing the result over the scale in index zero) */
bpoly->scales[0] = bpoly->scales[bpoly->dominant_edge] * (1.0f - scale_weight) +
bpoly->scales[!bpoly->dominant_edge] * scale_weight;
/* Scale the point distance weights, and introduce falloff */
bpoly->weight_dist_proj /= bpoly->scales[0];
bpoly->weight_dist_proj = powf(bpoly->weight_dist_proj, data->falloff);
bpoly->weight_dist /= avg_point_dist;
bpoly->weight_dist = powf(bpoly->weight_dist, data->falloff);
/* Re-check for infinite weights, now that all scalings and interpolations are computed */
if (bpoly->weight_dist < FLT_EPSILON) {
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST;
}
else if (bpoly->weight_dist_proj < FLT_EPSILON) {
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
}
else if (bpoly->weight_angular < FLT_EPSILON) {
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_ANGULAR;
}
}
}
else if (!(inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_DIST)) {
bpoly = bwdata->bind_polys;
for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
/* Scale the point distance weight by average point distance, and introduce falloff */
bpoly->weight_dist /= avg_point_dist;
bpoly->weight_dist = powf(bpoly->weight_dist, data->falloff);
/* Re-check for infinite weights, now that all scalings and interpolations are computed */
if (bpoly->weight_dist < FLT_EPSILON) {
inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST;
}
}
}
/* Final loop, to compute actual weights */
bpoly = bwdata->bind_polys;
for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
/* Weight computation from components */
if (inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_DIST) {
bpoly->weight = bpoly->weight_dist < FLT_EPSILON ? 1.0f : 0.0f;
}
else if (inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ) {
bpoly->weight = bpoly->weight_dist_proj < FLT_EPSILON ?
1.0f / bpoly->weight_dist : 0.0f;
}
else if (inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_ANGULAR) {
bpoly->weight = bpoly->weight_angular < FLT_EPSILON ?
1.0f / bpoly->weight_dist_proj / bpoly->weight_dist : 0.0f;
}
else {
bpoly->weight = 1.0f / bpoly->weight_angular /
bpoly->weight_dist_proj /
bpoly->weight_dist;
}
tot_weight += bpoly->weight;
}
bpoly = bwdata->bind_polys;
for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
bpoly->weight /= tot_weight;
/* Evaluate if this poly is relevant to bind */
/* Even though the weights should add up to 1.0,
* the losses of weights smaller than epsilon here
* should be negligible... */
if (bpoly->weight >= FLT_EPSILON) {
if (bpoly->inside) {
bwdata->numbinds += 1;
}
else {
if (bpoly->dominant_angle_weight < FLT_EPSILON || 1.0f - bpoly->dominant_angle_weight < FLT_EPSILON) {
bwdata->numbinds += 1;
}
else {
bwdata->numbinds += 2;
}
}
}
}
return bwdata;
}
BLI_INLINE float computeNormalDisplacement(const float point_co[3], const float point_co_proj[3], const float normal[3])
{
float disp_vec[3];
float normal_dist;
sub_v3_v3v3(disp_vec, point_co, point_co_proj);
normal_dist = len_v3(disp_vec);
if (dot_v3v3(disp_vec, normal) < 0) {
normal_dist *= -1;
}
return normal_dist;
}
static void bindVert(
void *__restrict userdata,
const int index,
const ParallelRangeTLS *__restrict UNUSED(tls))
{
SDefBindCalcData * const data = (SDefBindCalcData *)userdata;
float point_co[3];
float point_co_proj[3];
SDefBindWeightData *bwdata;
SDefVert *sdvert = data->bind_verts + index;
SDefBindPoly *bpoly;
SDefBind *sdbind;
if (data->success != MOD_SDEF_BIND_RESULT_SUCCESS) {
sdvert->binds = NULL;
sdvert->numbinds = 0;
return;
}
copy_v3_v3(point_co, data->vertexCos[index]);
bwdata = computeBindWeights(data, point_co);
if (bwdata == NULL) {
sdvert->binds = NULL;
sdvert->numbinds = 0;
return;
}
sdvert->binds = MEM_calloc_arrayN(bwdata->numbinds, sizeof(*sdvert->binds), "SDefVertBindData");
if (sdvert->binds == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
sdvert->numbinds = 0;
return;
}
sdvert->numbinds = bwdata->numbinds;
sdbind = sdvert->binds;
bpoly = bwdata->bind_polys;
for (int i = 0; i < bwdata->numbinds; bpoly++) {
if (bpoly->weight >= FLT_EPSILON) {
if (bpoly->inside) {
const MLoop *loop = &data->mloop[bpoly->loopstart];
sdbind->influence = bpoly->weight;
sdbind->numverts = bpoly->numverts;
sdbind->mode = MOD_SDEF_MODE_NGON;
sdbind->vert_weights = MEM_malloc_arrayN(bpoly->numverts, sizeof(*sdbind->vert_weights), "SDefNgonVertWeights");
if (sdbind->vert_weights == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return;
}
sdbind->vert_inds = MEM_malloc_arrayN(bpoly->numverts, sizeof(*sdbind->vert_inds), "SDefNgonVertInds");
if (sdbind->vert_inds == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return;
}
interp_weights_poly_v2(sdbind->vert_weights, bpoly->coords_v2, bpoly->numverts, bpoly->point_v2);
/* Reproject vert based on weights and original poly verts, to reintroduce poly non-planarity */
zero_v3(point_co_proj);
for (int j = 0; j < bpoly->numverts; j++, loop++) {
madd_v3_v3fl(point_co_proj, bpoly->coords[j], sdbind->vert_weights[j]);
sdbind->vert_inds[j] = loop->v;
}
sdbind->normal_dist = computeNormalDisplacement(point_co, point_co_proj, bpoly->normal);
sdbind++;
i++;
}
else {
float tmp_vec[3];
float cent[3], norm[3];
float v1[3], v2[3], v3[3];
if (1.0f - bpoly->dominant_angle_weight >= FLT_EPSILON) {
sdbind->influence = bpoly->weight * (1.0f - bpoly->dominant_angle_weight);
sdbind->numverts = bpoly->numverts;
sdbind->mode = MOD_SDEF_MODE_CENTROID;
sdbind->vert_weights = MEM_malloc_arrayN(3, sizeof(*sdbind->vert_weights), "SDefCentVertWeights");
if (sdbind->vert_weights == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return;
}
sdbind->vert_inds = MEM_malloc_arrayN(bpoly->numverts, sizeof(*sdbind->vert_inds), "SDefCentVertInds");
if (sdbind->vert_inds == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return;
}
sortPolyVertsEdge(sdbind->vert_inds, &data->mloop[bpoly->loopstart],
bpoly->edge_inds[bpoly->dominant_edge], bpoly->numverts);
copy_v3_v3(v1, data->targetCos[sdbind->vert_inds[0]]);
copy_v3_v3(v2, data->targetCos[sdbind->vert_inds[1]]);
copy_v3_v3(v3, bpoly->centroid);
mid_v3_v3v3v3(cent, v1, v2, v3);
normal_tri_v3(norm, v1, v2, v3);
add_v3_v3v3(tmp_vec, point_co, bpoly->normal);
/* We are sure the line is not parallel to the plane.
* Checking return value just to avoid warning... */
if (!isect_line_plane_v3(point_co_proj, point_co, tmp_vec, cent, norm)) {
BLI_assert(false);
}
interp_weights_tri_v3(sdbind->vert_weights, v1, v2, v3, point_co_proj);
sdbind->normal_dist = computeNormalDisplacement(point_co, point_co_proj, bpoly->normal);
sdbind++;
i++;
}
if (bpoly->dominant_angle_weight >= FLT_EPSILON) {
sdbind->influence = bpoly->weight * bpoly->dominant_angle_weight;
sdbind->numverts = bpoly->numverts;
sdbind->mode = MOD_SDEF_MODE_LOOPTRI;
sdbind->vert_weights = MEM_malloc_arrayN(3, sizeof(*sdbind->vert_weights), "SDefTriVertWeights");
if (sdbind->vert_weights == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return;
}
sdbind->vert_inds = MEM_malloc_arrayN(bpoly->numverts, sizeof(*sdbind->vert_inds), "SDefTriVertInds");
if (sdbind->vert_inds == NULL) {
data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
return;
}
sortPolyVertsTri(sdbind->vert_inds, &data->mloop[bpoly->loopstart], bpoly->edge_vert_inds[0], bpoly->numverts);
copy_v3_v3(v1, data->targetCos[sdbind->vert_inds[0]]);
copy_v3_v3(v2, data->targetCos[sdbind->vert_inds[1]]);
copy_v3_v3(v3, data->targetCos[sdbind->vert_inds[2]]);
mid_v3_v3v3v3(cent, v1, v2, v3);
normal_tri_v3(norm, v1, v2, v3);
add_v3_v3v3(tmp_vec, point_co, bpoly->normal);
/* We are sure the line is not parallel to the plane.
* Checking return value just to avoid warning... */
if (!isect_line_plane_v3(point_co_proj, point_co, tmp_vec, cent, norm)) {
BLI_assert(false);
}
interp_weights_tri_v3(sdbind->vert_weights, v1, v2, v3, point_co_proj);
sdbind->normal_dist = computeNormalDisplacement(point_co, point_co_proj, bpoly->normal);
sdbind++;
i++;
}
}
}
}
freeBindData(bwdata);
}
static bool surfacedeformBind(
SurfaceDeformModifierData *smd, float (*vertexCos)[3],
unsigned int numverts, unsigned int tnumpoly, unsigned int tnumverts, Mesh *target)
{
BVHTreeFromMesh treeData = {NULL};
const MVert *mvert = target->mvert;
const MPoly *mpoly = target->mpoly;
const MEdge *medge = target->medge;
const MLoop *mloop = target->mloop;
unsigned int tnumedges = target->totedge;
int adj_result;
SDefAdjacencyArray *vert_edges;
SDefAdjacency *adj_array;
SDefEdgePolys *edge_polys;
vert_edges = MEM_calloc_arrayN(tnumverts, sizeof(*vert_edges), "SDefVertEdgeMap");
if (vert_edges == NULL) {
modifier_setError((ModifierData *)smd, "Out of memory");
return false;
}
adj_array = MEM_malloc_arrayN(tnumedges, 2 * sizeof(*adj_array), "SDefVertEdge");
if (adj_array == NULL) {
modifier_setError((ModifierData *)smd, "Out of memory");
MEM_freeN(vert_edges);
return false;
}
edge_polys = MEM_calloc_arrayN(tnumedges, sizeof(*edge_polys), "SDefEdgeFaceMap");
if (edge_polys == NULL) {
modifier_setError((ModifierData *)smd, "Out of memory");
MEM_freeN(vert_edges);
MEM_freeN(adj_array);
return false;
}
smd->verts = MEM_malloc_arrayN(numverts, sizeof(*smd->verts), "SDefBindVerts");
if (smd->verts == NULL) {
modifier_setError((ModifierData *)smd, "Out of memory");
freeAdjacencyMap(vert_edges, adj_array, edge_polys);
return false;
}
BKE_bvhtree_from_mesh_get(&treeData, target, BVHTREE_FROM_LOOPTRI, 2);
if (treeData.tree == NULL) {
modifier_setError((ModifierData *)smd, "Out of memory");
freeAdjacencyMap(vert_edges, adj_array, edge_polys);
MEM_freeN(smd->verts);
smd->verts = NULL;
return false;
}
adj_result = buildAdjacencyMap(mpoly, medge, mloop, tnumpoly, tnumedges, vert_edges, adj_array, edge_polys);
if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) {
modifier_setError((ModifierData *)smd, "Target has edges with more than two polygons");
freeAdjacencyMap(vert_edges, adj_array, edge_polys);
free_bvhtree_from_mesh(&treeData);
MEM_freeN(smd->verts);
smd->verts = NULL;
return false;
}
smd->numverts = numverts;
smd->numpoly = tnumpoly;
SDefBindCalcData data = {
.treeData = &treeData,
.vert_edges = vert_edges,
.edge_polys = edge_polys,
.mpoly = mpoly,
.medge = medge,
.mloop = mloop,
.looptri = BKE_mesh_runtime_looptri_ensure(target),
.targetCos = MEM_malloc_arrayN(tnumverts, sizeof(float[3]), "SDefTargetBindVertArray"),
.bind_verts = smd->verts,
.vertexCos = vertexCos,
.falloff = smd->falloff,
.success = MOD_SDEF_BIND_RESULT_SUCCESS,
};
if (data.targetCos == NULL) {
modifier_setError((ModifierData *)smd, "Out of memory");
freeData((ModifierData *)smd);
return false;
}
invert_m4_m4(data.imat, smd->mat);
for (int i = 0; i < tnumverts; i++) {
mul_v3_m4v3(data.targetCos[i], smd->mat, mvert[i].co);
}
ParallelRangeSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (numverts > 10000);
BLI_task_parallel_range(0, numverts,
&data,
bindVert,
&settings);
MEM_freeN(data.targetCos);
if (data.success == MOD_SDEF_BIND_RESULT_MEM_ERR) {
modifier_setError((ModifierData *)smd, "Out of memory");
freeData((ModifierData *)smd);
}
else if (data.success == MOD_SDEF_BIND_RESULT_NONMANY_ERR) {
modifier_setError((ModifierData *)smd, "Target has edges with more than two polygons");
freeData((ModifierData *)smd);
}
else if (data.success == MOD_SDEF_BIND_RESULT_CONCAVE_ERR) {
modifier_setError((ModifierData *)smd, "Target contains concave polygons");
freeData((ModifierData *)smd);
}
else if (data.success == MOD_SDEF_BIND_RESULT_OVERLAP_ERR) {
modifier_setError((ModifierData *)smd, "Target contains overlapping verts");
freeData((ModifierData *)smd);
}
else if (data.success == MOD_SDEF_BIND_RESULT_GENERIC_ERR) {
/* I know this message is vague, but I could not think of a way
* to explain this with a reasonably sized message.
* Though it shouldn't really matter all that much,
* because this is very unlikely to occur */
modifier_setError((ModifierData *)smd, "Target contains invalid polygons");
freeData((ModifierData *)smd);
}
freeAdjacencyMap(vert_edges, adj_array, edge_polys);
free_bvhtree_from_mesh(&treeData);
return data.success == 1;
}
static void deformVert(
void *__restrict userdata,
const int index,
const ParallelRangeTLS *__restrict UNUSED(tls))
{
const SDefDeformData * const data = (SDefDeformData *)userdata;
const SDefBind *sdbind = data->bind_verts[index].binds;
float * const vertexCos = data->vertexCos[index];
float norm[3], temp[3];
zero_v3(vertexCos);
for (int j = 0; j < data->bind_verts[index].numbinds; j++, sdbind++) {
/* Mode-generic operations (allocate poly coordinates) */
float (*coords)[3] = MEM_malloc_arrayN(sdbind->numverts, sizeof(*coords), "SDefDoPolyCoords");
for (int k = 0; k < sdbind->numverts; k++) {
copy_v3_v3(coords[k], data->targetCos[sdbind->vert_inds[k]]);
}
normal_poly_v3(norm, coords, sdbind->numverts);
zero_v3(temp);
/* ---------- looptri mode ---------- */
if (sdbind->mode == MOD_SDEF_MODE_LOOPTRI) {
madd_v3_v3fl(temp, data->targetCos[sdbind->vert_inds[0]], sdbind->vert_weights[0]);
madd_v3_v3fl(temp, data->targetCos[sdbind->vert_inds[1]], sdbind->vert_weights[1]);
madd_v3_v3fl(temp, data->targetCos[sdbind->vert_inds[2]], sdbind->vert_weights[2]);
}
else {
/* ---------- ngon mode ---------- */
if (sdbind->mode == MOD_SDEF_MODE_NGON) {
for (int k = 0; k < sdbind->numverts; k++) {
madd_v3_v3fl(temp, coords[k], sdbind->vert_weights[k]);
}
}
/* ---------- centroid mode ---------- */
else if (sdbind->mode == MOD_SDEF_MODE_CENTROID) {
float cent[3];
mid_v3_v3_array(cent, coords, sdbind->numverts);
madd_v3_v3fl(temp, data->targetCos[sdbind->vert_inds[0]], sdbind->vert_weights[0]);
madd_v3_v3fl(temp, data->targetCos[sdbind->vert_inds[1]], sdbind->vert_weights[1]);
madd_v3_v3fl(temp, cent, sdbind->vert_weights[2]);
}
}
MEM_freeN(coords);
/* Apply normal offset (generic for all modes) */
madd_v3_v3fl(temp, norm, sdbind->normal_dist);
madd_v3_v3fl(vertexCos, temp, sdbind->influence);
}
}
static void surfacedeformModifier_do(
ModifierData *md,
const ModifierEvalContext *ctx,
float (*vertexCos)[3], unsigned int numverts, Object *ob)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
Mesh *target;
unsigned int tnumverts, tnumpoly;
/* Exit function if bind flag is not set (free bind data if any). */
if (!(smd->flags & MOD_SDEF_BIND)) {
if (smd->verts != NULL) {
if (!DEG_is_active(ctx->depsgraph)) {
modifier_setError(md, "Attempt to bind from inactive dependency graph");
return;
}
ModifierData *md_orig = modifier_get_original(md);
freeData(md_orig);
}
return;
}
Object *ob_target = smd->target;
target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
if (!target) {
modifier_setError(md, "No valid target mesh");
return;
}
tnumverts = target->totvert;
tnumpoly = target->totpoly;
/* If not bound, execute bind. */
if (smd->verts == NULL) {
if (!DEG_is_active(ctx->depsgraph)) {
modifier_setError(md, "Attempt to unbind from inactive dependency graph");
return;
}
SurfaceDeformModifierData *smd_orig = (SurfaceDeformModifierData *)modifier_get_original(md);
float tmp_mat[4][4];
invert_m4_m4(tmp_mat, ob->obmat);
mul_m4_m4m4(smd_orig->mat, tmp_mat, ob_target->obmat);
if (!surfacedeformBind(smd_orig, vertexCos, numverts, tnumpoly, tnumverts, target)) {
smd->flags &= ~MOD_SDEF_BIND;
}
/* Early abort, this is binding 'call', no need to perform whole evaluation. */
return;
}
/* Poly count checks */
if (smd->numverts != numverts) {
modifier_setError(md, "Verts changed from %u to %u", smd->numverts, numverts);
return;
}
else if (smd->numpoly != tnumpoly) {
modifier_setError(md, "Target polygons changed from %u to %u", smd->numpoly, tnumpoly);
return;
}
/* Actual vertex location update starts here */
SDefDeformData data = {
.bind_verts = smd->verts,
.targetCos = MEM_malloc_arrayN(tnumverts, sizeof(float[3]), "SDefTargetVertArray"),
.vertexCos = vertexCos,
};
if (data.targetCos != NULL) {
const MVert * const mvert = target->mvert;
for (int i = 0; i < tnumverts; i++) {
mul_v3_m4v3(data.targetCos[i], smd->mat, mvert[i].co);
}
ParallelRangeSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (numverts > 10000);
BLI_task_parallel_range(0, numverts,
&data,
deformVert,
&settings);
MEM_freeN(data.targetCos);
}
}
static void deformVerts(
ModifierData *md, const ModifierEvalContext *ctx,
Mesh *UNUSED(mesh),
float (*vertexCos)[3], int numVerts)
{
surfacedeformModifier_do(md, ctx, vertexCos, numVerts, ctx->object);
}
static void deformVertsEM(
ModifierData *md, const ModifierEvalContext *ctx,
struct BMEditMesh *UNUSED(editData),
Mesh *UNUSED(mesh),
float (*vertexCos)[3], int numVerts)
{
surfacedeformModifier_do(md, ctx, vertexCos, numVerts, ctx->object);
}
static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams))
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
return smd->target == NULL && !(smd->verts != NULL && !(smd->flags & MOD_SDEF_BIND));
}
ModifierTypeInfo modifierType_SurfaceDeform = {
/* name */ "Surface Deform",
/* structName */ "SurfaceDeformModifierData",
/* structSize */ sizeof(SurfaceDeformModifierData),
/* type */ eModifierTypeType_OnlyDeform,
/* flags */ eModifierTypeFlag_AcceptsMesh |
eModifierTypeFlag_SupportsEditmode,
/* copyData */ copyData,
/* deformVerts */ deformVerts,
/* deformMatrices */ NULL,
/* deformVertsEM */ deformVertsEM,
/* deformMatricesEM */ NULL,
/* applyModifier */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
/* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* dependsOnNormals */ NULL,
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
};