4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
28 * Creator-specific support for vertex deformation groups
29 * Added: apply deform function (ton)
34 #include "MEM_guardedalloc.h"
36 #include "DNA_cloth_types.h"
37 #include "DNA_curve_types.h"
38 #include "DNA_lattice_types.h"
39 #include "DNA_mesh_types.h"
40 #include "DNA_meshdata_types.h"
41 #include "DNA_modifier_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_object_force.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_particle_types.h"
46 #include "DNA_windowmanager_types.h"
48 #include "BLI_blenlib.h"
49 #include "BLI_editVert.h"
51 #include "BKE_customdata.h"
52 #include "BKE_DerivedMesh.h"
53 #include "BKE_depsgraph.h"
54 #include "BKE_deform.h"
55 #include "BKE_displist.h"
56 #include "BKE_global.h"
57 #include "BKE_lattice.h"
59 #include "BKE_utildefines.h"
62 #include "ED_view3d.h"
63 #include "mesh_intern.h"
66 static void BIF_undo_push() {}
67 static void error() {}
69 static Lattice *def_get_lattice(Object *ob)
71 if(ob->type==OB_LATTICE) {
72 Lattice *lt= ob->data;
80 /* only in editmode */
81 void sel_verts_defgroup (Object *obedit, int select)
98 for (eve=me->edit_mesh->verts.first; eve; eve=eve->next){
99 dvert= CustomData_em_get(&me->edit_mesh->vdata, eve->data, CD_MDEFORMVERT);
101 if (dvert && dvert->totweight){
102 for (i=0; i<dvert->totweight; i++){
103 if (dvert->dw[i].def_nr == (ob->actdef-1)){
104 if (select) eve->f |= SELECT;
105 else eve->f &= ~SELECT;
112 /* this has to be called, because this function operates on vertices only */
113 if(select) EM_select_flush(me->edit_mesh); // vertices to edges/faces
114 else EM_deselect_flush(me->edit_mesh);
119 Lattice *lt= def_get_lattice(ob);
127 tot= lt->pntsu*lt->pntsv*lt->pntsw;
128 for(a=0, bp= lt->def; a<tot; a++, bp++, dvert++) {
129 for (i=0; i<dvert->totweight; i++){
130 if (dvert->dw[i].def_nr == (ob->actdef-1)) {
131 if(select) bp->f1 |= SELECT;
132 else bp->f1 &= ~SELECT;
147 /* check if deform vertex has defgroup index */
148 MDeformWeight *get_defweight (MDeformVert *dv, int defgroup)
152 if (!dv || defgroup<0)
155 for (i=0; i<dv->totweight; i++){
156 if (dv->dw[i].def_nr == defgroup)
162 /* Ensures that mv has a deform weight entry for
163 the specified defweight group */
164 /* Note this function is mirrored in editmesh_tools.c, for use for editvertices */
165 MDeformWeight *verify_defweight (MDeformVert *dv, int defgroup)
167 MDeformWeight *newdw;
169 /* do this check always, this function is used to check for it */
170 if (!dv || defgroup<0)
173 newdw = get_defweight (dv, defgroup);
177 newdw = MEM_callocN (sizeof(MDeformWeight)*(dv->totweight+1), "deformWeight");
179 memcpy (newdw, dv->dw, sizeof(MDeformWeight)*dv->totweight);
184 dv->dw[dv->totweight].weight=0.0f;
185 dv->dw[dv->totweight].def_nr=defgroup;
190 return dv->dw+(dv->totweight-1);
193 bDeformGroup *add_defgroup_name (Object *ob, char *name)
195 bDeformGroup *defgroup;
200 defgroup = MEM_callocN (sizeof(bDeformGroup), "add deformGroup");
202 BLI_strncpy (defgroup->name, name, 32);
204 BLI_addtail(&ob->defbase, defgroup);
205 unique_vertexgroup_name(defgroup, ob);
207 ob->actdef = BLI_countlist(&ob->defbase);
212 void add_defgroup (Object *ob)
214 add_defgroup_name (ob, "Group");
218 void duplicate_defgroup ( Object *ob )
220 bDeformGroup *dg, *cdg;
221 char name[32], s[32];
222 MDeformWeight *org, *cpy;
227 if (ob->type != OB_MESH)
230 dg = BLI_findlink (&ob->defbase, (ob->actdef-1));
234 if (strstr(dg->name, "_copy")) {
235 BLI_strncpy (name, dg->name, 32); /* will be renamed _copy.001... etc */
237 BLI_snprintf (name, 32, "%s_copy", dg->name);
238 while (get_named_vertexgroup (ob, name)) {
239 if ((strlen (name) + 6) > 32) {
240 error ("Error: the name for the new group is > 32 characters");
244 BLI_snprintf (name, 32, "%s_copy", s);
248 cdg = copy_defgroup (dg);
249 strcpy (cdg->name, name);
250 unique_vertexgroup_name(cdg, ob);
252 BLI_addtail (&ob->defbase, cdg);
254 idg = (ob->actdef-1);
255 ob->actdef = BLI_countlist (&ob->defbase);
256 icdg = (ob->actdef-1);
262 for (i = 0; i < me->totvert; i++) {
264 org = get_defweight (dvert, idg);
266 cpy = verify_defweight (dvert, icdg);
267 cpy->weight = org->weight;
272 static void del_defgroup_update_users(Object *ob, int id)
274 ExplodeModifierData *emd;
276 ParticleSystem *psys;
277 ClothModifierData *clmd;
278 ClothSimSettings *clsim;
281 /* these cases don't use names to refer to vertex groups, so when
282 * they get deleted the numbers get out of sync, this corrects that */
285 if(ob->soft->vertgroup == id)
286 ob->soft->vertgroup= 0;
287 else if(ob->soft->vertgroup > id)
288 ob->soft->vertgroup--;
291 for(md=ob->modifiers.first; md; md=md->next) {
292 if(md->type == eModifierType_Explode) {
293 emd= (ExplodeModifierData*)md;
295 if(emd->vgroup == id)
297 else if(emd->vgroup > id)
300 else if(md->type == eModifierType_Cloth) {
301 clmd= (ClothModifierData*)md;
302 clsim= clmd->sim_parms;
305 if(clsim->vgroup_mass == id)
306 clsim->vgroup_mass= 0;
307 else if(clsim->vgroup_mass > id)
308 clsim->vgroup_mass--;
310 if(clsim->vgroup_bend == id)
311 clsim->vgroup_bend= 0;
312 else if(clsim->vgroup_bend > id)
313 clsim->vgroup_bend--;
315 if(clsim->vgroup_struct == id)
316 clsim->vgroup_struct= 0;
317 else if(clsim->vgroup_struct > id)
318 clsim->vgroup_struct--;
323 for(psys=ob->particlesystem.first; psys; psys=psys->next) {
324 for(a=0; a<PSYS_TOT_VG; a++)
325 if(psys->vgroup[a] == id)
327 else if(psys->vgroup[a] > id)
332 void del_defgroup_in_object_mode ( Object *ob )
339 if ((!ob) || (ob->type != OB_MESH))
342 dg = BLI_findlink (&ob->defbase, (ob->actdef-1));
348 for (i = 0; i < me->totvert; i++) {
349 dvert = me->dvert + i;
351 if (get_defweight (dvert, (ob->actdef-1)))
352 remove_vert_defgroup (ob, dg, i);
356 for (i = 0; i < me->totvert; i++) {
359 for (e = 0; e < dvert->totweight; e++) {
360 if (dvert->dw[e].def_nr > (ob->actdef-1))
361 dvert->dw[e].def_nr--;
367 del_defgroup_update_users(ob, ob->actdef);
369 /* Update the active deform index if necessary */
370 if (ob->actdef == BLI_countlist(&ob->defbase))
373 /* Remove the group */
374 BLI_freelinkN (&ob->defbase, dg);
377 void del_defgroup (Object *ob)
379 bDeformGroup *defgroup;
388 defgroup = BLI_findlink(&ob->defbase, ob->actdef-1);
392 /* Make sure that no verts are using this group */
393 remove_verts_defgroup(ob, 1);
395 /* Make sure that any verts with higher indices are adjusted accordingly */
396 if(ob->type==OB_MESH) {
398 EditMesh *em = me->edit_mesh;
402 for (eve=em->verts.first; eve; eve=eve->next){
403 dvert= CustomData_em_get(&me->edit_mesh->vdata, eve->data, CD_MDEFORMVERT);
406 for (i=0; i<dvert->totweight; i++)
407 if (dvert->dw[i].def_nr > (ob->actdef-1))
408 dvert->dw[i].def_nr--;
411 else if(ob->type==OB_LATTICE) {
412 Lattice *lt= def_get_lattice(ob);
414 MDeformVert *dvert= lt->dvert;
418 tot= lt->pntsu*lt->pntsv*lt->pntsw;
419 for(a=0, bp= lt->def; a<tot; a++, bp++, dvert++) {
420 for (i=0; i<dvert->totweight; i++){
421 if (dvert->dw[i].def_nr > (ob->actdef-1))
422 dvert->dw[i].def_nr--;
428 del_defgroup_update_users(ob, ob->actdef);
430 /* Update the active deform index if necessary */
431 if (ob->actdef==BLI_countlist(&ob->defbase))
434 /* Remove the group */
435 BLI_freelinkN (&ob->defbase, defgroup);
437 /* remove all dverts */
439 if(ob->type==OB_MESH) {
441 CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert);
444 else if(ob->type==OB_LATTICE) {
445 Lattice *lt= def_get_lattice(ob);
447 MEM_freeN(lt->dvert);
454 void del_all_defgroups (Object *ob)
460 /* Remove all DVerts */
461 if (ob->type==OB_MESH) {
463 CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert);
466 else if(ob->type==OB_LATTICE) {
467 Lattice *lt= def_get_lattice(ob);
469 MEM_freeN(lt->dvert);
474 /* Remove all DefGroups */
475 BLI_freelistN(&ob->defbase);
477 /* Fix counters/indices */
481 void create_dverts(ID *id)
483 /* create deform verts
486 if( GS(id->name)==ID_ME) {
487 Mesh *me= (Mesh *)id;
488 me->dvert= CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, me->totvert);
490 else if( GS(id->name)==ID_LT) {
491 Lattice *lt= (Lattice *)id;
492 lt->dvert= MEM_callocN(sizeof(MDeformVert)*lt->pntsu*lt->pntsv*lt->pntsw, "lattice deformVert");
496 /* for mesh in object mode
497 lattice can be in editmode */
498 void remove_vert_def_nr (Object *ob, int def_nr, int vertnum)
500 /* This routine removes the vertex from the deform
501 * group with number def_nr.
503 * This routine is meant to be fast, so it is the
504 * responsibility of the calling routine to:
505 * a) test whether ob is non-NULL
506 * b) test whether ob is a mesh
507 * c) calculate def_nr
510 MDeformWeight *newdw;
511 MDeformVert *dvert= NULL;
514 /* get the deform vertices corresponding to the
517 if(ob->type==OB_MESH) {
518 if( ((Mesh*)ob->data)->dvert )
519 dvert = ((Mesh*)ob->data)->dvert + vertnum;
521 else if(ob->type==OB_LATTICE) {
522 Lattice *lt= def_get_lattice(ob);
525 dvert = lt->dvert + vertnum;
531 /* for all of the deform weights in the
534 for (i=dvert->totweight - 1 ; i>=0 ; i--){
536 /* if the def_nr is the same as the one
537 * for our weight group then remove it
538 * from this deform vert.
540 if (dvert->dw[i].def_nr == def_nr) {
543 /* if there are still other deform weights
544 * attached to this vert then remove this
545 * deform weight, and reshuffle the others
547 if (dvert->totweight) {
548 newdw = MEM_mallocN (sizeof(MDeformWeight)*(dvert->totweight),
551 memcpy (newdw, dvert->dw, sizeof(MDeformWeight)*i);
552 memcpy (newdw+i, dvert->dw+i+1,
553 sizeof(MDeformWeight)*(dvert->totweight-i));
554 MEM_freeN (dvert->dw);
558 /* if there are no other deform weights
559 * left then just remove the deform weight
562 MEM_freeN (dvert->dw);
571 /* for Mesh in Object mode */
572 /* allows editmode for Lattice */
573 void add_vert_defnr (Object *ob, int def_nr, int vertnum,
574 float weight, int assignmode)
576 /* add the vert to the deform group with the
579 MDeformVert *dv= NULL;
580 MDeformWeight *newdw;
584 if(ob->type==OB_MESH) {
585 if(((Mesh*)ob->data)->dvert)
586 dv = ((Mesh*)ob->data)->dvert + vertnum;
588 else if(ob->type==OB_LATTICE) {
589 Lattice *lt= def_get_lattice(ob);
592 dv = lt->dvert + vertnum;
598 /* Lets first check to see if this vert is
599 * already in the weight group -- if so
602 for (i=0; i<dv->totweight; i++){
604 /* if this weight cooresponds to the
605 * deform group, then add it using
606 * the assign mode provided
608 if (dv->dw[i].def_nr == def_nr){
610 switch (assignmode) {
612 dv->dw[i].weight=weight;
615 dv->dw[i].weight+=weight;
616 if (dv->dw[i].weight >= 1.0)
617 dv->dw[i].weight = 1.0;
619 case WEIGHT_SUBTRACT:
620 dv->dw[i].weight-=weight;
621 /* if the weight is zero or less then
622 * remove the vert from the deform group
624 if (dv->dw[i].weight <= 0.0)
625 remove_vert_def_nr(ob, def_nr, vertnum);
632 /* if the vert wasn't in the deform group then
633 * we must take a different form of action ...
636 switch (assignmode) {
637 case WEIGHT_SUBTRACT:
638 /* if we are subtracting then we don't
639 * need to do anything
645 /* if we are doing an additive assignment, then
646 * we need to create the deform weight
648 newdw = MEM_callocN (sizeof(MDeformWeight)*(dv->totweight+1),
651 memcpy (newdw, dv->dw, sizeof(MDeformWeight)*dv->totweight);
656 dv->dw[dv->totweight].weight=weight;
657 dv->dw[dv->totweight].def_nr=def_nr;
664 /* called while not in editmode */
665 void add_vert_to_defgroup (Object *ob, bDeformGroup *dg, int vertnum,
666 float weight, int assignmode)
668 /* add the vert to the deform group with the
669 * specified assign mode
673 /* get the deform group number, exit if
676 def_nr = get_defgroup_num(ob, dg);
677 if (def_nr < 0) return;
679 /* if there's no deform verts then
682 if(ob->type==OB_MESH) {
683 if (!((Mesh*)ob->data)->dvert)
684 create_dverts(ob->data);
686 else if(ob->type==OB_LATTICE) {
687 if (!((Lattice*)ob->data)->dvert)
688 create_dverts(ob->data);
691 /* call another function to do the work
693 add_vert_defnr (ob, def_nr, vertnum, weight, assignmode);
696 /* Only available in editmode */
697 void assign_verts_defgroup (Object *obedit, float weight)
701 bDeformGroup *dg, *eg;
702 MDeformWeight *newdw;
706 // XXX if(multires_level1_test()) return;
713 dg=BLI_findlink(&ob->defbase, ob->actdef-1);
715 error ("No vertex group is active");
724 if (!CustomData_has_layer(&me->edit_mesh->vdata, CD_MDEFORMVERT))
725 EM_add_data_layer(me->edit_mesh, &me->edit_mesh->vdata, CD_MDEFORMVERT);
727 /* Go through the list of editverts and assign them */
728 for (eve=me->edit_mesh->verts.first; eve; eve=eve->next){
729 dvert= CustomData_em_get(&me->edit_mesh->vdata, eve->data, CD_MDEFORMVERT);
731 if (dvert && (eve->f & 1)){
733 /* See if this vert already has a reference to this group */
734 /* If so: Change its weight */
736 for (i=0; i<dvert->totweight; i++){
737 eg = BLI_findlink (&ob->defbase, dvert->dw[i].def_nr);
738 /* Find the actual group */
740 dvert->dw[i].weight= weight;
745 /* If not: Add the group and set its weight */
747 newdw = MEM_callocN (sizeof(MDeformWeight)*(dvert->totweight+1), "deformWeight");
749 memcpy (newdw, dvert->dw, sizeof(MDeformWeight)*dvert->totweight);
750 MEM_freeN (dvert->dw);
754 dvert->dw[dvert->totweight].weight= weight;
755 dvert->dw[dvert->totweight].def_nr= ob->actdef-1;
766 Lattice *lt= def_get_lattice(ob);
771 create_dverts(<->id);
773 tot= lt->pntsu*lt->pntsv*lt->pntsw;
774 for(a=0, bp= lt->def; a<tot; a++, bp++) {
776 add_vert_defnr (ob, ob->actdef-1, a, weight, WEIGHT_REPLACE);
781 printf ("Assigning deformation groups to unknown object type\n");
787 /* mesh object mode, lattice can be in editmode */
788 void remove_vert_defgroup (Object *ob, bDeformGroup *dg, int vertnum)
790 /* This routine removes the vertex from the specified
796 /* if the object is NULL abort
801 /* get the deform number that cooresponds
802 * to this deform group, and abort if it
805 def_nr = get_defgroup_num(ob, dg);
806 if (def_nr < 0) return;
808 /* call another routine to do the work
810 remove_vert_def_nr (ob, def_nr, vertnum);
813 /* for mesh in object mode lattice can be in editmode */
814 static float get_vert_def_nr (Object *ob, int def_nr, int vertnum)
816 MDeformVert *dvert= NULL;
819 /* get the deform vertices corresponding to the
822 if(ob->type==OB_MESH) {
823 if( ((Mesh*)ob->data)->dvert )
824 dvert = ((Mesh*)ob->data)->dvert + vertnum;
826 else if(ob->type==OB_LATTICE) {
827 Lattice *lt= def_get_lattice(ob);
830 dvert = lt->dvert + vertnum;
836 for(i=dvert->totweight-1 ; i>=0 ; i--)
837 if(dvert->dw[i].def_nr == def_nr)
838 return dvert->dw[i].weight;
843 /* mesh object mode, lattice can be in editmode */
844 float get_vert_defgroup (Object *ob, bDeformGroup *dg, int vertnum)
851 def_nr = get_defgroup_num(ob, dg);
852 if(def_nr < 0) return 0.0f;
854 return get_vert_def_nr (ob, def_nr, vertnum);
857 /* Only available in editmode */
858 /* removes from active defgroup, if allverts==0 only selected vertices */
859 void remove_verts_defgroup (Object *obedit, int allverts)
864 MDeformWeight *newdw;
865 bDeformGroup *dg, *eg;
868 // XXX if(multires_level1_test()) return;
875 dg=BLI_findlink(&ob->defbase, ob->actdef-1);
877 error ("No vertex group is active");
886 for (eve=me->edit_mesh->verts.first; eve; eve=eve->next){
887 dvert= CustomData_em_get(&me->edit_mesh->vdata, eve->data, CD_MDEFORMVERT);
889 if (dvert && dvert->dw && ((eve->f & 1) || allverts)){
890 for (i=0; i<dvert->totweight; i++){
892 eg = BLI_findlink (&ob->defbase, dvert->dw[i].def_nr);
895 if (dvert->totweight){
896 newdw = MEM_mallocN (sizeof(MDeformWeight)*(dvert->totweight), "deformWeight");
899 memcpy (newdw, dvert->dw, sizeof(MDeformWeight)*i);
900 memcpy (newdw+i, dvert->dw+i+1, sizeof(MDeformWeight)*(dvert->totweight-i));
901 MEM_freeN (dvert->dw);
906 MEM_freeN (dvert->dw);
918 Lattice *lt= def_get_lattice(ob);
922 int a, tot= lt->pntsu*lt->pntsv*lt->pntsw;
924 for(a=0, bp= lt->def; a<tot; a++, bp++) {
925 if(allverts || (bp->f1 & SELECT))
926 remove_vert_defgroup (ob, dg, a);
933 printf ("Removing deformation groups from unknown object type\n");
938 /* Only available in editmode */
939 /* removes from all defgroup, if allverts==0 only selected vertices */
940 void remove_verts_defgroups(Object *obedit, int allverts)
943 int actdef, defCount;
945 // XXX if (multires_level1_test()) return;
948 if (ob == NULL) return;
951 defCount= BLI_countlist(&ob->defbase);
954 error("Object has no vertex groups");
958 /* To prevent code redundancy, we just use remove_verts_defgroup, but that
959 * only operates on the active vgroup. So we iterate through all groups, by changing
962 for (ob->actdef= 1; ob->actdef <= defCount; ob->actdef++)
963 remove_verts_defgroup(ob, allverts);
968 void vertexgroup_select_by_name(Object *ob, char *name)
970 bDeformGroup *curdef;
975 for (curdef = ob->defbase.first; curdef; curdef=curdef->next, actdef++){
976 if (!strcmp(curdef->name, name)) {
981 ob->actdef=0; // this signals on painting to create a new one, if a bone in posemode is selected */
984 /* This function provides a shortcut for adding/removing verts from
985 * vertex groups. It is called by the Ctrl-G hotkey in EditMode for Meshes
986 * and Lattices. (currently only restricted to those two)
987 * It is only responsible for
989 void vgroup_assign_with_menu(Scene *scene, Object *ob)
991 VPaint *wp= scene->toolsettings->wpaint;
995 /* prevent crashes */
996 if (wp==NULL || ob==NULL) return;
998 defCount= BLI_countlist(&ob->defbase);
1000 /* give user choices of adding to current/new or removing from current */
1001 // XXX if (defCount && ob->actdef)
1002 // mode = pupmenu("Vertex Groups %t|Add Selected to New Group %x1|Add Selected to Active Group %x2|Remove Selected from Active Group %x3|Remove Selected from All Groups %x4");
1004 // mode= pupmenu("Vertex Groups %t|Add Selected to New Group %x1");
1006 /* handle choices */
1008 case 1: /* add to new group */
1010 assign_verts_defgroup(ob, wp->weight);
1011 BIF_undo_push("Assign to vertex group");
1013 case 2: /* add to current group */
1014 assign_verts_defgroup(ob, wp->weight);
1015 BIF_undo_push("Assign to vertex group");
1017 case 3: /* remove from current group */
1018 remove_verts_defgroup(ob, 0);
1019 BIF_undo_push("Remove from vertex group");
1021 case 4: /* remove from all groups */
1022 remove_verts_defgroups(ob, 0);
1023 BIF_undo_push("Remove from all vertex groups");
1028 /* This function provides a shortcut for commonly used vertex group
1029 * functions - change weight (not implemented), change active group, delete active group,
1030 * when Ctrl-Shift-G is used in EditMode, for Meshes and Lattices (only for now).
1032 void vgroup_operation_with_menu(Object *ob)
1037 /* prevent crashes and useless cases */
1038 if (ob==NULL) return;
1040 defCount= BLI_countlist(&ob->defbase);
1041 if (defCount == 0) return;
1043 /* give user choices of adding to current/new or removing from current */
1044 // XXX if (ob->actdef)
1045 // mode = pupmenu("Vertex Groups %t|Change Active Group%x1|Delete Active Group%x2|Delete All Groups%x3");
1047 // mode= pupmenu("Vertex Groups %t|Change Active Group%x1|Delete All Groups%x3");
1049 /* handle choices */
1051 case 1: /* change active group*/
1053 char *menustr= NULL; // XXX get_vertexgroup_menustr(ob);
1057 // XXX nr= pupmenu(menustr);
1059 if ((nr >= 1) && (nr <= defCount))
1066 case 2: /* delete active group */
1069 BIF_undo_push("Delete vertex group");
1072 case 3: /* delete all groups */
1074 del_all_defgroups(ob);
1075 BIF_undo_push("Delete all vertex groups");