/* * 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. */ /** \file * \ingroup bke */ #include #include #include "MEM_guardedalloc.h" #include "BLT_translation.h" #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "DNA_armature_types.h" #include "DNA_cloth_types.h" #include "DNA_curve_types.h" #include "DNA_gpencil_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "BKE_action.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_gpencil.h" #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_object_deform.h" /* own include */ /* -------------------------------------------------------------------- */ /** \name Misc helpers * \{ */ static Lattice *object_defgroup_lattice_get(ID *id) { Lattice *lt = (Lattice *)id; BLI_assert(GS(id->name) == ID_LT); return (lt->editlatt) ? lt->editlatt->latt : lt; } /** * Update users of vgroups from this object, according to given map. * * Use it when you remove or reorder vgroups in the object. * * \param map: an array mapping old indices to new indices. */ void BKE_object_defgroup_remap_update_users(Object *ob, const int *map) { ModifierData *md; ParticleSystem *psys; int a; /* these cases don't use names to refer to vertex groups, so when * they get removed the numbers get out of sync, this corrects that */ if (ob->soft) { ob->soft->vertgroup = map[ob->soft->vertgroup]; } for (md = ob->modifiers.first; md; md = md->next) { if (md->type == eModifierType_Explode) { ExplodeModifierData *emd = (ExplodeModifierData *)md; emd->vgroup = map[emd->vgroup]; } else if (md->type == eModifierType_Cloth) { ClothModifierData *clmd = (ClothModifierData *)md; ClothSimSettings *clsim = clmd->sim_parms; if (clsim) { clsim->vgroup_mass = map[clsim->vgroup_mass]; clsim->vgroup_bend = map[clsim->vgroup_bend]; clsim->vgroup_struct = map[clsim->vgroup_struct]; } } } for (psys = ob->particlesystem.first; psys; psys = psys->next) { for (a = 0; a < PSYS_TOT_VG; a++) { psys->vgroup[a] = map[psys->vgroup[a]]; } } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Group creation * \{ */ /** * Add a vgroup of given name to object. *Does not* handle MDeformVert data at all! */ bDeformGroup *BKE_object_defgroup_add_name(Object *ob, const char *name) { bDeformGroup *defgroup; if (!ob || !OB_TYPE_SUPPORT_VGROUP(ob->type)) { return NULL; } defgroup = BKE_object_defgroup_new(ob, name); BKE_object_defgroup_active_index_set(ob, BKE_object_defgroup_count(ob)); return defgroup; } /** * Add a vgroup of default name to object. *Does not* handle MDeformVert data at all! */ bDeformGroup *BKE_object_defgroup_add(Object *ob) { return BKE_object_defgroup_add_name(ob, DATA_("Group")); } /** * Create MDeformVert data for given ID. Work in Object mode only. */ MDeformVert *BKE_object_defgroup_data_create(ID *id) { if (GS(id->name) == ID_ME) { Mesh *me = (Mesh *)id; me->dvert = CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, me->totvert); return me->dvert; } if (GS(id->name) == ID_LT) { Lattice *lt = (Lattice *)id; lt->dvert = MEM_callocN(sizeof(MDeformVert) * lt->pntsu * lt->pntsv * lt->pntsw, "lattice deformVert"); return lt->dvert; } return NULL; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Group clearing * \{ */ /** * Remove all verts (or only selected ones) from given vgroup. Work in Object and Edit modes. * * \param use_selection: Only operate on selection. * \return True if any vertex was removed, false otherwise. */ bool BKE_object_defgroup_clear(Object *ob, bDeformGroup *dg, const bool use_selection) { MDeformVert *dv; const ListBase *defbase = BKE_object_defgroup_list(ob); const int def_nr = BLI_findindex(defbase, dg); bool changed = false; if (ob->type == OB_MESH) { Mesh *me = ob->data; if (me->edit_mesh) { BMEditMesh *em = me->edit_mesh; const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); if (cd_dvert_offset != -1) { BMVert *eve; BMIter iter; BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); if (dv && dv->dw && (!use_selection || BM_elem_flag_test(eve, BM_ELEM_SELECT))) { MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr); BKE_defvert_remove_group(dv, dw); /* dw can be NULL */ changed = true; } } } } else { if (me->dvert) { MVert *mv; int i; mv = me->mvert; dv = me->dvert; for (i = 0; i < me->totvert; i++, mv++, dv++) { if (dv->dw && (!use_selection || (mv->flag & SELECT))) { MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr); BKE_defvert_remove_group(dv, dw); /* dw can be NULL */ changed = true; } } } } } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); if (lt->dvert) { BPoint *bp; int i, tot = lt->pntsu * lt->pntsv * lt->pntsw; for (i = 0, bp = lt->def; i < tot; i++, bp++) { if (!use_selection || (bp->f1 & SELECT)) { MDeformWeight *dw; dv = <->dvert[i]; dw = BKE_defvert_find_index(dv, def_nr); BKE_defvert_remove_group(dv, dw); /* dw can be NULL */ changed = true; } } } } return changed; } /** * Remove all verts (or only selected ones) from all vgroups. Work in Object and Edit modes. * * \param use_selection: Only operate on selection. * \return True if any vertex was removed, false otherwise. */ bool BKE_object_defgroup_clear_all(Object *ob, const bool use_selection) { bDeformGroup *dg; bool changed = false; const ListBase *defbase = BKE_object_defgroup_list(ob); for (dg = defbase->first; dg; dg = dg->next) { if (BKE_object_defgroup_clear(ob, dg, use_selection)) { changed = true; } } return changed; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Group removal * \{ */ static void object_defgroup_remove_update_users(Object *ob, const int idx) { int i, defbase_tot = BKE_object_defgroup_count(ob) + 1; int *map = MEM_mallocN(sizeof(int) * defbase_tot, "vgroup del"); map[idx] = map[0] = 0; for (i = 1; i < idx; i++) { map[i] = i; } for (i = idx + 1; i < defbase_tot; i++) { map[i] = i - 1; } BKE_object_defgroup_remap_update_users(ob, map); MEM_freeN(map); } static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const int def_nr) { object_defgroup_remove_update_users(ob, def_nr + 1); /* Remove the group */ ListBase *defbase = BKE_object_defgroup_list_mutable(ob); BLI_freelinkN(defbase, dg); /* Update the active deform index if necessary */ const int active_index = BKE_object_defgroup_active_index_get(ob); if (active_index > def_nr) { BKE_object_defgroup_active_index_set(ob, active_index - 1); } /* remove all dverts */ if (BLI_listbase_is_empty(defbase)) { if (ob->type == OB_MESH) { Mesh *me = ob->data; CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert); me->dvert = NULL; } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); if (lt->dvert) { MEM_freeN(lt->dvert); lt->dvert = NULL; } } } else if (BKE_object_defgroup_active_index_get(ob) < 1) { /* Keep a valid active index if we still have some vgroups. */ BKE_object_defgroup_active_index_set(ob, 1); } } static void object_defgroup_remove_object_mode(Object *ob, bDeformGroup *dg) { MDeformVert *dvert_array = NULL; int dvert_tot = 0; const ListBase *defbase = BKE_object_defgroup_list(ob); const int def_nr = BLI_findindex(defbase, dg); BLI_assert(def_nr != -1); BKE_object_defgroup_array_get(ob->data, &dvert_array, &dvert_tot); if (dvert_array) { int i, j; MDeformVert *dv; for (i = 0, dv = dvert_array; i < dvert_tot; i++, dv++) { MDeformWeight *dw; dw = BKE_defvert_find_index(dv, def_nr); BKE_defvert_remove_group(dv, dw); /* dw can be NULL */ /* inline, make into a function if anything else needs to do this */ for (j = 0; j < dv->totweight; j++) { if (dv->dw[j].def_nr > def_nr) { dv->dw[j].def_nr--; } } /* done */ } } object_defgroup_remove_common(ob, dg, def_nr); } static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg) { int i; const ListBase *defbase = BKE_object_defgroup_list(ob); const int def_nr = BLI_findindex(defbase, dg); BLI_assert(def_nr != -1); /* Make sure that no verts are using this group - if none were removed, * we can skip next per-vert update. */ if (!BKE_object_defgroup_clear(ob, dg, false)) { /* Nothing to do. */ } /* Else, make sure that any groups with higher indices are adjusted accordingly */ else if (ob->type == OB_MESH) { Mesh *me = ob->data; BMEditMesh *em = me->edit_mesh; const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); BMIter iter; BMVert *eve; MDeformVert *dvert; BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); if (dvert) { for (i = 0; i < dvert->totweight; i++) { if (dvert->dw[i].def_nr > def_nr) { dvert->dw[i].def_nr--; } } } } } else if (ob->type == OB_LATTICE) { Lattice *lt = ((Lattice *)(ob->data))->editlatt->latt; BPoint *bp; MDeformVert *dvert = lt->dvert; int a, tot; if (dvert) { tot = lt->pntsu * lt->pntsv * lt->pntsw; for (a = 0, bp = lt->def; a < tot; a++, bp++, dvert++) { for (i = 0; i < dvert->totweight; i++) { if (dvert->dw[i].def_nr > def_nr) { dvert->dw[i].def_nr--; } } } } } object_defgroup_remove_common(ob, dg, def_nr); } /** * Remove given vgroup from object. Work in Object and Edit modes. */ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) { if (ob->type == OB_GPENCIL) { BKE_gpencil_vgroup_remove(ob, defgroup); } else { if (BKE_object_is_in_editmode_vgroup(ob)) { object_defgroup_remove_edit_mode(ob, defgroup); } else { object_defgroup_remove_object_mode(ob, defgroup); } BKE_object_batch_cache_dirty_tag(ob); } } /** * Remove all vgroups from object. Work in Object and Edit modes. * When only_unlocked=true, locked vertex groups are not removed. */ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) { ListBase *defbase = BKE_object_defgroup_list_mutable(ob); bDeformGroup *dg = (bDeformGroup *)defbase->first; const bool edit_mode = BKE_object_is_in_editmode_vgroup(ob); if (dg) { while (dg) { bDeformGroup *next_dg = dg->next; if (!only_unlocked || (dg->flag & DG_LOCK_WEIGHT) == 0) { if (edit_mode) { object_defgroup_remove_edit_mode(ob, dg); } else { object_defgroup_remove_object_mode(ob, dg); } } dg = next_dg; } } else { /* defbase is empty... */ /* remove all dverts */ if (ob->type == OB_MESH) { Mesh *me = ob->data; CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert); me->dvert = NULL; } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); if (lt->dvert) { MEM_freeN(lt->dvert); lt->dvert = NULL; } } /* Fix counters/indices */ BKE_object_defgroup_active_index_set(ob, 0); } } /** * Remove all vgroups from object. Work in Object and Edit modes. */ void BKE_object_defgroup_remove_all(struct Object *ob) { BKE_object_defgroup_remove_all_ex(ob, false); } /** * Compute mapping for vertex groups with matching name, -1 is used for no remapping. * Returns null if no remapping is required. * The returned array has to be freed. */ int *BKE_object_defgroup_index_map_create(Object *ob_src, Object *ob_dst, int *r_map_len) { const ListBase *src_defbase = BKE_object_defgroup_list(ob_src); const ListBase *dst_defbase = BKE_object_defgroup_list(ob_dst); /* Build src to merged mapping of vgroup indices. */ if (BLI_listbase_is_empty(src_defbase) || BLI_listbase_is_empty(dst_defbase)) { *r_map_len = 0; return NULL; } bDeformGroup *dg_src; *r_map_len = BLI_listbase_count(src_defbase); int *vgroup_index_map = MEM_malloc_arrayN( *r_map_len, sizeof(*vgroup_index_map), "defgroup index map create"); bool is_vgroup_remap_needed = false; int i; for (dg_src = src_defbase->first, i = 0; dg_src; dg_src = dg_src->next, i++) { vgroup_index_map[i] = BKE_object_defgroup_name_index(ob_dst, dg_src->name); is_vgroup_remap_needed = is_vgroup_remap_needed || (vgroup_index_map[i] != i); } if (!is_vgroup_remap_needed) { MEM_freeN(vgroup_index_map); vgroup_index_map = NULL; *r_map_len = 0; } return vgroup_index_map; } void BKE_object_defgroup_index_map_apply(MDeformVert *dvert, int dvert_len, const int *map, int map_len) { if (map == NULL || map_len == 0) { return; } MDeformVert *dv = dvert; for (int i = 0; i < dvert_len; i++, dv++) { int totweight = dv->totweight; for (int j = 0; j < totweight; j++) { int def_nr = dv->dw[j].def_nr; if ((uint)def_nr < (uint)map_len && map[def_nr] != -1) { dv->dw[j].def_nr = map[def_nr]; } else { totweight--; dv->dw[j] = dv->dw[totweight]; j--; } } if (totweight != dv->totweight) { if (totweight) { dv->dw = MEM_reallocN(dv->dw, sizeof(*dv->dw) * totweight); } else { MEM_SAFE_FREE(dv->dw); } dv->totweight = totweight; } } } /** * Get MDeformVert vgroup data from given object. Should only be used in Object mode. * * \return True if the id type supports weights. */ bool BKE_object_defgroup_array_get(ID *id, MDeformVert **dvert_arr, int *dvert_tot) { if (id) { switch (GS(id->name)) { case ID_ME: { Mesh *me = (Mesh *)id; *dvert_arr = me->dvert; *dvert_tot = me->totvert; return true; } case ID_LT: { Lattice *lt = object_defgroup_lattice_get(id); *dvert_arr = lt->dvert; *dvert_tot = lt->pntsu * lt->pntsv * lt->pntsw; return true; } default: break; } } *dvert_arr = NULL; *dvert_tot = 0; return false; } /** \} */ /* --- functions for getting vgroup aligned maps --- */ /** * gets the status of "flag" for each bDeformGroup * in the object data's vertex group list and returns an array containing them */ bool *BKE_object_defgroup_lock_flags_get(Object *ob, const int defbase_tot) { bool is_locked = false; int i; ListBase *defbase = BKE_object_defgroup_list_mutable(ob); bool *lock_flags = MEM_mallocN(defbase_tot * sizeof(bool), "defflags"); bDeformGroup *defgroup; for (i = 0, defgroup = defbase->first; i < defbase_tot && defgroup; defgroup = defgroup->next, i++) { lock_flags[i] = ((defgroup->flag & DG_LOCK_WEIGHT) != 0); is_locked |= lock_flags[i]; } if (is_locked) { return lock_flags; } MEM_freeN(lock_flags); return NULL; } bool *BKE_object_defgroup_validmap_get(Object *ob, const int defbase_tot) { bDeformGroup *dg; ModifierData *md; bool *defgroup_validmap; GHash *gh; int i, step1 = 1; const ListBase *defbase = BKE_object_defgroup_list(ob); VirtualModifierData virtualModifierData; if (BLI_listbase_is_empty(defbase)) { return NULL; } gh = BLI_ghash_str_new_ex(__func__, defbase_tot); /* add all names to a hash table */ for (dg = defbase->first; dg; dg = dg->next) { BLI_ghash_insert(gh, dg->name, NULL); } BLI_assert(BLI_ghash_len(gh) == defbase_tot); /* now loop through the armature modifiers and identify deform bones */ for (md = ob->modifiers.first; md; md = !md->next && step1 ? (step1 = 0), BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData) : md->next) { if (!(md->mode & (eModifierMode_Realtime | eModifierMode_Virtual))) { continue; } if (md->type == eModifierType_Armature) { ArmatureModifierData *amd = (ArmatureModifierData *)md; if (amd->object && amd->object->pose) { bPose *pose = amd->object->pose; bPoseChannel *chan; for (chan = pose->chanbase.first; chan; chan = chan->next) { void **val_p; if (chan->bone->flag & BONE_NO_DEFORM) { continue; } val_p = BLI_ghash_lookup_p(gh, chan->name); if (val_p) { *val_p = POINTER_FROM_INT(1); } } } } } defgroup_validmap = MEM_mallocN(sizeof(*defgroup_validmap) * defbase_tot, "wpaint valid map"); /* add all names to a hash table */ for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { defgroup_validmap[i] = (BLI_ghash_lookup(gh, dg->name) != NULL); } BLI_assert(i == BLI_ghash_len(gh)); BLI_ghash_free(gh, NULL, NULL); return defgroup_validmap; } /* Returns total selected vgroups, * wpi.defbase_sel is assumed malloc'd, all values are set */ bool *BKE_object_defgroup_selected_get(Object *ob, int defbase_tot, int *r_dg_flags_sel_tot) { bool *dg_selection = MEM_mallocN(defbase_tot * sizeof(bool), __func__); bDeformGroup *defgroup; unsigned int i; Object *armob = BKE_object_pose_armature_get(ob); (*r_dg_flags_sel_tot) = 0; const ListBase *defbase = BKE_object_defgroup_list(ob); if (armob) { bPose *pose = armob->pose; for (i = 0, defgroup = defbase->first; i < defbase_tot && defgroup; defgroup = defgroup->next, i++) { bPoseChannel *pchan = BKE_pose_channel_find_name(pose, defgroup->name); if (pchan && (pchan->bone->flag & BONE_SELECTED)) { dg_selection[i] = true; (*r_dg_flags_sel_tot) += 1; } else { dg_selection[i] = false; } } } else { memset(dg_selection, false, sizeof(*dg_selection) * defbase_tot); } return dg_selection; } /** * Checks if the lock relative mode is applicable. * * \return true if an unlocked deform group is active. */ bool BKE_object_defgroup_check_lock_relative(const bool *lock_flags, const bool *validmap, int index) { return validmap && validmap[index] && !(lock_flags && lock_flags[index]); } /** * Additional check for whether the lock relative mode is applicable in multi-paint mode. * * \return true if none of the selected groups are locked. */ bool BKE_object_defgroup_check_lock_relative_multi(int defbase_tot, const bool *lock_flags, const bool *selected, int sel_tot) { if (lock_flags == NULL) { return true; } if (selected == NULL || sel_tot <= 1) { return true; } for (int i = 0; i < defbase_tot; i++) { if (selected[i] && lock_flags[i]) { return false; } } return true; } /** * Takes a pair of boolean masks of all locked and all deform groups, and computes * a pair of masks for locked deform and unlocked deform groups. Output buffers may * reuse the input ones. */ void BKE_object_defgroup_split_locked_validmap( int defbase_tot, const bool *locked, const bool *deform, bool *r_locked, bool *r_unlocked) { if (!locked) { if (r_unlocked != deform) { memcpy(r_unlocked, deform, sizeof(bool) * defbase_tot); } if (r_locked) { memset(r_locked, 0, sizeof(bool) * defbase_tot); } return; } for (int i = 0; i < defbase_tot; i++) { bool is_locked = locked[i]; bool is_deform = deform[i]; r_locked[i] = is_deform && is_locked; r_unlocked[i] = is_deform && !is_locked; } } /** * Marks mirror vgroups in output and counts them. * Output and counter assumed to be already initialized. * Designed to be usable after BKE_object_defgroup_selected_get to extend selection to mirror. */ void BKE_object_defgroup_mirror_selection(struct Object *ob, int defbase_tot, const bool *dg_selection, bool *dg_flags_sel, int *r_dg_flags_sel_tot) { const ListBase *defbase = BKE_object_defgroup_list(ob); bDeformGroup *defgroup; unsigned int i; int i_mirr; for (i = 0, defgroup = defbase->first; i < defbase_tot && defgroup; defgroup = defgroup->next, i++) { if (dg_selection[i]) { char name_flip[MAXBONENAME]; BLI_string_flip_side_name(name_flip, defgroup->name, false, sizeof(name_flip)); i_mirr = STREQ(name_flip, defgroup->name) ? i : BKE_object_defgroup_name_index(ob, name_flip); if ((i_mirr >= 0 && i_mirr < defbase_tot) && (dg_flags_sel[i_mirr] == false)) { dg_flags_sel[i_mirr] = true; (*r_dg_flags_sel_tot) += 1; } } } } /** * Return the subset type of the Vertex Group Selection */ bool *BKE_object_defgroup_subset_from_select_type(Object *ob, eVGroupSelect subset_type, int *r_defgroup_tot, int *r_subset_count) { bool *defgroup_validmap = NULL; *r_defgroup_tot = BKE_object_defgroup_count(ob); switch (subset_type) { case WT_VGROUP_ACTIVE: { const int def_nr_active = BKE_object_defgroup_active_index_get(ob) - 1; defgroup_validmap = MEM_mallocN(*r_defgroup_tot * sizeof(*defgroup_validmap), __func__); memset(defgroup_validmap, false, *r_defgroup_tot * sizeof(*defgroup_validmap)); if ((def_nr_active >= 0) && (def_nr_active < *r_defgroup_tot)) { *r_subset_count = 1; defgroup_validmap[def_nr_active] = true; } else { *r_subset_count = 0; } break; } case WT_VGROUP_BONE_SELECT: { defgroup_validmap = BKE_object_defgroup_selected_get(ob, *r_defgroup_tot, r_subset_count); break; } case WT_VGROUP_BONE_DEFORM: { int i; defgroup_validmap = BKE_object_defgroup_validmap_get(ob, *r_defgroup_tot); *r_subset_count = 0; for (i = 0; i < *r_defgroup_tot; i++) { if (defgroup_validmap[i] == true) { *r_subset_count += 1; } } break; } case WT_VGROUP_BONE_DEFORM_OFF: { int i; defgroup_validmap = BKE_object_defgroup_validmap_get(ob, *r_defgroup_tot); *r_subset_count = 0; for (i = 0; i < *r_defgroup_tot; i++) { defgroup_validmap[i] = !defgroup_validmap[i]; if (defgroup_validmap[i] == true) { *r_subset_count += 1; } } break; } case WT_VGROUP_ALL: default: { defgroup_validmap = MEM_mallocN(*r_defgroup_tot * sizeof(*defgroup_validmap), __func__); memset(defgroup_validmap, true, *r_defgroup_tot * sizeof(*defgroup_validmap)); *r_subset_count = *r_defgroup_tot; break; } } return defgroup_validmap; } /** * store indices from the defgroup_validmap (faster lookups in some cases) */ void BKE_object_defgroup_subset_to_index_array(const bool *defgroup_validmap, const int defgroup_tot, int *r_defgroup_subset_map) { int i, j = 0; for (i = 0; i < defgroup_tot; i++) { if (defgroup_validmap[i]) { r_defgroup_subset_map[j++] = i; } } }