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
This commit is contained in:
Sergey Sharybin 2019-04-04 14:42:33 +02:00
parent c81eca3d98
commit 59f6371a85
Notes: blender-bot 2023-02-14 06:42:54 +01:00
Referenced by issue #63252, Bind in Mesh Deform Modifier fails
7 changed files with 130 additions and 89 deletions

View File

@ -403,6 +403,16 @@ void modifier_path_init(char *path, int path_maxlen, const char *name);
const char *modifier_path_relbase(struct Main *bmain, struct Object *ob);
const char *modifier_path_relbase_from_global(struct Object *ob);
/* Accessors of original/evaluated modifiers. */
/* For a given modifier data, get corresponding original one.
* If the modifier data is already original, return it as-is. */
struct ModifierData *modifier_get_original(struct ModifierData *md);
struct ModifierData *modifier_get_evaluated(
struct Depsgraph* depsgraph,
struct Object *object,
struct ModifierData *md);
/* wrappers for modifier callbacks that ensure valid normals */
struct Mesh *modwrap_applyModifier(

View File

@ -885,3 +885,23 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval, con
return me;
}
ModifierData *modifier_get_original(ModifierData *md)
{
if (md->orig_modifier_data == NULL) {
return md;
}
return md->orig_modifier_data;
}
struct ModifierData *modifier_get_evaluated(
Depsgraph* depsgraph,
Object *object,
ModifierData *md)
{
Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
if (object_eval == object) {
return md;
}
return modifiers_findByName(object_eval, md->name);
}

View File

