Shape Keys: Options to mirror shape keys in update, join operators
As described in #135095, this adds a "mirror" option to the "Join as Shapes" and "Update from Objects" operators, and additional menu items with the option enabled. Like the operators, this is a convenience feature that's functionally the same as selecting all shape keys with changed data and running the existing "Flip" operator. Pull Request: https://projects.blender.org/blender/blender/pulls/144098
This commit is contained in:
@@ -64,7 +64,9 @@ class MESH_MT_shape_key_context_menu(Menu):
|
||||
layout.operator("object.shape_key_transfer", text="Copy from Objects")
|
||||
layout.separator()
|
||||
layout.operator("object.join_shapes", text="New from Objects")
|
||||
layout.operator("object.join_shapes", text="New from Objects Flipped").use_mirror = True
|
||||
layout.operator("object.update_shapes", icon='FILE_REFRESH')
|
||||
layout.operator("object.update_shapes", text="Update from Objects Flipped").use_mirror = True
|
||||
layout.separator()
|
||||
layout.operator("object.shape_key_mirror", icon='ARROW_LEFTRIGHT', text="Flip").use_topology = False
|
||||
layout.operator("object.shape_key_mirror", text="Flip (Topology)").use_topology = True
|
||||
|
||||
@@ -582,6 +582,7 @@ wmOperatorStatus join_objects_exec(bContext *C, wmOperator *op);
|
||||
|
||||
wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C,
|
||||
bool ensure_keys_exist,
|
||||
bool mirror,
|
||||
ReportList *reports);
|
||||
|
||||
/* Mirror lookup API. */
|
||||
|
||||
@@ -110,6 +110,8 @@ bool shape_key_report_if_any_locked(Object *ob, ReportList *reports);
|
||||
*/
|
||||
bool shape_key_is_selected(const Object &object, const KeyBlock &kb, int keyblock_index);
|
||||
|
||||
void shape_key_mirror(Object *ob, KeyBlock *kb, bool use_topology, int &totmirr, int &totfail);
|
||||
|
||||
/* `object_utils.cc` */
|
||||
|
||||
bool calc_active_center_for_editmode(Object *obedit, bool select_only, float r_center[3]);
|
||||
|
||||
@@ -70,8 +70,24 @@ using blender::Span;
|
||||
* Add vertex positions of selected meshes as shape keys to the active mesh.
|
||||
* \{ */
|
||||
|
||||
static std::string create_mirrored_name(const blender::StringRefNull object_name,
|
||||
const bool mirror)
|
||||
{
|
||||
if (!mirror) {
|
||||
return object_name;
|
||||
}
|
||||
if (object_name.endswith(".L")) {
|
||||
return blender::StringRef(object_name).drop_suffix(2) + ".R";
|
||||
}
|
||||
if (object_name.endswith(".R")) {
|
||||
return blender::StringRef(object_name).drop_suffix(2) + ".L";
|
||||
}
|
||||
return object_name;
|
||||
}
|
||||
|
||||
wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C,
|
||||
const bool ensure_keys_exist,
|
||||
const bool mirror,
|
||||
ReportList *reports)
|
||||
{
|
||||
using namespace blender;
|
||||
@@ -144,17 +160,35 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C,
|
||||
&active_mesh, active_mesh.key, BKE_keyblock_add(active_mesh.key, nullptr));
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
for (const ObjectInfo &info : compatible_objects) {
|
||||
if (!info.name.endswith(".L") && !info.name.endswith(".R")) {
|
||||
BKE_report(reports, RPT_ERROR, "Selected objects' names must use .L or .R suffix");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mirror_count = 0;
|
||||
int mirror_fail_count = 0;
|
||||
int keys_changed = 0;
|
||||
bool any_keys_added = false;
|
||||
for (const ObjectInfo &info : compatible_objects) {
|
||||
const std::string name = create_mirrored_name(info.name, mirror);
|
||||
if (ensure_keys_exist) {
|
||||
KeyBlock *kb = BKE_keyblock_add(active_mesh.key, info.name.c_str());
|
||||
KeyBlock *kb = BKE_keyblock_add(active_mesh.key, name.c_str());
|
||||
BKE_keyblock_convert_from_mesh(&info.mesh, active_mesh.key, kb);
|
||||
any_keys_added = true;
|
||||
if (mirror) {
|
||||
ed::object::shape_key_mirror(&active_object, kb, false, mirror_count, mirror_fail_count);
|
||||
}
|
||||
}
|
||||
else if (KeyBlock *kb = BKE_keyblock_find_name(active_mesh.key, info.name.c_str())) {
|
||||
else if (KeyBlock *kb = BKE_keyblock_find_name(active_mesh.key, name.c_str())) {
|
||||
keys_changed++;
|
||||
BKE_keyblock_update_from_mesh(&info.mesh, kb);
|
||||
if (mirror) {
|
||||
ed::object::shape_key_mirror(&active_object, kb, false, mirror_count, mirror_fail_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +200,10 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C,
|
||||
BKE_reportf(reports, RPT_INFO, "Updated %d shape key(s)", keys_changed);
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
ED_mesh_report_mirror_ex(*reports, mirror_count, mirror_fail_count, SCE_SELECT_VERTEX);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&active_mesh.id, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, &active_mesh.id);
|
||||
|
||||
|
||||
@@ -5189,7 +5189,8 @@ static bool active_shape_key_editable_poll(bContext *C)
|
||||
|
||||
static wmOperatorStatus join_shapes_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return ED_mesh_shapes_join_objects_exec(C, true, op->reports);
|
||||
return ED_mesh_shapes_join_objects_exec(
|
||||
C, true, RNA_boolean_get(op->ptr, "use_mirror"), op->reports);
|
||||
}
|
||||
|
||||
void OBJECT_OT_join_shapes(wmOperatorType *ot)
|
||||
@@ -5204,11 +5205,16 @@ void OBJECT_OT_join_shapes(wmOperatorType *ot)
|
||||
ot->poll = active_shape_key_editable_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop = RNA_def_boolean(
|
||||
ot->srna, "use_mirror", false, "Mirror", "Mirror the new shape key values");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
static wmOperatorStatus update_all_shape_keys_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return ED_mesh_shapes_join_objects_exec(C, false, op->reports);
|
||||
return ED_mesh_shapes_join_objects_exec(
|
||||
C, false, RNA_boolean_get(op->ptr, "use_mirror"), op->reports);
|
||||
}
|
||||
|
||||
static bool object_update_shapes_poll(bContext *C)
|
||||
@@ -5237,6 +5243,10 @@ void OBJECT_OT_update_shapes(wmOperatorType *ot)
|
||||
ot->poll = object_update_shapes_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop = RNA_def_boolean(
|
||||
ot->srna, "use_mirror", false, "Mirror", "Mirror the new shape key values");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -160,10 +160,101 @@ static void object_shape_key_add(bContext *C, Object *ob, const bool from_mix)
|
||||
/** \name Remove Shape Key Function
|
||||
* \{ */
|
||||
|
||||
void shape_key_mirror(
|
||||
Object *ob, KeyBlock *kb, const bool use_topology, int &totmirr, int &totfail)
|
||||
{
|
||||
char *tag_elem = MEM_calloc_arrayN<char>(kb->totelem, "shape_key_mirror");
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
int i1, i2;
|
||||
float *fp1, *fp2;
|
||||
float tvec[3];
|
||||
|
||||
ED_mesh_mirror_spatial_table_begin(ob, nullptr, nullptr);
|
||||
|
||||
for (i1 = 0; i1 < mesh->verts_num; i1++) {
|
||||
i2 = mesh_get_x_mirror_vert(ob, nullptr, i1, use_topology);
|
||||
if (i2 == i1) {
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp1[0] = -fp1[0];
|
||||
tag_elem[i1] = 1;
|
||||
totmirr++;
|
||||
}
|
||||
else if (i2 != -1) {
|
||||
if (tag_elem[i1] == 0 && tag_elem[i2] == 0) {
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp2 = ((float *)kb->data) + i2 * 3;
|
||||
|
||||
copy_v3_v3(tvec, fp1);
|
||||
copy_v3_v3(fp1, fp2);
|
||||
copy_v3_v3(fp2, tvec);
|
||||
|
||||
/* flip x axis */
|
||||
fp1[0] = -fp1[0];
|
||||
fp2[0] = -fp2[0];
|
||||
totmirr++;
|
||||
}
|
||||
tag_elem[i1] = tag_elem[i2] = 1;
|
||||
}
|
||||
else {
|
||||
totfail++;
|
||||
}
|
||||
}
|
||||
|
||||
ED_mesh_mirror_spatial_table_end(ob);
|
||||
}
|
||||
else if (ob->type == OB_LATTICE) {
|
||||
const Lattice *lt = static_cast<const Lattice *>(ob->data);
|
||||
int i1, i2;
|
||||
float *fp1, *fp2;
|
||||
int u, v, w;
|
||||
/* half but found up odd value */
|
||||
const int pntsu_half = (lt->pntsu / 2) + (lt->pntsu % 2);
|
||||
|
||||
/* Currently edit-mode isn't supported by mesh so ignore here for now too. */
|
||||
#if 0
|
||||
if (lt->editlatt) {
|
||||
lt = lt->editlatt->latt;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (w = 0; w < lt->pntsw; w++) {
|
||||
for (v = 0; v < lt->pntsv; v++) {
|
||||
for (u = 0; u < pntsu_half; u++) {
|
||||
int u_inv = (lt->pntsu - 1) - u;
|
||||
float tvec[3];
|
||||
if (u == u_inv) {
|
||||
i1 = BKE_lattice_index_from_uvw(lt, u, v, w);
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp1[0] = -fp1[0];
|
||||
totmirr++;
|
||||
}
|
||||
else {
|
||||
i1 = BKE_lattice_index_from_uvw(lt, u, v, w);
|
||||
i2 = BKE_lattice_index_from_uvw(lt, u_inv, v, w);
|
||||
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp2 = ((float *)kb->data) + i2 * 3;
|
||||
|
||||
copy_v3_v3(tvec, fp1);
|
||||
copy_v3_v3(fp1, fp2);
|
||||
copy_v3_v3(fp2, tvec);
|
||||
fp1[0] = -fp1[0];
|
||||
fp2[0] = -fp2[0];
|
||||
totmirr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(tag_elem);
|
||||
}
|
||||
|
||||
static bool object_shape_key_mirror(
|
||||
bContext *C, Object *ob, int *r_totmirr, int *r_totfail, bool use_topology)
|
||||
{
|
||||
KeyBlock *kb;
|
||||
Key *key;
|
||||
int totmirr = 0, totfail = 0;
|
||||
|
||||
@@ -174,96 +265,8 @@ static bool object_shape_key_mirror(
|
||||
return false;
|
||||
}
|
||||
|
||||
kb = static_cast<KeyBlock *>(BLI_findlink(&key->block, ob->shapenr - 1));
|
||||
|
||||
if (kb) {
|
||||
char *tag_elem = MEM_calloc_arrayN<char>(kb->totelem, "shape_key_mirror");
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
int i1, i2;
|
||||
float *fp1, *fp2;
|
||||
float tvec[3];
|
||||
|
||||
ED_mesh_mirror_spatial_table_begin(ob, nullptr, nullptr);
|
||||
|
||||
for (i1 = 0; i1 < mesh->verts_num; i1++) {
|
||||
i2 = mesh_get_x_mirror_vert(ob, nullptr, i1, use_topology);
|
||||
if (i2 == i1) {
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp1[0] = -fp1[0];
|
||||
tag_elem[i1] = 1;
|
||||
totmirr++;
|
||||
}
|
||||
else if (i2 != -1) {
|
||||
if (tag_elem[i1] == 0 && tag_elem[i2] == 0) {
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp2 = ((float *)kb->data) + i2 * 3;
|
||||
|
||||
copy_v3_v3(tvec, fp1);
|
||||
copy_v3_v3(fp1, fp2);
|
||||
copy_v3_v3(fp2, tvec);
|
||||
|
||||
/* flip x axis */
|
||||
fp1[0] = -fp1[0];
|
||||
fp2[0] = -fp2[0];
|
||||
totmirr++;
|
||||
}
|
||||
tag_elem[i1] = tag_elem[i2] = 1;
|
||||
}
|
||||
else {
|
||||
totfail++;
|
||||
}
|
||||
}
|
||||
|
||||
ED_mesh_mirror_spatial_table_end(ob);
|
||||
}
|
||||
else if (ob->type == OB_LATTICE) {
|
||||
const Lattice *lt = static_cast<const Lattice *>(ob->data);
|
||||
int i1, i2;
|
||||
float *fp1, *fp2;
|
||||
int u, v, w;
|
||||
/* half but found up odd value */
|
||||
const int pntsu_half = (lt->pntsu / 2) + (lt->pntsu % 2);
|
||||
|
||||
/* Currently edit-mode isn't supported by mesh so ignore here for now too. */
|
||||
#if 0
|
||||
if (lt->editlatt) {
|
||||
lt = lt->editlatt->latt;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (w = 0; w < lt->pntsw; w++) {
|
||||
for (v = 0; v < lt->pntsv; v++) {
|
||||
for (u = 0; u < pntsu_half; u++) {
|
||||
int u_inv = (lt->pntsu - 1) - u;
|
||||
float tvec[3];
|
||||
if (u == u_inv) {
|
||||
i1 = BKE_lattice_index_from_uvw(lt, u, v, w);
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp1[0] = -fp1[0];
|
||||
totmirr++;
|
||||
}
|
||||
else {
|
||||
i1 = BKE_lattice_index_from_uvw(lt, u, v, w);
|
||||
i2 = BKE_lattice_index_from_uvw(lt, u_inv, v, w);
|
||||
|
||||
fp1 = ((float *)kb->data) + i1 * 3;
|
||||
fp2 = ((float *)kb->data) + i2 * 3;
|
||||
|
||||
copy_v3_v3(tvec, fp1);
|
||||
copy_v3_v3(fp1, fp2);
|
||||
copy_v3_v3(fp2, tvec);
|
||||
fp1[0] = -fp1[0];
|
||||
fp2[0] = -fp2[0];
|
||||
totmirr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(tag_elem);
|
||||
if (KeyBlock *kb = static_cast<KeyBlock *>(BLI_findlink(&key->block, ob->shapenr - 1))) {
|
||||
shape_key_mirror(ob, kb, use_topology, totmirr, totfail);
|
||||
}
|
||||
|
||||
*r_totmirr = totmirr;
|
||||
|
||||
Reference in New Issue
Block a user