From bd211f9523e1e2d90034d16d9e823a6aeb2e3ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 2 Mar 2023 16:53:25 +0100 Subject: [PATCH 1/2] Remove the Weight Paint: Fix Deforms operator The code was hard to read and highly inefficient, doing `O(num_vertices * num_vertex_groups)` evaluations of the mesh. It caused multiple issues and got in the way of improvements to Blender, and very little if ever used by people. --- scripts/modules/rna_manual_reference.py | 1 - scripts/startup/bl_ui/space_view3d.py | 1 - source/blender/editors/object/object_intern.h | 1 - source/blender/editors/object/object_ops.c | 1 - .../blender/editors/object/object_vgroup.cc | 442 +----------------- 5 files changed, 1 insertion(+), 445 deletions(-) diff --git a/scripts/modules/rna_manual_reference.py b/scripts/modules/rna_manual_reference.py index bb5943b2adf..d4350bc663f 100644 --- a/scripts/modules/rna_manual_reference.py +++ b/scripts/modules/rna_manual_reference.py @@ -1899,7 +1899,6 @@ url_manual_mapping = ( ("bpy.ops.object.shape_key_remove*", "animation/shape_keys/shape_keys_panel.html#bpy-ops-object-shape-key-remove"), ("bpy.ops.object.shape_key_retime*", "animation/shape_keys/shape_keys_panel.html#bpy-ops-object-shape-key-retime"), ("bpy.ops.object.vertex_group_add*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-add"), - ("bpy.ops.object.vertex_group_fix*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-fix"), ("bpy.ops.outliner.collection_new*", "editors/outliner/editing.html#bpy-ops-outliner-collection-new"), ("bpy.ops.outliner.show_hierarchy*", "editors/outliner/editing.html#bpy-ops-outliner-show-hierarchy"), ("bpy.ops.outliner.show_one_level*", "editors/outliner/editing.html#bpy-ops-outliner-show-one-level"), diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 682eb16dbf9..8a1877126f5 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -3201,7 +3201,6 @@ class VIEW3D_MT_paint_weight(Menu): props.data_type = 'VGROUP_WEIGHTS' layout.operator("object.vertex_group_limit_total", text="Limit Total") - layout.operator("object.vertex_group_fix", text="Fix Deforms") if not is_editmode: layout.separator() diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index c58ce0d8424..851ece42ccb 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -287,7 +287,6 @@ void OBJECT_OT_vertex_group_normalize(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_normalize_all(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_levels(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_lock(struct wmOperatorType *ot); -void OBJECT_OT_vertex_group_fix(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_invert(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_smooth(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_clean(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index c8e06ef97c7..96cf2c6628e 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -208,7 +208,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_vertex_group_normalize); WM_operatortype_append(OBJECT_OT_vertex_group_normalize_all); WM_operatortype_append(OBJECT_OT_vertex_group_lock); - WM_operatortype_append(OBJECT_OT_vertex_group_fix); WM_operatortype_append(OBJECT_OT_vertex_group_invert); WM_operatortype_append(OBJECT_OT_vertex_group_levels); WM_operatortype_append(OBJECT_OT_vertex_group_smooth); diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index 995577d2eb3..02258049c43 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -1219,363 +1219,6 @@ static bool vgroup_normalize(Object *ob) return false; } -/* This finds all of the vertices face-connected to vert by an edge and returns a - * MEM_allocated array of indices of size count. - * count is an int passed by reference so it can be assigned the value of the length here. */ -static blender::Vector getSurroundingVerts(Mesh *me, int vert) -{ - const MPoly *poly = me->polys().data(); - const MLoop *loops = me->loops().data(); - int i = me->totpoly; - - blender::Vector verts; - - while (i--) { - int j = poly->totloop; - int first_l = poly->totloop - 1; - const MLoop *ml = &loops[poly->loopstart]; - while (j--) { - /* XXX This assume a vert can only be once in a poly, even though - * it seems logical to me, not totally sure of that. */ - if (ml->v == vert) { - int a, b, k; - if (j == first_l) { - /* We are on the first corner. */ - a = ml[1].v; - b = ml[j].v; - } - else if (!j) { - /* We are on the last corner. */ - a = (ml - 1)->v; - b = loops[poly->loopstart].v; - } - else { - a = (ml - 1)->v; - b = (ml + 1)->v; - } - - /* Append a and b verts to array, if not yet present. */ - k = verts.size(); - /* XXX Maybe a == b is enough? */ - while (k-- && !(a == b && a == -1)) { - if (verts[k] == a) { - a = -1; - } - else if (verts[k] == b) { - b = -1; - } - } - if (a != -1) { - verts.append(a); - } - if (b != -1) { - verts.append(b); - } - - /* Vert found in this poly, we can go to next one! */ - break; - } - ml++; - } - poly++; - } - - return verts; -} - -/* Get a single point in space by averaging a point cloud (vectors of size 3) - * coord is the place the average is stored, - * points is the point cloud, count is the number of points in the cloud. - */ -static void getSingleCoordinate(float3 *points, int count, float coord[3]) -{ - int i; - zero_v3(coord); - for (i = 0; i < count; i++) { - add_v3_v3(coord, points[i]); - } - mul_v3_fl(coord, 1.0f / count); -} - -/* given a plane and a start and end position, - * compute the amount of vertical distance relative to the plane and store it in dists, - * then get the horizontal and vertical change and store them in changes - */ -static void getVerticalAndHorizontalChange(const float norm[3], - float d, - const float coord[3], - const float start[3], - float distToStart, - float *end, - float (*changes)[2], - float *dists, - int index) -{ - /* A = Q - ((Q - P).N)N - * D = (a * x0 + b * y0 +c * z0 + d) */ - float projA[3], projB[3]; - float plane[4]; - - plane_from_point_normal_v3(plane, coord, norm); - - closest_to_plane_normalized_v3(projA, plane, start); - closest_to_plane_normalized_v3(projB, plane, end); - /* (vertical and horizontal refer to the plane's y and xz respectively) - * vertical distance */ - dists[index] = dot_v3v3(norm, end) + d; - /* vertical change */ - changes[index][0] = dists[index] - distToStart; - // printf("vc %f %f\n", distance(end, projB, 3) - distance(start, projA, 3), changes[index][0]); - /* horizontal change */ - changes[index][1] = len_v3v3(projA, projB); -} - -/** - * By changing nonzero weights, try to move a vertex in `me->mverts` with index 'index' to - * `distToBe` distance away from the provided plane strength can change `distToBe` so that it moves - * towards `distToBe` by that percentage `cp` changes how much the weights are adjusted - * to check the distance - * - * `index` is the index of the vertex being moved. - * `norm` and `d` are the plane's properties for the equation: `ax + by + cz + d = 0`. - * `coord` is a point on the plane. - */ -static void moveCloserToDistanceFromPlane(Depsgraph *depsgraph, - Scene * /*scene*/, - Object *ob, - Mesh *me, - int index, - const float norm[3], - const float coord[3], - float d, - float distToBe, - float strength, - float cp) -{ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *mesh_eval = (Mesh *)object_eval->data; - - Mesh *me_deform; - MDeformWeight *dw, *dw_eval; - float3 m; - MDeformVert *dvert = me->deform_verts_for_write().data() + index; - MDeformVert *dvert_eval = mesh_eval->deform_verts_for_write().data() + index; - int totweight = dvert->totweight; - float oldw = 0; - float oldPos[3] = {0}; - float vc, hc, dist = 0.0f; - int i, k; - float(*changes)[2] = static_cast( - MEM_mallocN(sizeof(float[2]) * totweight, "vertHorzChange")); - float *dists = static_cast(MEM_mallocN(sizeof(float) * totweight, "distance")); - - /* track if up or down moved it closer for each bone */ - bool *upDown = static_cast(MEM_callocN(sizeof(bool) * totweight, "upDownTracker")); - - int *dwIndices = static_cast(MEM_callocN(sizeof(int) * totweight, "dwIndexTracker")); - float distToStart; - int bestIndex = 0; - bool wasChange; - bool wasUp; - int lastIndex = -1; - float originalDistToBe = distToBe; - do { - wasChange = false; - me_deform = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); - const Span positions = me_deform->vert_positions(); - m = positions[index]; - copy_v3_v3(oldPos, m); - distToStart = dot_v3v3(norm, oldPos) + d; - - if (distToBe == originalDistToBe) { - distToBe += distToStart - distToStart * strength; - } - for (i = 0; i < totweight; i++) { - dwIndices[i] = i; - dw = (dvert->dw + i); - dw_eval = (dvert_eval->dw + i); - vc = hc = 0; - if (!dw->weight) { - changes[i][0] = 0; - changes[i][1] = 0; - dists[i] = distToStart; - continue; - } - for (k = 0; k < 2; k++) { - if (me_deform) { - /* DO NOT try to do own cleanup here, this is call for dramatic failures and bugs! - * Better to over-free and recompute a bit. */ - BKE_object_free_derived_caches(object_eval); - } - oldw = dw->weight; - if (k) { - dw->weight *= 1 + cp; - } - else { - dw->weight /= 1 + cp; - } - if (dw->weight == oldw) { - changes[i][0] = 0; - changes[i][1] = 0; - dists[i] = distToStart; - break; - } - if (dw->weight > 1) { - dw->weight = 1; - } - dw_eval->weight = dw->weight; - me_deform = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); - m = positions[index]; - getVerticalAndHorizontalChange(norm, d, coord, oldPos, distToStart, m, changes, dists, i); - dw->weight = oldw; - dw_eval->weight = oldw; - if (!k) { - vc = changes[i][0]; - hc = changes[i][1]; - dist = dists[i]; - } - else { - if (fabsf(dist - distToBe) < fabsf(dists[i] - distToBe)) { - upDown[i] = false; - changes[i][0] = vc; - changes[i][1] = hc; - dists[i] = dist; - } - else { - upDown[i] = true; - } - if (fabsf(dists[i] - distToBe) > fabsf(distToStart - distToBe)) { - changes[i][0] = 0; - changes[i][1] = 0; - dists[i] = distToStart; - } - } - } - } - /* sort the changes by the vertical change */ - for (k = 0; k < totweight; k++) { - bestIndex = k; - for (i = k + 1; i < totweight; i++) { - dist = dists[i]; - - if (fabsf(dist) > fabsf(dists[i])) { - bestIndex = i; - } - } - /* switch with k */ - if (bestIndex != k) { - std::swap(upDown[k], upDown[bestIndex]); - std::swap(dwIndices[k], dwIndices[bestIndex]); - swap_v2_v2(changes[k], changes[bestIndex]); - std::swap(dists[k], dists[bestIndex]); - } - } - bestIndex = -1; - /* find the best change with an acceptable horizontal change */ - for (i = 0; i < totweight; i++) { - if (fabsf(changes[i][0]) > fabsf(changes[i][1] * 2.0f)) { - bestIndex = i; - break; - } - } - if (bestIndex != -1) { - wasChange = true; - /* it is a good place to stop if it tries to move the opposite direction - * (relative to the plane) of last time */ - if (lastIndex != -1) { - if (wasUp != upDown[bestIndex]) { - wasChange = false; - } - } - lastIndex = bestIndex; - wasUp = upDown[bestIndex]; - dw = (dvert->dw + dwIndices[bestIndex]); - oldw = dw->weight; - if (upDown[bestIndex]) { - dw->weight *= 1 + cp; - } - else { - dw->weight /= 1 + cp; - } - if (dw->weight > 1) { - dw->weight = 1; - } - if (oldw == dw->weight) { - wasChange = false; - } - if (me_deform) { - /* DO NOT try to do own cleanup here, this is call for dramatic failures and bugs! - * Better to over-free and recompute a bit. */ - BKE_object_free_derived_caches(object_eval); - } - } - } while (wasChange && ((distToStart - distToBe) / fabsf(distToStart - distToBe) == - (dists[bestIndex] - distToBe) / fabsf(dists[bestIndex] - distToBe))); - - MEM_freeN(upDown); - MEM_freeN(changes); - MEM_freeN(dists); - MEM_freeN(dwIndices); -} - -/* this is used to try to smooth a surface by only adjusting the nonzero weights of a vertex - * but it could be used to raise or lower an existing 'bump.' */ -static void vgroup_fix( - const bContext *C, Scene * /*scene*/, Object *ob, float distToBe, float strength, float cp) -{ - using namespace blender; - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - int i; - - Mesh *me = static_cast(ob->data); - if (!(me->editflag & ME_EDIT_PAINT_VERT_SEL)) { - return; - } - const bke::AttributeAccessor attributes = me->attributes(); - const VArray select_vert = attributes.lookup_or_default( - ".select_vert", ATTR_DOMAIN_POINT, false); - for (i = 0; i < me->totvert; i++) { - if (select_vert[i]) { - blender::Vector verts = getSurroundingVerts(me, i); - const int count = verts.size(); - if (!verts.is_empty()) { - float3 m; - float3 *p = static_cast(MEM_callocN(sizeof(float3) * (count), "deformedPoints")); - int k; - - Mesh *me_deform = mesh_get_eval_deform( - depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); - const Span positions_deform = me_deform->vert_positions(); - k = count; - while (k--) { - p[k] = positions_deform[verts[k]]; - } - - if (count >= 3) { - float d /*, dist */ /* UNUSED */, mag; - float coord[3]; - float norm[3]; - getSingleCoordinate(p, count, coord); - m = positions_deform[i]; - sub_v3_v3v3(norm, m, coord); - mag = normalize_v3(norm); - if (mag) { /* zeros fix */ - d = -dot_v3v3(norm, coord); - // dist = (dot_v3v3(norm, m.co) + d); /* UNUSED */ - moveCloserToDistanceFromPlane( - depsgraph, scene_eval, object_eval, me, i, norm, coord, d, distToBe, strength, cp); - } - } - - MEM_freeN(p); - } - } - } -} - static void vgroup_levels_subset(Object *ob, const bool *vgroup_validmap, const int vgroup_tot, @@ -2746,7 +2389,7 @@ static bool vertex_group_mesh_poll_ex(bContext *C, Object *ob) return true; } -static bool vertex_group_mesh_with_dvert_poll(bContext *C) +static bool UNUSED_FUNCTION(vertex_group_mesh_with_dvert_poll(bContext *C)) { Object *ob = ED_object_context(C); if (!vertex_group_mesh_poll_ex(C, ob)) { @@ -3338,89 +2981,6 @@ void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Vertex Group Fix Position Operator - * \{ */ - -static int vertex_group_fix_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - Scene *scene = CTX_data_scene(C); - - float distToBe = RNA_float_get(op->ptr, "dist"); - float strength = RNA_float_get(op->ptr, "strength"); - float cp = RNA_float_get(op->ptr, "accuracy"); - ModifierData *md = static_cast(ob->modifiers.first); - - while (md) { - if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { - break; - } - md = md->next; - } - - if (md && md->type == eModifierType_Mirror) { - BKE_report(op->reports, - RPT_ERROR_INVALID_CONTEXT, - "This operator does not support an active mirror modifier"); - return OPERATOR_CANCELLED; - } - vgroup_fix(C, scene, ob, distToBe, strength, cp); - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -void OBJECT_OT_vertex_group_fix(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Fix Vertex Group Deform"; - ot->idname = "OBJECT_OT_vertex_group_fix"; - ot->description = - "Modify the position of selected vertices by changing only their respective " - "groups' weights (this tool may be slow for many vertices)"; - - /* api callbacks */ - ot->poll = vertex_group_mesh_with_dvert_poll; - ot->exec = vertex_group_fix_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, - "dist", - 0.0f, - -FLT_MAX, - FLT_MAX, - "Distance", - "The distance to move to", - -10.0f, - 10.0f); - RNA_def_float(ot->srna, - "strength", - 1.0f, - -2.0f, - FLT_MAX, - "Strength", - "The distance moved can be changed by this multiplier", - -2.0f, - 2.0f); - RNA_def_float( - ot->srna, - "accuracy", - 1.0f, - 0.05f, - FLT_MAX, - "Change Sensitivity", - "Change the amount weights are altered with each iteration: lower values are slower", - 0.05f, - 1.0f); -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Vertex Group Lock Operator * \{ */ -- 2.30.2 From d8293806b6aace5ba5f2c7b8b2d310755a74f389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 2 Mar 2023 16:54:47 +0100 Subject: [PATCH 2/2] Remove `vertex_group_mesh_with_dvert_poll` and `vertex_group_mesh_poll_ex` --- .../blender/editors/object/object_vgroup.cc | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index 02258049c43..d8e17ac86b7 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -2375,36 +2375,6 @@ static bool vertex_group_poll(bContext *C) return vertex_group_poll_ex(C, ob); } -static bool vertex_group_mesh_poll_ex(bContext *C, Object *ob) -{ - if (!vertex_group_poll_ex(C, ob)) { - return false; - } - - if (ob->type != OB_MESH) { - CTX_wm_operator_poll_msg_set(C, "Only mesh objects are supported"); - return false; - } - - return true; -} - -static bool UNUSED_FUNCTION(vertex_group_mesh_with_dvert_poll(bContext *C)) -{ - Object *ob = ED_object_context(C); - if (!vertex_group_mesh_poll_ex(C, ob)) { - return false; - } - - Mesh *me = static_cast(ob->data); - if (me->deform_verts().is_empty()) { - CTX_wm_operator_poll_msg_set(C, "The active mesh object has no vertex group data"); - return false; - } - - return true; -} - static bool UNUSED_FUNCTION(vertex_group_poll_edit)(bContext *C) { Object *ob = ED_object_context(C); -- 2.30.2