@ -1577,6 +1577,8 @@ void ED_mesh_deform_bind_callback(
MeshDeformModifierData *mmd, Mesh *cagemesh,
float *vertexcos, int totvert, float cagemat[4][4])
{
MeshDeformModifierData *mmd_orig =
(MeshDeformModifierData *)modifier_get_original(&mmd->modifier);
MeshDeformBind mdb;
MVert *mvert;
int a;
@ -1602,23 +1604,23 @@ void ED_mesh_deform_bind_callback(
mul_v3_m4v3(mdb.vertexcos[a], mdb.cagemat, vertexcos + a * 3);
/* solve */
harmonic_coordinates_bind(mmd, &mdb);
harmonic_coordinates_bind(mmd_orig, &mdb);
/* assign bind variables */
mmd->bindcagecos = (float *)mdb.cagecos;
mmd->totvert = mdb.totvert;
mmd->totcagevert = mdb.totcagevert;
copy_m4_m4(mmd->bindmat, mmd->object->obmat);
mmd_orig->bindcagecos = (float *)mdb.cagecos;
mmd_orig->totvert = mdb.totvert;
mmd_orig->totcagevert = mdb.totcagevert;
copy_m4_m4(mmd_orig->bindmat, mmd_orig->object->obmat);
/* transform bindcagecos to world space */
for (a = 0; a < mdb.totcagevert; a++)
mul_m4_v3(mmd->object->obmat, mmd->bindcagecos + a * 3);
mul_m4_v3(mmd_orig->object->obmat, mmd_orig->bindcagecos + a * 3);
/* free */
MEM_freeN(mdb.vertexcos);
/* compact weights */
modifier_mdef_compact_influences((ModifierData *)mmd);
modifier_mdef_compact_influences((ModifierData *)mmd_orig);
end_progress_bar();
waitcursor(0);

View File

@ -93,23 +93,38 @@ static void modifier_skin_customdata_delete(struct Object *ob);
/******************************** API ****************************/
static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Scene *scene, Object *ob)
static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *ob)
{
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
BKE_object_eval_reset(ob_eval);
if (ob->type == OB_MESH) {
Mesh *me_eval = mesh_create_eval_final_view(depsgraph, scene, ob, &CD_MASK_BAREMESH);
Mesh *me_eval = mesh_create_eval_final_view(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
BKE_id_free(NULL, me_eval);
}
else if (ob->type == OB_LATTICE) {
BKE_lattice_modifiers_calc(depsgraph, scene, ob);
BKE_lattice_modifiers_calc(depsgraph, scene_eval, ob_eval);
}
else if (ob->type == OB_MBALL) {
BKE_displist_make_mball(depsgraph, scene, ob);
BKE_displist_make_mball(depsgraph, scene_eval, ob_eval);
}
else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
BKE_displist_make_curveTypes(depsgraph, scene, ob, false, false, NULL);
BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false, false, NULL);
}
}
static void object_force_modifier_bind_simple_options(
Depsgraph *depsgraph,
Object *object,
ModifierData *md)
{
ModifierData *md_eval = (ModifierData *)modifier_get_evaluated(depsgraph, object, md);
const int mode = md_eval->mode;
md_eval->mode |= eModifierMode_Realtime;
object_force_modifier_update_for_bind(depsgraph, object);
md_eval->mode = mode;
}
ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type)
{
ModifierData *md = NULL, *new_md = NULL;
@ -1926,19 +1941,17 @@ static int correctivesmooth_bind_exec(bContext *C, wmOperator *op)
csmd->bind_coords_num = 0;
}
else {
/* signal to modifier to recalculate */
csmd->bind_coords_num = (unsigned int)-1;
/* Signal to modifier to recalculate. */
CorrectiveSmoothModifierData *csmd_eval =
(CorrectiveSmoothModifierData *)modifier_get_evaluated(depsgraph, ob, &csmd->modifier);
csmd_eval->bind_coords_num = (unsigned int)-1;
/* Force modifier to run, it will call binding routine
* (this has to happen outside of depsgraph evaluation). */
const int mode = csmd->modifier.mode;
csmd->modifier.mode |= eModifierMode_Realtime;
object_force_modifier_update_for_bind(depsgraph, scene, ob);
csmd->modifier.mode = mode;
object_force_modifier_bind_simple_options(depsgraph, ob, &csmd->modifier);
}
/* We need ID_RECALC_COPY_ON_WRITE to ensure (un)binding is flushed to CoW copies of the object... */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
@ -1979,7 +1992,6 @@ static bool meshdeform_poll(bContext *C)
static int meshdeform_bind_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
MeshDeformModifierData *mmd = (MeshDeformModifierData *)edit_modifier_property_get(op, ob, eModifierType_MeshDeform);
@ -2002,16 +2014,14 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op)
}
else {
/* Force modifier to run, it will call binding routine (this has to happen outside of depsgraph evaluation). */
const int mode = mmd->modifier.mode;
mmd->modifier.mode |= eModifierMode_Realtime;
mmd->bindfunc = ED_mesh_deform_bind_callback;
object_force_modifier_update_for_bind(depsgraph, scene, ob);
mmd->modifier.mode = mode;
mmd->bindfunc = NULL;
MeshDeformModifierData *mmd_eval =
(MeshDeformModifierData *)modifier_get_evaluated(depsgraph, ob, &mmd->modifier);
mmd_eval->bindfunc = ED_mesh_deform_bind_callback;
object_force_modifier_bind_simple_options(depsgraph, ob, &mmd->modifier);
mmd_eval->bindfunc = NULL;
}
/* We need ID_RECALC_COPY_ON_WRITE to ensure (un)binding is flushed to CoW copies of the object... */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
@ -2188,7 +2198,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op)
if (free) {
BKE_ocean_free_modifier_cache(omd);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
@ -2288,7 +2298,6 @@ static bool laplaciandeform_poll(bContext *C)
static int laplaciandeform_bind_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
Depsgraph *depsgraph = CTX_data_depsgraph(C);
LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)edit_modifier_property_get(op, ob, eModifierType_LaplacianDeform);
@ -2304,15 +2313,25 @@ static int laplaciandeform_bind_exec(bContext *C, wmOperator *op)
lmd->flag |= MOD_LAPLACIANDEFORM_BIND;
}
LaplacianDeformModifierData *lmd_eval =
(LaplacianDeformModifierData *)modifier_get_evaluated(depsgraph, ob, &lmd->modifier);
lmd_eval->flag = lmd->flag;
/* Force modifier to run, it will call binding routine
* (this has to happen outside of depsgraph evaluation). */
const int mode = lmd->modifier.mode;
lmd->modifier.mode |= eModifierMode_Realtime;
object_force_modifier_update_for_bind(depsgraph, scene, ob);
lmd->modifier.mode = mode;
object_force_modifier_bind_simple_options(depsgraph, ob, &lmd->modifier);
/* We need ID_RECALC_COPY_ON_WRITE to ensure (un)binding is flushed to CoW copies of the object... */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
/* This is hard to know from the modifier itself whether the evaluation is
* happening for binding or not. So we copy all the required data here. */
lmd->total_verts = lmd_eval->total_verts;
if (lmd_eval->vertexco == NULL) {
MEM_SAFE_FREE(lmd->vertexco);
}
else {
lmd->vertexco = MEM_dupallocN(lmd_eval->vertexco);
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
@ -2351,7 +2370,6 @@ static bool surfacedeform_bind_poll(bContext *C)
static int surfacedeform_bind_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
Depsgraph *depsgraph = CTX_data_depsgraph(C);
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)edit_modifier_property_get(op, ob, eModifierType_SurfaceDeform);
@ -2367,15 +2385,15 @@ static int surfacedeform_bind_exec(bContext *C, wmOperator *op)
smd->flags |= MOD_SDEF_BIND;
}
SurfaceDeformModifierData *smd_eval =
(SurfaceDeformModifierData *)modifier_get_evaluated(depsgraph, ob, &smd->modifier);
smd_eval->flags = smd->flags;
/* Force modifier to run, it will call binding routine
* (this has to happen outside of depsgraph evaluation). */
const int mode = smd->modifier.mode;
smd->modifier.mode |= eModifierMode_Realtime;
object_force_modifier_update_for_bind(depsgraph, scene, ob);
smd->modifier.mode = mode;
object_force_modifier_bind_simple_options(depsgraph, ob, &smd->modifier);
/* We need ID_RECALC_COPY_ON_WRITE to ensure (un)binding is flushed to CoW copies of the object... */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}

