GPv3: Parent armature with empty weights

Implements setting the armature parent and generating
vertex groups for the bones in the armature.

Note: This does not implement the `Envelope` or `Automatic Weights` options.

Pull Request: https://projects.blender.org/blender/blender/pulls/127515
This commit is contained in:
Falk David
2024-09-12 20:25:27 +02:00
committed by Falk David
parent 1e22f364f8
commit f68910092b
4 changed files with 107 additions and 29 deletions

View File

@@ -647,24 +647,34 @@ ModifierData *BKE_modifiers_get_virtual_modifierlist(const Object *ob,
Object *BKE_modifiers_is_deformed_by_armature(Object *ob)
{
VirtualModifierData virtual_modifier_data;
ArmatureModifierData *amd = nullptr;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data);
Object *armature = nullptr;
/* return the first selected armature, this lets us use multiple armatures */
for (; md; md = md->next) {
if (md->type == eModifierType_Armature) {
amd = (ArmatureModifierData *)md;
if (amd->object && (amd->object->base_flag & BASE_SELECTED)) {
return amd->object;
if (ob->type == OB_GREASE_PENCIL) {
for (; md; md = md->next) {
if (md->type == eModifierType_GreasePencilArmature) {
auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
armature = amd->object;
if (armature && (armature->base_flag & BASE_SELECTED)) {
return armature;
}
}
}
}
else {
for (; md; md = md->next) {
if (md->type == eModifierType_Armature) {
auto *amd = reinterpret_cast<ArmatureModifierData *>(md);
armature = amd->object;
if (armature && (armature->base_flag & BASE_SELECTED)) {
return armature;
}
}
}
}
/* If we're still here then return the last armature. */
if (amd) {
return amd->object;
}
return nullptr;
return armature;
}
Object *BKE_modifiers_is_deformed_by_meshdeform(Object *ob)

View File

@@ -12,6 +12,7 @@
#include "BKE_deform.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_modifier.hh"
#include "BKE_object_deform.h"
#include "BKE_paint.hh"
#include "BKE_report.hh"
@@ -200,6 +201,46 @@ void normalize_vertex_weights(MDeformVert &dvert,
active_vertex_group_is_unlocked);
}
static int armature_traversal(Object &ob,
const Bone *bone,
const FunctionRef<bool(Object &, const Bone *)> bone_callback)
{
int count = 0;
if (bone != nullptr) {
/* Only call `bone_callback` if the bone is non null */
count += bone_callback(ob, bone) ? 1 : 0;
/* Try to execute `bone_callback` for the first child. */
count += armature_traversal(ob, static_cast<Bone *>(bone->childbase.first), bone_callback);
/* Try to execute `bone_callback` for the next bone at this depth of the recursion. */
count += armature_traversal(ob, bone->next, bone_callback);
}
return count;
}
bool add_armature_vertex_groups(Object &object, const Object &ob_armature)
{
const bArmature &armature = *static_cast<const bArmature *>(ob_armature.data);
const int defbase_add = armature_traversal(
object,
static_cast<const Bone *>(armature.bonebase.first),
[&](Object &object, const Bone *bone) {
if ((bone->flag & BONE_NO_DEFORM) == 0) {
/* Check if the name of the bone matches a vertex group name. */
if (!BKE_object_defgroup_find_name(&object, bone->name)) {
/* Add a new vertex group with the name of the bone. */
BKE_object_defgroup_add_name(&object, bone->name);
return true;
}
}
return false;
});
return defbase_add > 0;
}
struct ClosestGreasePencilDrawing {
const bke::greasepencil::Drawing *drawing = nullptr;
int active_defgroup_index;

View File

@@ -497,6 +497,9 @@ void normalize_vertex_weights(MDeformVert &dvert,
Span<bool> vertex_group_is_locked,
Span<bool> vertex_group_is_bone_deformed);
/** Adds vertex groups for the bones in the armature (with matiching names). */
bool add_armature_vertex_groups(Object &object, const Object &armature);
void clipboard_free();
const bke::CurvesGeometry &clipboard_curves();
/**

View File

@@ -83,6 +83,7 @@
#include "ED_armature.hh"
#include "ED_curve.hh"
#include "ED_gpencil_legacy.hh"
#include "ED_grease_pencil.hh"
#include "ED_mesh.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
@@ -620,7 +621,9 @@ bool parent_set(ReportList *reports,
*/
/* XXX currently this should only happen for meshes, curves, surfaces, * and lattices
* - this stuff isn't available for meta-balls yet. */
if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ELEM(
ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE, OB_GREASE_PENCIL))
{
ModifierData *md;
switch (partype) {
@@ -647,9 +650,18 @@ bool parent_set(ReportList *reports,
break;
default: /* armature deform */
if (BKE_modifiers_is_deformed_by_armature(ob) != par) {
md = modifier_add(reports, bmain, scene, ob, nullptr, eModifierType_Armature);
if (md) {
((ArmatureModifierData *)md)->object = par;
if (ob->type == OB_GREASE_PENCIL) {
md = modifier_add(
reports, bmain, scene, ob, nullptr, eModifierType_GreasePencilArmature);
if (md) {
((GreasePencilArmatureModifierData *)md)->object = par;
}
}
else {
md = modifier_add(reports, bmain, scene, ob, nullptr, eModifierType_Armature);
if (md) {
((ArmatureModifierData *)md)->object = par;
}
}
}
break;
@@ -718,7 +730,7 @@ bool parent_set(ReportList *reports,
reports, depsgraph, scene, ob, par, ARM_GROUPS_AUTO, xmirror);
WM_cursor_wait(false);
}
/* get corrected inverse */
/* Get corrected inverse. */
ob->partype = PAROBJECT;
invert_m4_m4(ob->parentinv, BKE_object_calc_parent(depsgraph, scene, ob).ptr());
@@ -740,6 +752,18 @@ bool parent_set(ReportList *reports,
invert_m4_m4(ob->parentinv, BKE_object_calc_parent(depsgraph, scene, ob).ptr());
}
else if (is_armature_parent && (ob->type == OB_GREASE_PENCIL) && (par->type == OB_ARMATURE)) {
if (partype == PAR_ARMATURE_NAME) {
ed::greasepencil::add_armature_vertex_groups(*ob, *par);
}
else if (ELEM(partype, PAR_ARMATURE_AUTO, PAR_ARMATURE_ENVELOPE)) {
/* TODO. */
}
/* get corrected inverse */
ob->partype = PAROBJECT;
invert_m4_m4(ob->parentinv, BKE_object_calc_parent(depsgraph, scene, ob).ptr());
}
else {
/* calculate inverse parent matrix */
invert_m4_m4(ob->parentinv, BKE_object_calc_parent(depsgraph, scene, ob).ptr());
@@ -948,7 +972,7 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
if (child->type == OB_MESH) {
has_children_of_type.mesh = true;
}
if (child->type == OB_GPENCIL_LEGACY) {
if (child->type == OB_GPENCIL_LEGACY || child->type == OB_GREASE_PENCIL) {
has_children_of_type.gpencil = true;
}
if (child->type == OB_CURVES) {
@@ -1725,8 +1749,8 @@ static Collection *single_object_users_collection(Main *bmain,
const bool copy_collections,
const bool is_master_collection)
{
/* Generate new copies for objects in given collection and all its children, * and optionally
* also copy collections themselves. */
/* Generate new copies for objects in given collection and all its children, * and
* optionally also copy collections themselves. */
if (copy_collections && !is_master_collection) {
Collection *collection_new = (Collection *)BKE_id_copy_ex(
bmain, &collection->id, nullptr, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS);
@@ -2365,8 +2389,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
id_root = &collection->id;
user_overrides_from_selected_objects = true;
}
/* Else, poll func ensures us that ID_IS_LINKED(obact) is true, or that it is already an existing
* liboverride. */
/* Else, poll func ensures us that ID_IS_LINKED(obact) is true, or that it is already an
* existing liboverride. */
else {
BLI_assert(ID_IS_LINKED(obact) || ID_IS_OVERRIDE_LIBRARY_REAL(obact));
id_root = &obact->id;
@@ -2423,8 +2447,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
/* For the time being, replace selected linked objects by their overrides in all collections.
* While this may not be the absolute best behavior in all cases, in most common one this should
* match the expected result. */
* While this may not be the absolute best behavior in all cases, in most common one this
* should match the expected result. */
if (user_overrides_objects_uids != nullptr) {
LISTBASE_FOREACH (Collection *, coll_iter, &bmain->collections) {
if (ID_IS_LINKED(coll_iter)) {
@@ -2498,8 +2522,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
break;
}
case ID_OB: {
/* TODO: Not sure how well we can handle this case, when we don't have the collections as
* reference containers... */
/* TODO: Not sure how well we can handle this case, when we don't have the collections
* as reference containers... */
break;
}
default:
@@ -2578,13 +2602,13 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve
potential_root_collections.remove(collection_root_iter);
}
else {
/* Current potential root is not found in current collection's hierarchy, so the later is
* a potential candidate as root collection. */
/* Current potential root is not found in current collection's hierarchy, so the later
* is a potential candidate as root collection. */
is_potential_root = true;
}
}
/* Only add the current collection as potential root if it is not a descendant of any already
* known potential root collections. */
/* Only add the current collection as potential root if it is not a descendant of any
* already known potential root collections. */
if (is_potential_root && !has_parents_in_potential_roots) {
potential_root_collections.add_new(collection);
}