View File

@ -45,6 +45,9 @@
#include "BLI_strict_flags.h"
#include "DEG_depsgraph_query.h"
// #define DEBUG_TIME
#include "PIL_time.h"
@ -558,7 +561,7 @@ static void calc_deltas(
static void correctivesmooth_modifier_do(
ModifierData *md, Object *ob, Mesh *mesh,
ModifierData *md, Depsgraph *depsgraph, Object *ob, Mesh *mesh,
float (*vertexCos)[3], unsigned int numVerts,
struct BMEditMesh *em)
{
@ -580,10 +583,20 @@ static void correctivesmooth_modifier_do(
/* signal to recalculate, whoever sets MUST also free bind coords */
(csmd->bind_coords_num == (unsigned int)-1))
{
BLI_assert(csmd->bind_coords == NULL);
csmd->bind_coords = MEM_dupallocN(vertexCos);
csmd->bind_coords_num = numVerts;
BLI_assert(csmd->bind_coords != NULL);
if (DEG_is_active(depsgraph)) {
BLI_assert(csmd->bind_coords == NULL);
csmd->bind_coords = MEM_dupallocN(vertexCos);
csmd->bind_coords_num = numVerts;
BLI_assert(csmd->bind_coords != NULL);
/* Copy bound data to the original modifier. */
CorrectiveSmoothModifierData *csmd_orig =
(CorrectiveSmoothModifierData *)modifier_get_original(&csmd->modifier);
csmd_orig->bind_coords = MEM_dupallocN(csmd->bind_coords);
csmd_orig->bind_coords_num = csmd->bind_coords_num;
}
else {
modifier_setError(md, "Attempt to bind from inactive dependency graph");
}
}
if (UNLIKELY(use_only_smooth)) {
@ -711,7 +724,7 @@ static void deformVerts(
{
Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, numVerts, false, false);
correctivesmooth_modifier_do(md, ctx->object, mesh_src, vertexCos, (unsigned int)numVerts, NULL);
correctivesmooth_modifier_do(md, ctx->depsgraph, ctx->object, mesh_src, vertexCos, (unsigned int)numVerts, NULL);
if (mesh_src != mesh) {
BKE_id_free(NULL, mesh_src);
@ -725,7 +738,7 @@ static void deformVertsEM(
{
Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, numVerts, false, false);
correctivesmooth_modifier_do(md, ctx->object, mesh_src, vertexCos, (unsigned int)numVerts, editData);
correctivesmooth_modifier_do(md, ctx->depsgraph, ctx->object, mesh_src, vertexCos, (unsigned int)numVerts, editData);
if (mesh_src != mesh) {
BKE_id_free(NULL, mesh_src);

View File

@ -298,14 +298,6 @@ static void meshdeformModifier_do(
*/
Object *ob_target = mmd->object;
cagemesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
#if 0 /* This shall not be needed if we always get evaluated target object... */
if (cagemesh == NULL && mmd->bindcagecos == NULL && ob == DEG_get_original_object(ob)) {
/* Special case, binding happens outside of depsgraph evaluation, so we can build our own
* target mesh if needed. */
cagemesh = mesh_create_eval_final_view(ctx->depsgraph, DEG_get_input_scene(ctx->depsgraph), mmd->object, &CD_MASK_BAREMESH);
free_cagemesh = cagemesh != NULL;
}
#endif
if (cagemesh == NULL) {
modifier_setError(md, "Cannot get mesh from cage object");
return;
@ -321,13 +313,11 @@ static void meshdeformModifier_do(
/* bind weights if needed */
if (!mmd->bindcagecos) {
/* progress bar redraw can make this recursive .. */
if (!DEG_is_active(ctx->depsgraph)) {
modifier_setError(md, "Attempt to bind from inactive dependency graph");
goto finally;
}
if (!recursive_bind_sentinel) {
if (ob != DEG_get_original_object(ob)) {
BLI_assert(!"Trying to bind inside of depsgraph evaluation");
modifier_setError(md, "Trying to bind inside of depsgraph evaluation");
goto finally;
}
recursive_bind_sentinel = 1;
mmd->bindfunc(mmd, cagemesh, (float *)vertexCos, numVerts, cagemat);
recursive_bind_sentinel = 0;

View File

@ -1124,7 +1124,7 @@ static void deformVert(
static void surfacedeformModifier_do(
ModifierData *md,
const ModifierEvalContext *UNUSED(ctx),
const ModifierEvalContext *ctx,
float (*vertexCos)[3], unsigned int numverts, Object *ob)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
@ -1133,30 +1133,19 @@ static void surfacedeformModifier_do(
/* Exit function if bind flag is not set (free bind data if any). */
if (!(smd->flags & MOD_SDEF_BIND)) {
/* Note: with new CoW system, we expect unbinding to be done by a special call from main thread,
* outside of depsgraph evaluation (see object_force_modifier_update_for_bind() in object_modifier.c). */
if (smd->verts != NULL) {
if (ob != DEG_get_original_object(ob)) {
BLI_assert(!"Trying to unbind inside of depsgraph evaluation");
modifier_setError(md, "Trying to unbind inside of depsgraph evaluation");
}
else {
freeData(md);
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 0 /* Should not be needed anymore since we always get that mesh from eval object ? */
if (target == NULL && smd->verts == NULL && ob == DEG_get_original_object(ob)) {
/* Special case, binding happens outside of depsgraph evaluation, so we can build our own
* target mesh if needed. */
target = mesh_create_eval_final_view(ctx->depsgraph, DEG_get_input_scene(ctx->depsgraph), smd->target, CD_MASK_BAREMESH);
free_target = target != NULL;
}
#endif
if (!target) {
modifier_setError(md, "No valid target mesh");
return;
@ -1166,20 +1155,19 @@ static void surfacedeformModifier_do(
tnumpoly = target->totpoly;
/* If not bound, execute bind. */
/* Note: with new CoW system, we expect binding to be done by a special call from main thread,
* outside of depsgraph evaluation (see object_force_modifier_update_for_bind() in object_modifier.c). */
if (smd->verts == NULL) {
if (ob != DEG_get_original_object(ob)) {
BLI_assert(!"Trying to bind inside of depsgraph evaluation");
modifier_setError(md, "Trying to bind inside of depsgraph evaluation");
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->mat, tmp_mat, ob_target->obmat);
mul_m4_m4m4(smd_orig->mat, tmp_mat, ob_target->obmat);
if (!surfacedeformBind(smd, vertexCos, numverts, tnumpoly, tnumverts, target)) {
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. */